Skip to content

Commit

Permalink
Merge pull request #320 from mousheng/mention_component
Browse files Browse the repository at this point in the history
Add mention component
  • Loading branch information
aaron1604 authored Aug 7, 2023
2 parents bbe7184 + 1a56b61 commit 417c528
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 3 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion client/packages/lowcoder-design/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,5 @@ 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";
export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg";
export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg";
export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg";
268 changes: 268 additions & 0 deletions client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx
Original file line number Diff line number Diff line change
@@ -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<TextAreaRef>,
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<PrefixType>("@");
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: (
<Wrapper $style={props.style}>
<ConfigProvider
theme={{
token: {
colorBgContainer: props.style.background,
colorBorder: props.style.border,
borderRadius: parseInt(props.style.radius),
colorText: props.style.text,
colorPrimary: props.style.accent,
},
}}
>
<Mentions
prefix={Object.keys(mentionList)}
onFocus={() => {
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", padding: props.style.padding }}
readOnly={props.readOnly}
/>
</ConfigProvider>
</Wrapper>
),
style: props.style,
...validateState,
});
})
.setPropertyViewFn((children) => (
<>
<Section name={sectionNames.basic}>
{children.mentionList.propertyView({
label: trans("mention.mentionList"),
})}
{children.value.propertyView({ label: trans("prop.defaultValue") })}
{children.placeholder.propertyView({
label: trans("prop.placeholder"),
})}
</Section>
<FormDataPropertyView {...children} />
{children.label.getPropertyView()}

<Section name={sectionNames.interaction}>
{children.onEvent.getPropertyView()}
{disabledPropertyView(children)}
</Section>

<Section name={sectionNames.advanced}>
{readOnlyPropertyView(children)}
</Section>

<Section name={sectionNames.validation}>
{requiredPropertyView(children)}
{children.validationType.propertyView({
label: trans("prop.textType"),
})}
{minLengthPropertyView(children)}
{maxLengthPropertyView(children)}
{children.customRule.propertyView({})}
</Section>

<Section name={sectionNames.layout}>
{children.autoHeight.getPropertyView()}
{hiddenPropertyView(children)}
</Section>

<Section name={sectionNames.style}>
{children.style.getPropertyView()}
</Section>
</>
))
.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")),
]);
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BoolControl } from "comps/controls/boolControl";
import { check } from "util/convertUtils";
import {
BoolCodeControl,
CustomRuleControl,
Expand Down Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
13 changes: 12 additions & 1 deletion client/packages/lowcoder/src/comps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ import {
VideoCompIcon,
TimeLineIcon,
LottieIcon,
MentionIcon,
AutoCompleteCompIcon,
} from "lowcoder-design";

Expand Down Expand Up @@ -121,7 +122,8 @@ import { RemoteCompInfo } from "types/remoteComp";
import { ScannerComp } from "./comps/buttonComp/scannerComp";
import { SignatureComp } from "./comps/signatureComp";
import { TimeLineComp } from "./comps/timelineComp/timelineComp";
import { AutoCompleteComp } from "./comps/autoCompleteComp/autoCompleteComp";
import { MentionComp } from "./comps/textInputComp/mentionComp";
import { AutoCompleteComp } from "./comps/autoCompleteComp/autoCompleteComp"
//Added by Aqib Mirza
import { JsonLottieComp } from "./comps/jsonComp/jsonLottieComp";

Expand Down Expand Up @@ -857,6 +859,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,
},
autocomplete: {
name: trans("uiComp.autoCompleteCompName"),
enName: "autoComplete",
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/comps/uiCompRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export type UICompType =
| "signature"
| "jsonLottie" //Added By Aqib Mirza
| "timeline"
| "mention"
| "autocomplete"

export const uiCompRegistry = {} as Record<UICompType | string, UICompManifest>;
Expand Down
8 changes: 8 additions & 0 deletions client/packages/lowcoder/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -845,6 +847,9 @@ export const en = {
timelineCompName: "Time Line",
timelineCompDesc: "Time Line",
timelineCompKeywords: "",
mentionCompName: "mention",
mentionCompDesc: "mention",
mentionCompKeywords: "",
autoCompleteCompName: "autoComplete",
autoCompleteCompDesc: "autoComplete",
autoCompleteCompKeywords: "",
Expand Down Expand Up @@ -2471,6 +2476,9 @@ export const en = {
clickedObjectDesc: "clicked item data",
clickedIndexDesc: "clicked item index",
},
mention:{
mentionList: "mention list",
},
autoComplete: {
value: "auto complete value",
checkedValueFrom: "checked value from",
Expand Down
Loading

0 comments on commit 417c528

Please sign in to comment.