From dd727c8d631e81080ce89fc634d71e4d7ba42878 Mon Sep 17 00:00:00 2001 From: Yiwei Mao Date: Fri, 23 May 2025 12:41:45 +0800 Subject: [PATCH] feat(material): auto rename effect (#261) --- .../src/nodes/default-form-meta.tsx | 4 + .../src/nodes/default-form-meta.tsx | 4 + common/config/rush/pnpm-lock.yaml | 7 ++ .../materials/form-materials/package.json | 3 +- .../components/blur-input.tsx | 22 ++++ .../components/json-schema-editor/index.tsx | 7 +- .../components/variable-selector/index.tsx | 2 +- .../src/effects/auto-rename-ref/config.json | 5 + .../src/effects/auto-rename-ref/index.ts | 104 ++++++++++++++++++ .../form-materials/src/effects/index.ts | 1 + .../variable-core/src/ast/ast-registers.ts | 11 +- 11 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 packages/materials/form-materials/src/components/json-schema-editor/components/blur-input.tsx create mode 100644 packages/materials/form-materials/src/effects/auto-rename-ref/config.json create mode 100644 packages/materials/form-materials/src/effects/auto-rename-ref/index.ts diff --git a/apps/demo-fixed-layout/src/nodes/default-form-meta.tsx b/apps/demo-fixed-layout/src/nodes/default-form-meta.tsx index bf08c457..ad6865c3 100644 --- a/apps/demo-fixed-layout/src/nodes/default-form-meta.tsx +++ b/apps/demo-fixed-layout/src/nodes/default-form-meta.tsx @@ -1,3 +1,4 @@ +import { autoRenameRefEffect } from '@flowgram.ai/form-materials'; import { FormRenderProps, FormMeta, ValidateTrigger } from '@flowgram.ai/fixed-layout-editor'; import { FlowNodeJSON } from '../typings'; @@ -27,4 +28,7 @@ export const defaultFormMeta: FormMeta = { return undefined; }, }, + effect: { + inputsValues: autoRenameRefEffect, + }, }; diff --git a/apps/demo-free-layout/src/nodes/default-form-meta.tsx b/apps/demo-free-layout/src/nodes/default-form-meta.tsx index 320cf405..8e82e36b 100644 --- a/apps/demo-free-layout/src/nodes/default-form-meta.tsx +++ b/apps/demo-free-layout/src/nodes/default-form-meta.tsx @@ -1,4 +1,5 @@ import { FormRenderProps, FormMeta, ValidateTrigger } from '@flowgram.ai/free-layout-editor'; +import { autoRenameRefEffect } from '@flowgram.ai/form-materials'; import { FlowNodeJSON } from '../typings'; import { FormHeader, FormContent, FormInputs, FormOutputs } from '../form-components'; @@ -27,4 +28,7 @@ export const defaultFormMeta: FormMeta = { return undefined; }, }, + effect: { + inputsValues: autoRenameRefEffect, + }, }; diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 1cab1e59..f45bd513 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -1916,6 +1916,9 @@ importers: commander: specifier: ^11.0.0 version: 11.1.0 + immer: + specifier: ~10.1.1 + version: 10.1.1 inquirer: specifier: ^9.2.7 version: 9.3.7 @@ -11746,6 +11749,10 @@ packages: dev: true optional: true + /immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + dev: false + /immutable@5.0.3: resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} diff --git a/packages/materials/form-materials/package.json b/packages/materials/form-materials/package.json index dbd5d9d9..75bd6654 100644 --- a/packages/materials/form-materials/package.json +++ b/packages/materials/form-materials/package.json @@ -40,7 +40,8 @@ "nanoid": "^4.0.2", "commander": "^11.0.0", "chalk": "^5.3.0", - "inquirer": "^9.2.7" + "inquirer": "^9.2.7", + "immer": "~10.1.1" }, "devDependencies": { "@flowgram.ai/eslint-config": "workspace:*", diff --git a/packages/materials/form-materials/src/components/json-schema-editor/components/blur-input.tsx b/packages/materials/form-materials/src/components/json-schema-editor/components/blur-input.tsx new file mode 100644 index 00000000..71eb932a --- /dev/null +++ b/packages/materials/form-materials/src/components/json-schema-editor/components/blur-input.tsx @@ -0,0 +1,22 @@ +import React, { useEffect, useState } from 'react'; + +import Input, { InputProps } from '@douyinfe/semi-ui/lib/es/input'; + +export function BlurInput(props: InputProps) { + const [value, setValue] = useState(''); + + useEffect(() => { + setValue(props.value as string); + }, [props.value]); + + return ( + { + setValue(value); + }} + onBlur={(e) => props.onChange?.(value, e)} + /> + ); +} diff --git a/packages/materials/form-materials/src/components/json-schema-editor/index.tsx b/packages/materials/form-materials/src/components/json-schema-editor/index.tsx index 4c4af7aa..c35859f0 100644 --- a/packages/materials/form-materials/src/components/json-schema-editor/index.tsx +++ b/packages/materials/form-materials/src/components/json-schema-editor/index.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useState } from 'react'; -import { Button, Checkbox, IconButton, Input } from '@douyinfe/semi-ui'; +import { Button, Checkbox, IconButton } from '@douyinfe/semi-ui'; import { IconExpand, IconShrink, @@ -31,6 +31,7 @@ import { import { UIName } from './styles'; import { UIRow } from './styles'; import { usePropertiesEdit } from './hooks'; +import { BlurInput } from './components/blur-input'; export function JsonSchemaEditor(props: { value?: IJsonSchema; @@ -109,7 +110,7 @@ function PropertyEdit(props: { - {config?.descTitle ?? 'Description'} - onChange('description', value)} diff --git a/packages/materials/form-materials/src/components/variable-selector/index.tsx b/packages/materials/form-materials/src/components/variable-selector/index.tsx index 0894a384..9e2bf8e9 100644 --- a/packages/materials/form-materials/src/components/variable-selector/index.tsx +++ b/packages/materials/form-materials/src/components/variable-selector/index.tsx @@ -100,7 +100,7 @@ export const VariableSelector = ({ ); }} showClear={false} - arrowIcon={value ? null : } + arrowIcon={} triggerRender={triggerRender} placeholder={config?.placeholder ?? 'Select Variable...'} /> diff --git a/packages/materials/form-materials/src/effects/auto-rename-ref/config.json b/packages/materials/form-materials/src/effects/auto-rename-ref/config.json new file mode 100644 index 00000000..676054fa --- /dev/null +++ b/packages/materials/form-materials/src/effects/auto-rename-ref/config.json @@ -0,0 +1,5 @@ +{ + "name": "auto-rename-ref", + "depMaterials": ["flow-value"], + "depPackages": ["lodash"] +} diff --git a/packages/materials/form-materials/src/effects/auto-rename-ref/index.ts b/packages/materials/form-materials/src/effects/auto-rename-ref/index.ts new file mode 100644 index 00000000..9677fc27 --- /dev/null +++ b/packages/materials/form-materials/src/effects/auto-rename-ref/index.ts @@ -0,0 +1,104 @@ +import { isArray, isObject } from 'lodash'; +import { + DataEvent, + Effect, + EffectOptions, + VariableFieldKeyRenameService, +} from '@flowgram.ai/editor'; + +import { IFlowRefValue } from '../../typings'; + +/** + * Auto rename ref when form item's key is renamed + * + * Example: + * + * formMeta: { + * effects: { + * "inputsValues": autoRenameRefEffect, + * } + * } + */ +export const autoRenameRefEffect: EffectOptions[] = [ + { + event: DataEvent.onValueInit, + effect: ((params) => { + const { context, form, name } = params; + + const renameService = context.node.getService(VariableFieldKeyRenameService); + + const disposable = renameService.onRename(({ before, after }) => { + const beforeKeyPath = [ + ...before.parentFields.map((_field) => _field.key).reverse(), + before.key, + ]; + const afterKeyPath = [ + ...after.parentFields.map((_field) => _field.key).reverse(), + after.key, + ]; + + // traverse rename refs inside form item 'name' + traverseRef(name, form.getValueIn(name), (_drilldownName, _v) => { + if (isRefMatch(_v, beforeKeyPath)) { + _v.content = [...afterKeyPath, ...(_v.content || [])?.slice(beforeKeyPath.length)]; + form.setValueIn(_drilldownName, _v); + } + }); + }); + + return () => { + disposable.dispose(); + }; + }) as Effect, + }, +]; + +/** + * If ref value's keyPath is the under as targetKeyPath + * @param value + * @param targetKeyPath + * @returns + */ +function isRefMatch(value: IFlowRefValue, targetKeyPath: string[]) { + return targetKeyPath.every((_key, index) => _key === value.content?.[index]); +} + +/** + * If value is ref + * @param value + * @returns + */ +function isRef(value: any): value is IFlowRefValue { + return ( + value?.type === 'ref' && Array.isArray(value?.content) && typeof value?.content[0] === 'string' + ); +} + +/** + * Traverse value to find ref + * @param value + * @param options + * @returns + */ +function traverseRef(name: string, value: any, cb: (name: string, _v: IFlowRefValue) => void) { + if (isObject(value)) { + if (isRef(value)) { + cb(name, value); + return; + } + + Object.entries(value).forEach(([_key, _value]) => { + traverseRef(`${name}.${_key}`, _value, cb); + }); + return; + } + + if (isArray(value)) { + value.forEach((_value, idx) => { + traverseRef(`${name}[${idx}]`, _value, cb); + }); + return; + } + + return; +} diff --git a/packages/materials/form-materials/src/effects/index.ts b/packages/materials/form-materials/src/effects/index.ts index d225fe8a..efaf3d89 100644 --- a/packages/materials/form-materials/src/effects/index.ts +++ b/packages/materials/form-materials/src/effects/index.ts @@ -1,2 +1,3 @@ export * from './provide-batch-input'; export * from './provide-batch-outputs'; +export * from './auto-rename-ref'; diff --git a/packages/variable-engine/variable-core/src/ast/ast-registers.ts b/packages/variable-engine/variable-core/src/ast/ast-registers.ts index 2b602179..45c562f9 100644 --- a/packages/variable-engine/variable-core/src/ast/ast-registers.ts +++ b/packages/variable-engine/variable-core/src/ast/ast-registers.ts @@ -13,7 +13,12 @@ import { ObjectType, StringType, } from './type'; -import { EnumerateExpression, KeyPathExpression, WrapArrayExpression } from './expression'; +import { + EnumerateExpression, + // KeyPathExpression, + KeyPathExpressionV2, + WrapArrayExpression, +} from './expression'; import { Property, VariableDeclaration, VariableDeclarationList } from './declaration'; import { DataNode, MapNode } from './common'; import { ASTNode, ASTNodeRegistry } from './ast-node'; @@ -41,7 +46,9 @@ export class ASTRegisters { this.registerAST(Property); this.registerAST(VariableDeclaration); this.registerAST(VariableDeclarationList); - this.registerAST(KeyPathExpression); + // this.registerAST(KeyPathExpression); + this.registerAST(KeyPathExpressionV2); + this.registerAST(EnumerateExpression); this.registerAST(WrapArrayExpression); this.registerAST(MapNode);