From 3e5796b2561e381aa0aea00e0bb00256d4c245d5 Mon Sep 17 00:00:00 2001
From: mantou132 <709922234@qq.com>
Date: Sat, 2 Mar 2024 18:04:00 +0800
Subject: [PATCH] [duoyun-ui] Improve ``
---
packages/duoyun-ui/package.json | 2 +-
packages/duoyun-ui/src/elements/select.ts | 11 ++-
packages/duoyun-ui/src/patterns/form.ts | 98 ++++++++++++++---------
packages/gem-examples/src/console/item.ts | 9 +++
packages/gem-port/src/react.ts | 2 +-
5 files changed, 80 insertions(+), 42 deletions(-)
diff --git a/packages/duoyun-ui/package.json b/packages/duoyun-ui/package.json
index 8097d24d..9003bf53 100644
--- a/packages/duoyun-ui/package.json
+++ b/packages/duoyun-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "duoyun-ui",
- "version": "1.1.18",
+ "version": "1.1.19",
"description": "A lightweight desktop UI component library, implemented using Gem",
"keywords": [
"frontend",
diff --git a/packages/duoyun-ui/src/elements/select.ts b/packages/duoyun-ui/src/elements/select.ts
index cadd608a..e49a092f 100644
--- a/packages/duoyun-ui/src/elements/select.ts
+++ b/packages/duoyun-ui/src/elements/select.ts
@@ -278,7 +278,6 @@ export class DuoyunSelectElement extends GemElement implements BasePicker
#onSearch = (evt: CustomEvent) => {
this.setState({ search: evt.detail, open: true });
- this.search(evt.detail);
evt.stopPropagation();
};
@@ -358,9 +357,9 @@ export class DuoyunSelectElement extends GemElement implements BasePicker
);
this.effect(
() => {
- if (this.state.open && !this.searchable && !this.inline) {
- const restoreInert = setBodyInert(this.optionsRef.element!);
- this.optionsRef.element?.focus();
+ if (this.state.open && !this.searchable && !this.inline && this.optionsRef.element) {
+ const restoreInert = setBodyInert(this.optionsRef.element);
+ this.optionsRef.element.focus();
return () => {
restoreInert();
this.focus();
@@ -369,6 +368,10 @@ export class DuoyunSelectElement extends GemElement implements BasePicker
},
() => [this.state.open],
);
+ this.effect(
+ ([search]) => this.search(search),
+ () => [this.state.search],
+ );
};
#getOptions = () => {
diff --git a/packages/duoyun-ui/src/patterns/form.ts b/packages/duoyun-ui/src/patterns/form.ts
index 754da34e..5554b7a4 100644
--- a/packages/duoyun-ui/src/patterns/form.ts
+++ b/packages/duoyun-ui/src/patterns/form.ts
@@ -20,6 +20,7 @@ type FormItemProps = {
field: keyof T | string[];
options?: DuoyunFormItemElement['options'];
+ getOptions?: (input: string) => Promise;
multiple?: boolean;
placeholder?: string;
searchable?: boolean;
@@ -29,6 +30,8 @@ type FormItemProps = {
rules?: DuoyunFormItemElement['rules'];
slot?: TemplateResult | HTMLElement | HTMLElement[];
+
+ isHidden?: (data: T) => boolean;
};
export type FormItem =
@@ -65,6 +68,16 @@ const style = createCSSSheet(css`
}
`);
+type OptionsRecord = {
+ loading: boolean;
+ options?: DuoyunFormItemElement['options'];
+};
+
+type State = {
+ data: T;
+ optionsRecord: Partial>;
+};
+
/**
* @customElement dy-pat-form
*/
@@ -72,17 +85,20 @@ const style = createCSSSheet(css`
@adoptedStyle(blockContainer)
@adoptedStyle(focusStyle)
@adoptedStyle(style)
-export class DyPatFormElement> extends GemElement {
+export class DyPatFormElement> extends GemElement> {
@refobject formRef: RefObject;
@property data?: T;
@property formItems?: FormItem[];
- state: T = {} as T;
+ state: State = {
+ data: {} as T,
+ optionsRecord: {},
+ };
#onChange = ({ detail }: CustomEvent) => {
- this.setState(
- Object.keys(detail).reduce((p, c) => {
+ this.setState({
+ data: Object.keys(detail).reduce((p, c) => {
const keys = c.split(',');
if (keys.length === 1) {
p[c] = detail[c];
@@ -92,37 +108,47 @@ export class DyPatFormElement> extends GemElement
a[lastKey] = detail[c];
}
return p;
- }, this.state as any),
- );
+ }, this.state.data as any),
+ });
+ };
+
+ #onOptionsChange = async (props: FormItemProps, input: string) => {
+ if (!props.getOptions) return;
+ const options = (this.state.optionsRecord[String(props.field)] ||= {} as OptionsRecord);
+ options.loading = true;
+ this.update();
+ try {
+ options.options = await props.getOptions(input);
+ } finally {
+ options.loading = false;
+ this.update();
+ }
};
- #renderItem = ({
- label,
- field,
- type,
- clearable,
- multiple,
- placeholder,
- required,
- rules,
- options,
- searchable,
- slot,
- }: FormItemProps) => {
+ #renderItem = (props: FormItemProps) => {
+ const { optionsRecord, data } = this.state;
+ const name = String(props.field);
+ const onChange = (evt: CustomEvent) => props.type === 'text' && this.#onOptionsChange(props, evt.detail);
+ const onSearch = (evt: CustomEvent) => props.type === 'select' && this.#onOptionsChange(props, evt.detail);
return html`
${slot}${props.slot}
`;
};
@@ -141,7 +167,7 @@ export class DyPatFormElement> extends GemElement
this.memo(
() => {
if (this.data) {
- this.state = structuredClone(this.data);
+ this.state.data = structuredClone(this.data);
}
},
() => [this.data],
@@ -194,17 +220,17 @@ export function createForm>(options: CreateFormOptio
.data=${options.data}
>
`,
- prepareClose: (ele) => options.prepareClose?.(ele.state),
+ prepareClose: (ele) => options.prepareClose?.(ele.state.data),
prepareOk: async (ele) => {
const valid = await ele.valid();
if (!valid) throw null;
- await waitLoading(options.prepareOk?.(ele.state));
+ await waitLoading(options.prepareOk?.(ele.state.data));
await DuoyunWaitElement.instance?.removed;
},
})
- .then((ele) => ele.state)
+ .then((ele) => ele.state.data)
.catch((ele) => {
- throw ele.state;
+ throw ele.state.data;
})
.finally(() => {
if (options.query) {
diff --git a/packages/gem-examples/src/console/item.ts b/packages/gem-examples/src/console/item.ts
index 3b890e6e..d13c2dd5 100644
--- a/packages/gem-examples/src/console/item.ts
+++ b/packages/gem-examples/src/console/item.ts
@@ -85,11 +85,20 @@ export class ConsolePageItemElement extends GemElement {
field: 'username',
label: 'Username',
required: true,
+ async getOptions(input) {
+ await sleep(300);
+ return Array(4)
+ .fill(null)
+ .map((_, index) => ({ label: `${input}-${index}` }));
+ },
},
{
type: 'text',
field: 'name',
label: 'Name',
+ isHidden(data) {
+ return !data.username;
+ },
},
],
{
diff --git a/packages/gem-port/src/react.ts b/packages/gem-port/src/react.ts
index b8ab4201..d03239b1 100644
--- a/packages/gem-port/src/react.ts
+++ b/packages/gem-port/src/react.ts
@@ -54,7 +54,7 @@ function createReactSourceFile(elementFilePath: string, outDir: string) {
.join(';\n')}
}
- const ${componentName}: ForwardRefExoticComponent & RefAttributes<${componentMethodsName}>> = forwardRef<${componentMethodsName}, ${componentPropsName}>(function (props, ref): JSX.Element {
+ export const ${componentName}: ForwardRefExoticComponent & RefAttributes<${componentMethodsName}>> = forwardRef<${componentMethodsName}, ${componentPropsName}>(function (props, ref): JSX.Element {
const elementRef = useRef<${constructorName}>(null);
useImperativeHandle(ref, () => {
return {