Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mention component #320

Merged
merged 3 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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";
export { ReactComponent as LottieIcon } from "icons/icon-lottie.svg";
export { ReactComponent as MentionIcon } from "icons/icon-mention-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
11 changes: 11 additions & 0 deletions client/packages/lowcoder/src/comps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import {
VideoCompIcon,
TimeLineIcon,
LottieIcon,
MentionIcon,
} from "lowcoder-design";

import { defaultFormData, FormComp } from "./comps/formComp/formComp";
Expand All @@ -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";
Expand Down Expand Up @@ -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() {
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"

export const uiCompRegistry = {} as Record<UICompType | string, UICompManifest>;

Expand Down
10 changes: 9 additions & 1 deletion 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: "",
},
comp: {
menuViewDocs: "View documentation",
Expand Down Expand Up @@ -2467,5 +2472,8 @@ export const en = {
valueDesc: "data of timeline",
clickedObjectDesc: "clicked item data",
clickedIndexDesc: "clicked item index",
}
},
mention:{
mentionList: "mention list",
},
};
Loading
Loading