From f9febe5fc3f5dc2e7c58ed30ce49c84cc9d74d4e Mon Sep 17 00:00:00 2001 From: sanmaopep Date: Tue, 4 Mar 2025 10:24:05 +0800 Subject: [PATCH] chore: rename variable-plugin in demo to sync-variable-plugin --- .../form-components/fx-expression/index.tsx | 4 +- .../properties-edit/property-edit.tsx | 12 +-- .../src/form-components/type-selector.tsx | 8 +- .../src/form-components/type-tag.tsx | 2 +- .../src/hooks/use-editor-props.ts | 4 +- apps/demo-fixed-layout/src/plugins/index.ts | 2 +- .../icons.tsx | 0 .../src/plugins/sync-variable-plugin/index.ts | 1 + .../sync-variable-plugin.ts | 78 +++++++++++++++++++ .../utils.ts | 23 +++++- .../variable-selector/index.module.less | 0 .../variable-selector/index.tsx | 0 .../variable-selector/use-variable-tree.ts | 0 .../src/plugins/variable-plugin/index.ts | 1 - .../variable-plugin/variable-plugin.ts | 65 ---------------- .../form-components/fx-expression/index.tsx | 4 +- .../properties-edit/property-edit.tsx | 12 +-- .../src/form-components/type-selector.tsx | 8 +- .../src/form-components/type-tag.tsx | 2 +- .../src/hooks/use-editor-props.tsx | 4 +- apps/demo-free-layout/src/plugins/index.ts | 2 +- .../icons.tsx | 0 .../src/plugins/sync-variable-plugin/index.ts | 1 + .../sync-variable-plugin.ts | 78 +++++++++++++++++++ .../utils.ts | 23 +++++- .../variable-selector/index.module.less | 0 .../variable-selector/index.tsx | 0 .../variable-selector/use-variable-tree.ts | 0 .../src/plugins/variable-plugin/index.ts | 1 - .../variable-plugin/variable-plugin.ts | 65 ---------------- 30 files changed, 234 insertions(+), 166 deletions(-) rename apps/demo-fixed-layout/src/plugins/{variable-plugin => sync-variable-plugin}/icons.tsx (100%) create mode 100644 apps/demo-fixed-layout/src/plugins/sync-variable-plugin/index.ts create mode 100644 apps/demo-fixed-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts rename apps/demo-fixed-layout/src/plugins/{variable-plugin => sync-variable-plugin}/utils.ts (54%) rename apps/demo-fixed-layout/src/plugins/{variable-plugin => sync-variable-plugin}/variable-selector/index.module.less (100%) rename apps/demo-fixed-layout/src/plugins/{variable-plugin => sync-variable-plugin}/variable-selector/index.tsx (100%) rename apps/demo-fixed-layout/src/plugins/{variable-plugin => sync-variable-plugin}/variable-selector/use-variable-tree.ts (100%) delete mode 100644 apps/demo-fixed-layout/src/plugins/variable-plugin/index.ts delete mode 100644 apps/demo-fixed-layout/src/plugins/variable-plugin/variable-plugin.ts rename apps/demo-free-layout/src/plugins/{variable-plugin => sync-variable-plugin}/icons.tsx (100%) create mode 100644 apps/demo-free-layout/src/plugins/sync-variable-plugin/index.ts create mode 100644 apps/demo-free-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts rename apps/demo-free-layout/src/plugins/{variable-plugin => sync-variable-plugin}/utils.ts (54%) rename apps/demo-free-layout/src/plugins/{variable-plugin => sync-variable-plugin}/variable-selector/index.module.less (100%) rename apps/demo-free-layout/src/plugins/{variable-plugin => sync-variable-plugin}/variable-selector/index.tsx (100%) rename apps/demo-free-layout/src/plugins/{variable-plugin => sync-variable-plugin}/variable-selector/use-variable-tree.ts (100%) delete mode 100644 apps/demo-free-layout/src/plugins/variable-plugin/index.ts delete mode 100644 apps/demo-free-layout/src/plugins/variable-plugin/variable-plugin.ts diff --git a/apps/demo-fixed-layout/src/form-components/fx-expression/index.tsx b/apps/demo-fixed-layout/src/form-components/fx-expression/index.tsx index 60860a4e..001bfebf 100644 --- a/apps/demo-fixed-layout/src/form-components/fx-expression/index.tsx +++ b/apps/demo-fixed-layout/src/form-components/fx-expression/index.tsx @@ -3,7 +3,7 @@ import { type SVGProps } from 'react'; import { Input, Button } from '@douyinfe/semi-ui'; import { FlowValueSchema, FlowRefValueSchema } from '../../typings'; -import { VariableSelector } from '../../plugins/variable-plugin/variable-selector'; +import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector'; export function FxIcon(props: SVGProps) { return ( @@ -43,7 +43,7 @@ export function FxExpression(props: FxExpressionProps) { onChange({ type: 'expression', content: v })} + onChange={(v) => onChange({ type: 'expression', content: v })} disabled={disabled} /> ) : ( diff --git a/apps/demo-fixed-layout/src/form-components/properties-edit/property-edit.tsx b/apps/demo-fixed-layout/src/form-components/properties-edit/property-edit.tsx index dc18c3a6..2421497d 100644 --- a/apps/demo-fixed-layout/src/form-components/properties-edit/property-edit.tsx +++ b/apps/demo-fixed-layout/src/form-components/properties-edit/property-edit.tsx @@ -5,7 +5,7 @@ import { IconCrossCircleStroked } from '@douyinfe/semi-icons'; import { TypeSelector } from '../type-selector'; import { JsonSchema } from '../../typings'; -import { VariableSelector } from '../../plugins/variable-plugin/variable-selector'; +import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector'; import { LeftColumn, Row } from './styles'; export interface PropertyEditProps { @@ -17,7 +17,7 @@ export interface PropertyEditProps { onDelete?: () => void; } -export const PropertyEdit: React.FC = props => { +export const PropertyEdit: React.FC = (props) => { const { value, disabled } = props; const [inputKey, updateKey] = useState(props.propertyKey); const updateProperty = (key: keyof JsonSchema, val: any) => { @@ -34,12 +34,12 @@ export const PropertyEdit: React.FC = props => { value={value.type} disabled={disabled} style={{ position: 'absolute', top: 6, left: 4, zIndex: 1 }} - onChange={val => updateProperty('type', val)} + onChange={(val) => updateProperty('type', val)} /> updateKey(v.trim())} + onChange={(v) => updateKey(v.trim())} onBlur={() => { if (inputKey !== '') { props.onChange(value, props.propertyKey, inputKey); @@ -54,14 +54,14 @@ export const PropertyEdit: React.FC = props => { updateProperty('default', val)} + onChange={(val) => updateProperty('default', val)} style={{ flexGrow: 1, height: 32 }} /> ) : ( updateProperty('default', val)} + onChange={(val) => updateProperty('default', val)} /> )} {props.onDelete && !disabled && ( diff --git a/apps/demo-fixed-layout/src/form-components/type-selector.tsx b/apps/demo-fixed-layout/src/form-components/type-selector.tsx index e6b6a55b..a01b992f 100644 --- a/apps/demo-fixed-layout/src/form-components/type-selector.tsx +++ b/apps/demo-fixed-layout/src/form-components/type-selector.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Tag, Dropdown } from '@douyinfe/semi-ui'; -import { VariableTypeIcons } from '../plugins/variable-plugin/icons'; +import { VariableTypeIcons } from '../plugins/sync-variable-plugin/icons'; export interface TypeSelectorProps { value?: string; @@ -12,7 +12,7 @@ export interface TypeSelectorProps { } const dropdownMenus = ['object', 'boolean', 'array', 'string', 'integer', 'number']; -export const TypeSelector: React.FC = props => { +export const TypeSelector: React.FC = (props) => { const { value, disabled } = props; const icon = VariableTypeIcons[value as any]; return ( @@ -22,7 +22,7 @@ export const TypeSelector: React.FC = props => { disabled={disabled} render={ - {dropdownMenus.map(key => ( + {dropdownMenus.map((key) => ( { @@ -39,7 +39,7 @@ export const TypeSelector: React.FC = props => { { + onClick={(e) => { e.stopPropagation(); e.preventDefault(); }} diff --git a/apps/demo-fixed-layout/src/form-components/type-tag.tsx b/apps/demo-fixed-layout/src/form-components/type-tag.tsx index 2166becb..66cb3d90 100644 --- a/apps/demo-fixed-layout/src/form-components/type-tag.tsx +++ b/apps/demo-fixed-layout/src/form-components/type-tag.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; import { Tag, Tooltip } from '@douyinfe/semi-ui'; -import { VariableTypeIcons, ArrayIcons } from '../plugins/variable-plugin/icons'; +import { VariableTypeIcons, ArrayIcons } from '../plugins/sync-variable-plugin/icons'; interface PropsType { name?: string | JSX.Element; diff --git a/apps/demo-fixed-layout/src/hooks/use-editor-props.ts b/apps/demo-fixed-layout/src/hooks/use-editor-props.ts index 181df51d..e062e4c8 100644 --- a/apps/demo-fixed-layout/src/hooks/use-editor-props.ts +++ b/apps/demo-fixed-layout/src/hooks/use-editor-props.ts @@ -14,7 +14,7 @@ import { import { type FlowNodeRegistry } from '../typings'; import { shortcutGetter } from '../shortcuts'; import { GroupBoxHeader, GroupNode } from '../plugins/group-plugin'; -import { createVariablePlugin, createClipboardPlugin } from '../plugins'; +import { createSyncVariablePlugin, createClipboardPlugin } from '../plugins'; import { defaultFormMeta } from '../nodes'; import { SelectorBoxPopover } from '../components/selector-box-popover'; import NodeAdder from '../components/node-adder'; @@ -227,7 +227,7 @@ export function useEditorProps( * Variable plugin * 变量插件 */ - createVariablePlugin({}), + createSyncVariablePlugin({}), /** * Clipboard plugin * 剪切板插件 diff --git a/apps/demo-fixed-layout/src/plugins/index.ts b/apps/demo-fixed-layout/src/plugins/index.ts index 97cbaaf6..9b740cf5 100644 --- a/apps/demo-fixed-layout/src/plugins/index.ts +++ b/apps/demo-fixed-layout/src/plugins/index.ts @@ -1,2 +1,2 @@ export { createClipboardPlugin } from './clipboard-plugin/create-clipboard-plugin'; -export { createVariablePlugin } from './variable-plugin/variable-plugin'; +export { createSyncVariablePlugin } from './sync-variable-plugin/sync-variable-plugin'; diff --git a/apps/demo-fixed-layout/src/plugins/variable-plugin/icons.tsx b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/icons.tsx similarity index 100% rename from apps/demo-fixed-layout/src/plugins/variable-plugin/icons.tsx rename to apps/demo-fixed-layout/src/plugins/sync-variable-plugin/icons.tsx diff --git a/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/index.ts b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/index.ts new file mode 100644 index 00000000..3fbb018d --- /dev/null +++ b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/index.ts @@ -0,0 +1 @@ +export * from './sync-variable-plugin'; diff --git a/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts new file mode 100644 index 00000000..ae8e650e --- /dev/null +++ b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts @@ -0,0 +1,78 @@ +import { + definePluginCreator, + FlowNodeVariableData, + getNodeForm, + PluginCreator, + FixedLayoutPluginContext, + ASTFactory, +} from '@flowgram.ai/fixed-layout-editor'; + +import { createASTFromJSONSchema } from './utils'; + +export interface SyncVariablePluginOptions {} + +/** + * Creates a plugin to synchronize output data to the variable engine when nodes are created or updated. + * @param ctx - The plugin context, containing the document and other relevant information. + * @param options - Plugin options, currently an empty object. + */ +export const createSyncVariablePlugin: PluginCreator = + definePluginCreator({ + onInit(ctx, options) { + const flowDocument = ctx.document; + + // Listen for node creation events + flowDocument.onNodeCreate(({ node }) => { + const form = getNodeForm(node); + const variableData = node.getData(FlowNodeVariableData); + + /** + * Synchronizes output data to the variable engine. + * @param value - The output data to synchronize. + */ + const syncOutputs = (value: any) => { + if (!value) { + // If the output data is empty, clear the variable + variableData.clearVar(); + return; + } + + // Create an Type AST from the output data's JSON schema + // NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL + const typeAST = createASTFromJSONSchema(value); + + if (typeAST) { + // Use the node's title or its ID as the title for the variable + const title = form?.getValueIn('title') || node.id; + + // Set the variable in the variable engine + variableData.setVar( + ASTFactory.createVariableDeclaration({ + meta: { + title: `${title}.outputs`, + // NOTICE: You can add more metadata here as needed + }, + key: `${node.id}.outputs`, + type: typeAST, + }) + ); + } else { + // If the AST cannot be created, clear the variable + variableData.clearVar(); + } + }; + + if (form) { + // Initially synchronize the output data + syncOutputs(form.getValueIn('outputs')); + + // Listen for changes in the form values and re-synchronize when outputs change + form.onFormValuesChange((props) => { + if (props.name.match(/^outputs/)) { + syncOutputs(form.getValueIn('outputs')); + } + }); + } + }); + }, + }); diff --git a/apps/demo-fixed-layout/src/plugins/variable-plugin/utils.ts b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/utils.ts similarity index 54% rename from apps/demo-fixed-layout/src/plugins/variable-plugin/utils.ts rename to apps/demo-fixed-layout/src/plugins/sync-variable-plugin/utils.ts index 610a7d6e..5690a134 100644 --- a/apps/demo-fixed-layout/src/plugins/variable-plugin/utils.ts +++ b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/utils.ts @@ -3,12 +3,32 @@ import { ASTFactory, type ASTNodeJSON } from '@flowgram.ai/fixed-layout-editor'; import { type JsonSchema } from '../../typings'; +/** + * Sorts the properties of a JSON schema based on the 'extra.index' field. + * If the 'extra.index' field is not present, the property will be treated as having an index of 0. + * + * @param properties - The properties of the JSON schema to sort. + * @returns A sorted array of property entries. + */ function sortProperties(properties: Record) { return Object.entries(properties).sort( (a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0) ); } +/** + * Converts a JSON schema to an Abstract Syntax Tree (AST) representation. + * This function recursively processes the JSON schema and creates corresponding AST nodes. + * + * For more information on JSON Schema, refer to the official documentation: + * https://json-schema.org/ + * + * Note: Depending on your business needs, you can use your own Domain-Specific Language (DSL) + * Create a new function to convert your custom DSL to AST directly. + * + * @param jsonSchema - The JSON schema to convert. + * @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized. + */ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | undefined { const { type } = jsonSchema || {}; @@ -39,7 +59,8 @@ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | u return ASTFactory.createInteger(); default: - // Camel case to variable-core type + // If the type is not recognized, return undefined. + // You can extend this function to handle custom types if needed. return; } } diff --git a/apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/index.module.less b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less similarity index 100% rename from apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/index.module.less rename to apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less diff --git a/apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/index.tsx b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx similarity index 100% rename from apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/index.tsx rename to apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx diff --git a/apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/use-variable-tree.ts b/apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts similarity index 100% rename from apps/demo-fixed-layout/src/plugins/variable-plugin/variable-selector/use-variable-tree.ts rename to apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts diff --git a/apps/demo-fixed-layout/src/plugins/variable-plugin/index.ts b/apps/demo-fixed-layout/src/plugins/variable-plugin/index.ts deleted file mode 100644 index 16578beb..00000000 --- a/apps/demo-fixed-layout/src/plugins/variable-plugin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './variable-plugin'; diff --git a/apps/demo-fixed-layout/src/plugins/variable-plugin/variable-plugin.ts b/apps/demo-fixed-layout/src/plugins/variable-plugin/variable-plugin.ts deleted file mode 100644 index cda9b788..00000000 --- a/apps/demo-fixed-layout/src/plugins/variable-plugin/variable-plugin.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - definePluginCreator, - FixedLayoutPluginContext, - FlowNodeVariableData, - getNodeForm, - PluginCreator, - ASTFactory, -} from '@flowgram.ai/fixed-layout-editor'; - -import { createASTFromJSONSchema } from './utils'; - -export interface VariablePluginOptions {} - -export const createVariablePlugin: PluginCreator = definePluginCreator< - VariablePluginOptions, - FixedLayoutPluginContext ->({ - onInit(ctx, options) { - const flowDocument = ctx.document; - - /** - * Listens to the creation of nodes and synchronizes outputs data to the variable engine - * 监听节点的创建,并将 outputs 数据同步给变量引擎 - */ - flowDocument.onNodeCreate(({ node }) => { - const form = getNodeForm(node); - const variableData = node.getData(FlowNodeVariableData); - - const syncOutputs = (value: any) => { - if (!value) { - variableData.clearVar(); - return; - } - - const typeAST = createASTFromJSONSchema(value); - - if (typeAST) { - const title = form?.getValueIn('title') || node.id; - - variableData.setVar( - ASTFactory.createVariableDeclaration({ - meta: { - title: `${title}.outputs`, - }, - key: `${node.id}.outputs`, - type: typeAST, - }) - ); - return; - } else { - variableData.clearVar(); - } - }; - if (form) { - syncOutputs(form.getValueIn('outputs')); - // Listen outputs change - form.onFormValuesChange((props) => { - if (props.name.match(/^outputs/)) { - syncOutputs(form.getValueIn('outputs')); - } - }); - } - }); - }, -}); diff --git a/apps/demo-free-layout/src/form-components/fx-expression/index.tsx b/apps/demo-free-layout/src/form-components/fx-expression/index.tsx index 36b6327a..b83cc3e1 100644 --- a/apps/demo-free-layout/src/form-components/fx-expression/index.tsx +++ b/apps/demo-free-layout/src/form-components/fx-expression/index.tsx @@ -3,7 +3,7 @@ import React, { type SVGProps } from 'react'; import { Input, Button } from '@douyinfe/semi-ui'; import { FlowRefValueSchema, FlowLiteralValueSchema } from '../../typings'; -import { VariableSelector } from '../../plugins/variable-plugin/variable-selector'; +import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector'; export function FxIcon(props: SVGProps) { return ( @@ -45,7 +45,7 @@ export function FxExpression(props: FxExpressionProps) { value={value.content} hasError={props.hasError} style={{ flexGrow: 1 }} - onChange={v => onChange({ type: 'expression', content: v })} + onChange={(v) => onChange({ type: 'expression', content: v })} disabled={disabled} /> ) : ( diff --git a/apps/demo-free-layout/src/form-components/properties-edit/property-edit.tsx b/apps/demo-free-layout/src/form-components/properties-edit/property-edit.tsx index dc18c3a6..2421497d 100644 --- a/apps/demo-free-layout/src/form-components/properties-edit/property-edit.tsx +++ b/apps/demo-free-layout/src/form-components/properties-edit/property-edit.tsx @@ -5,7 +5,7 @@ import { IconCrossCircleStroked } from '@douyinfe/semi-icons'; import { TypeSelector } from '../type-selector'; import { JsonSchema } from '../../typings'; -import { VariableSelector } from '../../plugins/variable-plugin/variable-selector'; +import { VariableSelector } from '../../plugins/sync-variable-plugin/variable-selector'; import { LeftColumn, Row } from './styles'; export interface PropertyEditProps { @@ -17,7 +17,7 @@ export interface PropertyEditProps { onDelete?: () => void; } -export const PropertyEdit: React.FC = props => { +export const PropertyEdit: React.FC = (props) => { const { value, disabled } = props; const [inputKey, updateKey] = useState(props.propertyKey); const updateProperty = (key: keyof JsonSchema, val: any) => { @@ -34,12 +34,12 @@ export const PropertyEdit: React.FC = props => { value={value.type} disabled={disabled} style={{ position: 'absolute', top: 6, left: 4, zIndex: 1 }} - onChange={val => updateProperty('type', val)} + onChange={(val) => updateProperty('type', val)} /> updateKey(v.trim())} + onChange={(v) => updateKey(v.trim())} onBlur={() => { if (inputKey !== '') { props.onChange(value, props.propertyKey, inputKey); @@ -54,14 +54,14 @@ export const PropertyEdit: React.FC = props => { updateProperty('default', val)} + onChange={(val) => updateProperty('default', val)} style={{ flexGrow: 1, height: 32 }} /> ) : ( updateProperty('default', val)} + onChange={(val) => updateProperty('default', val)} /> )} {props.onDelete && !disabled && ( diff --git a/apps/demo-free-layout/src/form-components/type-selector.tsx b/apps/demo-free-layout/src/form-components/type-selector.tsx index e6b6a55b..a01b992f 100644 --- a/apps/demo-free-layout/src/form-components/type-selector.tsx +++ b/apps/demo-free-layout/src/form-components/type-selector.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Tag, Dropdown } from '@douyinfe/semi-ui'; -import { VariableTypeIcons } from '../plugins/variable-plugin/icons'; +import { VariableTypeIcons } from '../plugins/sync-variable-plugin/icons'; export interface TypeSelectorProps { value?: string; @@ -12,7 +12,7 @@ export interface TypeSelectorProps { } const dropdownMenus = ['object', 'boolean', 'array', 'string', 'integer', 'number']; -export const TypeSelector: React.FC = props => { +export const TypeSelector: React.FC = (props) => { const { value, disabled } = props; const icon = VariableTypeIcons[value as any]; return ( @@ -22,7 +22,7 @@ export const TypeSelector: React.FC = props => { disabled={disabled} render={ - {dropdownMenus.map(key => ( + {dropdownMenus.map((key) => ( { @@ -39,7 +39,7 @@ export const TypeSelector: React.FC = props => { { + onClick={(e) => { e.stopPropagation(); e.preventDefault(); }} diff --git a/apps/demo-free-layout/src/form-components/type-tag.tsx b/apps/demo-free-layout/src/form-components/type-tag.tsx index 2166becb..66cb3d90 100644 --- a/apps/demo-free-layout/src/form-components/type-tag.tsx +++ b/apps/demo-free-layout/src/form-components/type-tag.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; import { Tag, Tooltip } from '@douyinfe/semi-ui'; -import { VariableTypeIcons, ArrayIcons } from '../plugins/variable-plugin/icons'; +import { VariableTypeIcons, ArrayIcons } from '../plugins/sync-variable-plugin/icons'; interface PropsType { name?: string | JSX.Element; diff --git a/apps/demo-free-layout/src/hooks/use-editor-props.tsx b/apps/demo-free-layout/src/hooks/use-editor-props.tsx index b6ebeb11..b20d9490 100644 --- a/apps/demo-free-layout/src/hooks/use-editor-props.tsx +++ b/apps/demo-free-layout/src/hooks/use-editor-props.tsx @@ -12,7 +12,7 @@ import { FreeLayoutProps } from '@flowgram.ai/free-layout-editor'; import { FlowNodeRegistry, FlowDocumentJSON } from '../typings'; import { shortcuts } from '../shortcuts'; -import { createVariablePlugin } from '../plugins'; +import { createSyncVariablePlugin } from '../plugins'; import { defaultFormMeta } from '../nodes/default-form-meta'; import { SelectorBoxPopover } from '../components/selector-box-popover'; import { BaseNode, LineAddButton, NodePanel } from '../components'; @@ -184,7 +184,7 @@ export function useEditorProps( * Variable plugin * 变量插件 */ - createVariablePlugin({}), + createSyncVariablePlugin({}), /** * Snap plugin * 自动对齐及辅助线插件 diff --git a/apps/demo-free-layout/src/plugins/index.ts b/apps/demo-free-layout/src/plugins/index.ts index c8a1acb6..b344fa7c 100644 --- a/apps/demo-free-layout/src/plugins/index.ts +++ b/apps/demo-free-layout/src/plugins/index.ts @@ -1 +1 @@ -export { createVariablePlugin } from './variable-plugin/variable-plugin'; +export { createSyncVariablePlugin } from './sync-variable-plugin/sync-variable-plugin'; diff --git a/apps/demo-free-layout/src/plugins/variable-plugin/icons.tsx b/apps/demo-free-layout/src/plugins/sync-variable-plugin/icons.tsx similarity index 100% rename from apps/demo-free-layout/src/plugins/variable-plugin/icons.tsx rename to apps/demo-free-layout/src/plugins/sync-variable-plugin/icons.tsx diff --git a/apps/demo-free-layout/src/plugins/sync-variable-plugin/index.ts b/apps/demo-free-layout/src/plugins/sync-variable-plugin/index.ts new file mode 100644 index 00000000..3fbb018d --- /dev/null +++ b/apps/demo-free-layout/src/plugins/sync-variable-plugin/index.ts @@ -0,0 +1 @@ +export * from './sync-variable-plugin'; diff --git a/apps/demo-free-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts b/apps/demo-free-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts new file mode 100644 index 00000000..5b4f1dd7 --- /dev/null +++ b/apps/demo-free-layout/src/plugins/sync-variable-plugin/sync-variable-plugin.ts @@ -0,0 +1,78 @@ +import { + definePluginCreator, + FlowNodeVariableData, + getNodeForm, + PluginCreator, + FreeLayoutPluginContext, + ASTFactory, +} from '@flowgram.ai/free-layout-editor'; + +import { createASTFromJSONSchema } from './utils'; + +export interface SyncVariablePluginOptions {} + +/** + * Creates a plugin to synchronize output data to the variable engine when nodes are created or updated. + * @param ctx - The plugin context, containing the document and other relevant information. + * @param options - Plugin options, currently an empty object. + */ +export const createSyncVariablePlugin: PluginCreator = + definePluginCreator({ + onInit(ctx, options) { + const flowDocument = ctx.document; + + // Listen for node creation events + flowDocument.onNodeCreate(({ node }) => { + const form = getNodeForm(node); + const variableData = node.getData(FlowNodeVariableData); + + /** + * Synchronizes output data to the variable engine. + * @param value - The output data to synchronize. + */ + const syncOutputs = (value: any) => { + if (!value) { + // If the output data is empty, clear the variable + variableData.clearVar(); + return; + } + + // Create an Type AST from the output data's JSON schema + // NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL + const typeAST = createASTFromJSONSchema(value); + + if (typeAST) { + // Use the node's title or its ID as the title for the variable + const title = form?.getValueIn('title') || node.id; + + // Set the variable in the variable engine + variableData.setVar( + ASTFactory.createVariableDeclaration({ + meta: { + title: `${title}.outputs`, + // NOTICE: You can add more metadata here as needed + }, + key: `${node.id}.outputs`, + type: typeAST, + }) + ); + } else { + // If the AST cannot be created, clear the variable + variableData.clearVar(); + } + }; + + if (form) { + // Initially synchronize the output data + syncOutputs(form.getValueIn('outputs')); + + // Listen for changes in the form values and re-synchronize when outputs change + form.onFormValuesChange((props) => { + if (props.name.match(/^outputs/)) { + syncOutputs(form.getValueIn('outputs')); + } + }); + } + }); + }, + }); diff --git a/apps/demo-free-layout/src/plugins/variable-plugin/utils.ts b/apps/demo-free-layout/src/plugins/sync-variable-plugin/utils.ts similarity index 54% rename from apps/demo-free-layout/src/plugins/variable-plugin/utils.ts rename to apps/demo-free-layout/src/plugins/sync-variable-plugin/utils.ts index 94863e82..7f62bffa 100644 --- a/apps/demo-free-layout/src/plugins/variable-plugin/utils.ts +++ b/apps/demo-free-layout/src/plugins/sync-variable-plugin/utils.ts @@ -3,12 +3,32 @@ import { ASTFactory, type ASTNodeJSON } from '@flowgram.ai/free-layout-editor'; import { type JsonSchema } from '../../typings'; +/** + * Sorts the properties of a JSON schema based on the 'extra.index' field. + * If the 'extra.index' field is not present, the property will be treated as having an index of 0. + * + * @param properties - The properties of the JSON schema to sort. + * @returns A sorted array of property entries. + */ function sortProperties(properties: Record) { return Object.entries(properties).sort( (a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0) ); } +/** + * Converts a JSON schema to an Abstract Syntax Tree (AST) representation. + * This function recursively processes the JSON schema and creates corresponding AST nodes. + * + * For more information on JSON Schema, refer to the official documentation: + * https://json-schema.org/ + * + * Note: Depending on your business needs, you can use your own Domain-Specific Language (DSL) + * Create a new function to convert your custom DSL to AST directly. + * + * @param jsonSchema - The JSON schema to convert. + * @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized. + */ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | undefined { const { type } = jsonSchema || {}; @@ -39,7 +59,8 @@ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | u return ASTFactory.createInteger(); default: - // Camel case to variable-core type + // If the type is not recognized, return undefined. + // You can extend this function to handle custom types if needed. return; } } diff --git a/apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/index.module.less b/apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less similarity index 100% rename from apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/index.module.less rename to apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.module.less diff --git a/apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/index.tsx b/apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx similarity index 100% rename from apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/index.tsx rename to apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/index.tsx diff --git a/apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/use-variable-tree.ts b/apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts similarity index 100% rename from apps/demo-free-layout/src/plugins/variable-plugin/variable-selector/use-variable-tree.ts rename to apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts diff --git a/apps/demo-free-layout/src/plugins/variable-plugin/index.ts b/apps/demo-free-layout/src/plugins/variable-plugin/index.ts deleted file mode 100644 index 16578beb..00000000 --- a/apps/demo-free-layout/src/plugins/variable-plugin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './variable-plugin'; diff --git a/apps/demo-free-layout/src/plugins/variable-plugin/variable-plugin.ts b/apps/demo-free-layout/src/plugins/variable-plugin/variable-plugin.ts deleted file mode 100644 index a43d8a74..00000000 --- a/apps/demo-free-layout/src/plugins/variable-plugin/variable-plugin.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - definePluginCreator, - FlowNodeVariableData, - getNodeForm, - PluginCreator, - FreeLayoutPluginContext, - ASTFactory, -} from '@flowgram.ai/free-layout-editor'; - -import { createASTFromJSONSchema } from './utils'; - -export interface VariablePluginOptions {} - -export const createVariablePlugin: PluginCreator = definePluginCreator< - VariablePluginOptions, - FreeLayoutPluginContext ->({ - onInit(ctx, options) { - const flowDocument = ctx.document; - - /** - * Listens to the creation of nodes and synchronizes outputs data to the variable engine - * 监听节点的创建,并将 outputs 数据同步给变量引擎 - */ - flowDocument.onNodeCreate(({ node }) => { - const form = getNodeForm(node); - const variableData = node.getData(FlowNodeVariableData); - - const syncOutputs = (value: any) => { - if (!value) { - variableData.clearVar(); - return; - } - - const typeAST = createASTFromJSONSchema(value); - - if (typeAST) { - const title = form?.getValueIn('title') || node.id; - - variableData.setVar( - ASTFactory.createVariableDeclaration({ - meta: { - title: `${title}.outputs`, - }, - key: `${node.id}.outputs`, - type: typeAST, - }) - ); - return; - } else { - variableData.clearVar(); - } - }; - if (form) { - syncOutputs(form.getValueIn('outputs')); - // Listen outputs change - form.onFormValuesChange((props) => { - if (props.name.match(/^outputs/)) { - syncOutputs(form.getValueIn('outputs')); - } - }); - } - }); - }, -});