mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
chore: rename variable-plugin in demo to sync-variable-plugin
This commit is contained in:
parent
700221c69b
commit
f9febe5fc3
@ -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<SVGSVGElement>) {
|
||||
return (
|
||||
@ -43,7 +43,7 @@ export function FxExpression(props: FxExpressionProps) {
|
||||
<VariableSelector
|
||||
value={value.content}
|
||||
style={{ flexGrow: 1 }}
|
||||
onChange={v => onChange({ type: 'expression', content: v })}
|
||||
onChange={(v) => onChange({ type: 'expression', content: v })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -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<PropertyEditProps> = props => {
|
||||
export const PropertyEdit: React.FC<PropertyEditProps> = (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<PropertyEditProps> = 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)}
|
||||
/>
|
||||
<Input
|
||||
value={inputKey}
|
||||
disabled={disabled}
|
||||
onChange={v => 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<PropertyEditProps> = props => {
|
||||
<VariableSelector
|
||||
value={value.default}
|
||||
disabled={disabled}
|
||||
onChange={val => updateProperty('default', val)}
|
||||
onChange={(val) => updateProperty('default', val)}
|
||||
style={{ flexGrow: 1, height: 32 }}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
disabled={disabled}
|
||||
value={value.default}
|
||||
onChange={val => updateProperty('default', val)}
|
||||
onChange={(val) => updateProperty('default', val)}
|
||||
/>
|
||||
)}
|
||||
{props.onDelete && !disabled && (
|
||||
|
||||
@ -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<TypeSelectorProps> = props => {
|
||||
export const TypeSelector: React.FC<TypeSelectorProps> = (props) => {
|
||||
const { value, disabled } = props;
|
||||
const icon = VariableTypeIcons[value as any];
|
||||
return (
|
||||
@ -22,7 +22,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
|
||||
disabled={disabled}
|
||||
render={
|
||||
<Dropdown.Menu>
|
||||
{dropdownMenus.map(key => (
|
||||
{dropdownMenus.map((key) => (
|
||||
<Dropdown.Item
|
||||
key={key}
|
||||
onClick={() => {
|
||||
@ -39,7 +39,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
|
||||
<Tag
|
||||
color="white"
|
||||
style={props.style}
|
||||
onClick={e => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
* 剪切板插件
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from './sync-variable-plugin';
|
||||
@ -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<SyncVariablePluginOptions> =
|
||||
definePluginCreator<SyncVariablePluginOptions, FixedLayoutPluginContext>({
|
||||
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'));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -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<string, JsonSchema>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from './variable-plugin';
|
||||
@ -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<VariablePluginOptions> = 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>(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'));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -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<SVGSVGElement>) {
|
||||
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}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -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<PropertyEditProps> = props => {
|
||||
export const PropertyEdit: React.FC<PropertyEditProps> = (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<PropertyEditProps> = 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)}
|
||||
/>
|
||||
<Input
|
||||
value={inputKey}
|
||||
disabled={disabled}
|
||||
onChange={v => 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<PropertyEditProps> = props => {
|
||||
<VariableSelector
|
||||
value={value.default}
|
||||
disabled={disabled}
|
||||
onChange={val => updateProperty('default', val)}
|
||||
onChange={(val) => updateProperty('default', val)}
|
||||
style={{ flexGrow: 1, height: 32 }}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
disabled={disabled}
|
||||
value={value.default}
|
||||
onChange={val => updateProperty('default', val)}
|
||||
onChange={(val) => updateProperty('default', val)}
|
||||
/>
|
||||
)}
|
||||
{props.onDelete && !disabled && (
|
||||
|
||||
@ -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<TypeSelectorProps> = props => {
|
||||
export const TypeSelector: React.FC<TypeSelectorProps> = (props) => {
|
||||
const { value, disabled } = props;
|
||||
const icon = VariableTypeIcons[value as any];
|
||||
return (
|
||||
@ -22,7 +22,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
|
||||
disabled={disabled}
|
||||
render={
|
||||
<Dropdown.Menu>
|
||||
{dropdownMenus.map(key => (
|
||||
{dropdownMenus.map((key) => (
|
||||
<Dropdown.Item
|
||||
key={key}
|
||||
onClick={() => {
|
||||
@ -39,7 +39,7 @@ export const TypeSelector: React.FC<TypeSelectorProps> = props => {
|
||||
<Tag
|
||||
color="white"
|
||||
style={props.style}
|
||||
onClick={e => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
* 自动对齐及辅助线插件
|
||||
|
||||
@ -1 +1 @@
|
||||
export { createVariablePlugin } from './variable-plugin/variable-plugin';
|
||||
export { createSyncVariablePlugin } from './sync-variable-plugin/sync-variable-plugin';
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export * from './sync-variable-plugin';
|
||||
@ -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<SyncVariablePluginOptions> =
|
||||
definePluginCreator<SyncVariablePluginOptions, FreeLayoutPluginContext>({
|
||||
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'));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -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<string, JsonSchema>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from './variable-plugin';
|
||||
@ -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<VariablePluginOptions> = 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>(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'));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user