From 3d672ffa9e8b2d7de595420cf7012f15909474fd Mon Sep 17 00:00:00 2001 From: mou <10402885@qq.com> Date: Tue, 1 Aug 2023 16:22:17 +0800 Subject: [PATCH 1/2] Add mention component --- .../src/icons/icon-mention-comp.svg | 1 + .../lowcoder-design/src/icons/index.ts | 3 +- .../comps/comps/textInputComp/mentionComp.tsx | 268 ++++++++++++++++++ .../textInputComp/textInputConstants.tsx | 12 + .../comps/controls/eventHandlerControl.tsx | 5 + client/packages/lowcoder/src/comps/index.tsx | 11 + .../lowcoder/src/comps/uiCompRegistry.ts | 1 + .../packages/lowcoder/src/i18n/locales/en.ts | 10 +- .../packages/lowcoder/src/i18n/locales/zh.ts | 10 +- .../src/pages/editor/editorConstants.tsx | 2 + 10 files changed, 320 insertions(+), 3 deletions(-) create mode 100644 client/packages/lowcoder-design/src/icons/icon-mention-comp.svg create mode 100644 client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx diff --git a/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg b/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg new file mode 100644 index 000000000..4c04c61e2 --- /dev/null +++ b/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts index dd55eb897..b51898abc 100644 --- a/client/packages/lowcoder-design/src/icons/index.ts +++ b/client/packages/lowcoder-design/src/icons/index.ts @@ -288,4 +288,5 @@ export { ReactComponent as ExpandIcon } from "icons/icon-expand.svg"; export { ReactComponent as CompressIcon } from "icons/icon-compress.svg"; export { ReactComponent as TableCellsIcon } from "icons/icon-table-cells.svg"; // Added By Aqib Mirza export { ReactComponent as TimeLineIcon } from "icons/icon-timeline-comp.svg" -export { ReactComponent as LottieIcon } from "icons/icon-lottie.svg"; \ No newline at end of file +export { ReactComponent as LottieIcon } from "icons/icon-lottie.svg"; +export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg"; \ No newline at end of file diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx new file mode 100644 index 000000000..f4fe60cf5 --- /dev/null +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx @@ -0,0 +1,268 @@ +import { useState, useEffect } from "react"; +import { + NameConfig, + NameConfigPlaceHolder, + NameConfigRequired, + withExposingConfigs, +} from "comps/generators/withExposing"; +import { Section, sectionNames } from "lowcoder-design"; +import { BoolControl } from "../../controls/boolControl"; +import { AutoHeightControl } from "../../controls/autoHeightControl"; +import { UICompBuilder } from "../../generators"; +import { FormDataPropertyView } from "../formComp/formDataConstants"; +import { + checkMentionListData, + textInputChildren, +} from "./textInputConstants"; +import { + withMethodExposing, + refMethods, +} from "../../generators/withMethodExposing"; +import { styleControl } from "comps/controls/styleControl"; +import styled from "styled-components"; +import { + InputLikeStyle, + InputLikeStyleType, +} from "comps/controls/styleControlConstants"; +import { + disabledPropertyView, + hiddenPropertyView, + maxLengthPropertyView, + minLengthPropertyView, + readOnlyPropertyView, + requiredPropertyView, +} from "comps/utils/propertyUtils"; +import { booleanExposingStateControl } from "comps/controls/codeStateControl"; +import { trans } from "i18n"; +import { RefControl } from "comps/controls/refControl"; +import { TextAreaRef } from "antd/lib/input/TextArea"; +import { Mentions, ConfigProvider } from "antd"; +import { blurMethod, focusWithOptions } from "comps/utils/methodUtils"; +import type { MentionsOptionProps } from "antd/es/mentions"; +import { + textInputValidate, +} from "../textInputComp/textInputConstants"; +import { jsonControl } from "@lowcoder-ee/comps/controls/codeControl"; +// 事件控制 +import { + submitEvent, + eventHandlerControl, + mentionEvent, + focusEvent, + blurEvent, + changeEvent +} from "comps/controls/eventHandlerControl"; + +const Wrapper = styled.div<{ + $style: InputLikeStyleType; +}>` + height: 100%; + + .ant-input-clear-icon { + opacity: 0.45; + color: ${(props) => props.$style.text}; + top: 10px; + + &:hover { + opacity: 0.65; + color: ${(props) => props.$style.text}; + } + } +`; + +const EventOptions = [ + focusEvent, + blurEvent, + changeEvent, + mentionEvent, + submitEvent, +] as const; + +let MentionTmpComp = (function () { + const childrenMap = { + ...textInputChildren, + viewRef: RefControl, + allowClear: BoolControl, + autoHeight: AutoHeightControl, + style: styleControl(InputLikeStyle), + mentionList: jsonControl(checkMentionListData, { + "@": ["Li Lei", "Han Meimei"], + "#": ["123", "456", "789"], + }), + onEvent: eventHandlerControl(EventOptions), + invalid: booleanExposingStateControl("invalid"), + }; + + return new UICompBuilder(childrenMap, (props) => { + const { mentionList } = props; + const [validateState, setvalidateState] = useState({}); + const [activationFlag, setActivationFlag] = useState(false); + const [prefix, setPrefix] = useState("@"); + type PrefixType = "@" | keyof typeof mentionList; + + // 获取提及搜索关键字 + const onSearch = (_: string, newPrefix: PrefixType) => { + setPrefix(newPrefix); + }; + const onChange = (value: string) => { + props.value.onChange(value); + props.onEvent("change"); + }; + + const onPressEnter = (e: any) => { + if (e.shiftKey) { + e.preventDefault(); + props.onEvent("submit"); + } + }; + + const onSelect = (option: MentionsOptionProps) => { + props.onEvent("mention"); + }; + const getValidate = (value: any): "" | "warning" | "error" | undefined => { + if ( + value.hasOwnProperty("validateStatus") && + value["validateStatus"] === "error" + ) + return "error"; + return ""; + }; + + const getTextInputValidate = () => { + return { + value: { value: props.value.value }, + required: props.required, + minLength: props?.minLength ?? 0, + maxLength: props?.maxLength ?? 0, + validationType: props.validationType, + regex: props.regex, + customRule: props.customRule, + }; + }; + + useEffect(() => { + if (activationFlag) { + const temp = textInputValidate(getTextInputValidate()); + setvalidateState(temp); + props.invalid.onChange(temp.validateStatus !== ""); + } + }, [ + props.value.value, + props.required, + props?.minLength, + props?.maxLength, + props.validationType, + props.regex, + props.customRule, + ]); + return props.label({ + required: props.required, + children: ( + + + { + setActivationFlag(true); + props.onEvent("focus"); + }} + onBlur={() => props.onEvent("blur")} + onPressEnter={onPressEnter} + onSearch={onSearch} + onChange={onChange} + onSelect={onSelect} + placeholder={props.placeholder} + value={props.value.value} + disabled={props.disabled} + status={getValidate(validateState)} + options={(mentionList[prefix] || []).map((value: string) => ({ + key: value, + value, + label: value, + }))} + autoSize={props.autoHeight} + style={{ height: "100%", maxHeight: "100%", resize: "none" }} + readOnly={props.readOnly} + /> + + + ), + style: props.style, + ...validateState, + }); + }) + .setPropertyViewFn((children) => ( + <> +
+ {children.mentionList.propertyView({ + label: trans("mention.mentionList"), + })} + {children.value.propertyView({ label: trans("prop.defaultValue") })} + {children.placeholder.propertyView({ + label: trans("prop.placeholder"), + })} +
+ + {children.label.getPropertyView()} + +
+ {children.onEvent.getPropertyView()} + {disabledPropertyView(children)} +
+ +
+ {readOnlyPropertyView(children)} +
+ +
+ {requiredPropertyView(children)} + {children.validationType.propertyView({ + label: trans("prop.textType"), + })} + {minLengthPropertyView(children)} + {maxLengthPropertyView(children)} + {children.customRule.propertyView({})} +
+ +
+ {children.autoHeight.getPropertyView()} + {hiddenPropertyView(children)} +
+ +
+ {children.style.getPropertyView()} +
+ + )) + .build(); +})(); + +MentionTmpComp = class extends MentionTmpComp { + override autoHeight(): boolean { + return this.children.autoHeight.getView(); + } +}; + +const TextareaTmp2Comp = withMethodExposing( + MentionTmpComp, + refMethods([focusWithOptions, blurMethod]) +); + +export const MentionComp = withExposingConfigs(TextareaTmp2Comp, [ + new NameConfig("value", trans("export.inputValueDesc")), + NameConfigPlaceHolder, + NameConfigRequired, + new NameConfig("invalid", trans("export.invalidDesc")), + new NameConfig("hidden", trans("export.hiddenDesc")), + new NameConfig("disabled", trans("export.disabledDesc")), +]); diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx index f6bdabe60..9b1e76fd7 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx @@ -1,4 +1,5 @@ import { BoolControl } from "comps/controls/boolControl"; +import { check } from "util/convertUtils"; import { BoolCodeControl, CustomRuleControl, @@ -268,3 +269,14 @@ export const inputRefMethods = [ (comp.children.viewRef.viewRef?.input?.setRangeText as any)?.(...params), }, ]; + +export function checkMentionListData(data: any) { + if(data === "") return {} + for(const key in data) { + check(data[key], ["array"], key,(node)=>{ + check(node, ["string"], ); + return node + }) + } + return data +} diff --git a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx index f940759f9..426357c92 100644 --- a/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/eventHandlerControl.tsx @@ -305,6 +305,11 @@ export const successEvent: EventConfigType = { value: "success", description: trans("event.successDesc"), }; +export const mentionEvent: EventConfigType = { + label: trans("event.mention"), + value: "mention", + description: trans("event.mentionDesc"), +}; export const InputEventHandlerControl = eventHandlerControl([ changeEvent, diff --git a/client/packages/lowcoder/src/comps/index.tsx b/client/packages/lowcoder/src/comps/index.tsx index e2bdd418c..746fde17a 100644 --- a/client/packages/lowcoder/src/comps/index.tsx +++ b/client/packages/lowcoder/src/comps/index.tsx @@ -93,6 +93,7 @@ import { VideoCompIcon, TimeLineIcon, LottieIcon, + MentionIcon, } from "lowcoder-design"; import { defaultFormData, FormComp } from "./comps/formComp/formComp"; @@ -119,6 +120,7 @@ import { RemoteCompInfo } from "types/remoteComp"; import { ScannerComp } from "./comps/buttonComp/scannerComp"; import { SignatureComp } from "./comps/signatureComp"; import { TimeLineComp } from "./comps/timelineComp/timelineComp"; +import { MentionComp } from "./comps/textInputComp/mentionComp"; //Added by Aqib Mirza import { JsonLottieComp } from "./comps/jsonComp/jsonLottieComp"; @@ -855,6 +857,15 @@ const uiCompMap: Registry = { h: 55, }, }, + mention: { + name: trans("uiComp.mentionCompName"), + enName: "mention", + description: trans("uiComp.mentionCompDesc"), + categories: ["dataInputText"], + icon: MentionIcon, + keywords: trans("uiComp.mentionCompKeywords"), + comp: MentionComp, + }, }; export function loadComps() { diff --git a/client/packages/lowcoder/src/comps/uiCompRegistry.ts b/client/packages/lowcoder/src/comps/uiCompRegistry.ts index bba81c125..ccdae86ac 100644 --- a/client/packages/lowcoder/src/comps/uiCompRegistry.ts +++ b/client/packages/lowcoder/src/comps/uiCompRegistry.ts @@ -112,6 +112,7 @@ export type UICompType = | "signature" | "jsonLottie" //Added By Aqib Mirza | "timeline" + | "mention" export const uiCompRegistry = {} as Record; diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 160955098..0339b7d1e 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -262,6 +262,8 @@ export const en = { parseDesc: "Triggers on parse", success: "Success", successDesc: "Triggers on success", + mention: "mention", + mentionDesc: "Triggers on mention", }, themeDetail: { primary: "Brand color", @@ -845,6 +847,9 @@ export const en = { timelineCompName: "Time Line", timelineCompDesc: "Time Line", timelineCompKeywords: "", + mentionCompName: "mention", + mentionCompDesc: "mention", + mentionCompKeywords: "", }, comp: { menuViewDocs: "View documentation", @@ -2467,5 +2472,8 @@ export const en = { valueDesc: "data of timeline", clickedObjectDesc: "clicked item data", clickedIndexDesc: "clicked item index", - } + }, + mention:{ + mentionList: "mention list", + }, }; diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index f92dff893..708f41ccf 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -258,7 +258,9 @@ event: { parse: "解析", parseDesc: "在解析时触发", success: "成功", - successDesc: "在成功时触发" + successDesc: "在成功时触发", + mention: "提及", + mentionDesc: "在提及时触发", }, themeDetail: { primary: "颜色主题", @@ -828,6 +830,9 @@ uiComp: { timelineCompName: "时间线", timelineCompDesc: "时间线组件", timelineCompKeywords: "sjx", + mentionCompName: "提及", + mentionCompDesc: "提及组件", + mentionCompKeywords: "tj", }, comp: { menuViewDocs: "查看文档", @@ -2458,5 +2463,8 @@ timeLine: { endlessLoop: "循环播放", keepLastFrame: "冻结最后一帧", }, + mention:{ + mentionList: "提及列表", + }, }; diff --git a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx index 7e23f6d99..0e5eb27ba 100644 --- a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx @@ -37,6 +37,7 @@ import { LeftVideo, LeftSignature, TimeLineIcon, + MentionIcon, } from "lowcoder-design"; export const CompStateIcon: { @@ -103,4 +104,5 @@ export const CompStateIcon: { signature: , jsonLottie: , //Added By Aqib Mirza timeline: , + mention: , }; From 13c9647f2bf7a644e7fdedf8253429e28eb1b9e3 Mon Sep 17 00:00:00 2001 From: Aqib Mirza Date: Mon, 7 Aug 2023 15:19:48 +0530 Subject: [PATCH 2/2] fix: design fixes --- .../lowcoder/src/comps/comps/textInputComp/mentionComp.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx index f4fe60cf5..9184ce39d 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx @@ -191,7 +191,7 @@ let MentionTmpComp = (function () { label: value, }))} autoSize={props.autoHeight} - style={{ height: "100%", maxHeight: "100%", resize: "none" }} + style={{ height: "100%", maxHeight: "100%", resize: "none", padding: props.style.padding }} readOnly={props.readOnly} />