Просмотр исходного кода

feat(client): add node.form instead of getNodeForm(node) (#846)

* feat(client): add node.form instead of getNodeForm(node)

* refactor(demo): getNodeForm(node) -> node.form

* docs: update node.form docs
xiamidaxia 3 месяцев назад
Родитель
Сommit
9191f5b3f8
20 измененных файлов с 67 добавлено и 46 удалено
  1. 4 4
      apps/demo-fixed-layout/src/components/tools/save.tsx
  2. 4 4
      apps/demo-free-layout/src/components/testrun/testrun-button/index.tsx
  3. 4 4
      apps/demo-free-layout/src/components/tools/save.tsx
  4. 1 2
      apps/demo-free-layout/src/plugins/runtime-plugin/runtime-service/index.ts
  5. 5 3
      apps/docs/src/en/guide/advanced/fixed-layout/node.mdx
  6. 5 3
      apps/docs/src/en/guide/advanced/free-layout/node.mdx
  7. 5 4
      apps/docs/src/zh/guide/advanced/fixed-layout/node.mdx
  8. 5 3
      apps/docs/src/zh/guide/advanced/free-layout/node.mdx
  9. 16 0
      packages/client/editor/src/preset/editor-default-preset.ts
  10. 7 0
      packages/client/editor/src/preset/editor-props.ts
  11. 1 2
      packages/client/free-layout-editor/__tests__/history.test.ts
  12. 1 2
      packages/materials/form-antd-materials/src/effects/provide-batch-input/index.ts
  13. 1 2
      packages/materials/form-antd-materials/src/effects/provide-batch-outputs/index.ts
  14. 1 2
      packages/materials/form-antd-materials/src/effects/provide-json-schema-outputs/index.ts
  15. 1 2
      packages/materials/form-antd-materials/src/form-plugins/batch-outputs-plugin/index.ts
  16. 1 2
      packages/materials/form-materials/src/effects/provide-batch-input/index.ts
  17. 1 2
      packages/materials/form-materials/src/effects/provide-json-schema-outputs/index.ts
  18. 1 2
      packages/materials/form-materials/src/form-plugins/batch-outputs-plugin/index.ts
  19. 1 2
      packages/materials/form-materials/src/form-plugins/infer-assign-plugin/index.ts
  20. 2 1
      packages/node-engine/node/src/get-node-form.tsx

+ 4 - 4
apps/demo-fixed-layout/src/components/tools/save.tsx

@@ -5,7 +5,7 @@
 
 import { useState, useEffect, useCallback } from 'react';
 
-import { useClientContext, getNodeForm, FlowNodeEntity } from '@flowgram.ai/fixed-layout-editor';
+import { useClientContext, FlowNodeEntity } from '@flowgram.ai/fixed-layout-editor';
 import { Button, Badge } from '@douyinfe/semi-ui';
 
 export function Save(props: { disabled: boolean }) {
@@ -13,7 +13,7 @@ export function Save(props: { disabled: boolean }) {
   const clientContext = useClientContext();
 
   const updateValidateData = useCallback(() => {
-    const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
+    const allForms = clientContext.document.getAllNodes().map((node) => node.form);
     const count = allForms.filter((form) => form?.state.invalid).length;
     setErrorCount(count);
   }, [clientContext]);
@@ -22,7 +22,7 @@ export function Save(props: { disabled: boolean }) {
    * Validate all node and Save
    */
   const onSave = useCallback(async () => {
-    const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
+    const allForms = clientContext.document.getAllNodes().map((node) => node.form);
     await Promise.all(allForms.map(async (form) => form?.validate()));
     console.log('>>>>> save data: ', clientContext.document.toJSON());
   }, [clientContext]);
@@ -32,7 +32,7 @@ export function Save(props: { disabled: boolean }) {
      * Listen single node validate
      */
     const listenSingleNodeValidate = (node: FlowNodeEntity) => {
-      const form = getNodeForm(node);
+      const form = node.form;
       if (form) {
         const formValidateDispose = form.onValidate(() => updateValidateData());
         node.onDispose(() => formValidateDispose.dispose());

+ 4 - 4
apps/demo-free-layout/src/components/testrun/testrun-button/index.tsx

@@ -6,7 +6,7 @@
 import { useState, useEffect, useCallback } from 'react';
 
 import { usePanelManager } from '@flowgram.ai/panel-manager-plugin';
-import { useClientContext, getNodeForm, FlowNodeEntity } from '@flowgram.ai/free-layout-editor';
+import { useClientContext, FlowNodeEntity } from '@flowgram.ai/free-layout-editor';
 import { Button, Badge } from '@douyinfe/semi-ui';
 import { IconPlay } from '@douyinfe/semi-icons';
 
@@ -19,7 +19,7 @@ export function TestRunButton(props: { disabled: boolean }) {
   const clientContext = useClientContext();
   const panelManager = usePanelManager();
   const updateValidateData = useCallback(() => {
-    const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
+    const allForms = clientContext.document.getAllNodes().map((node) => node.form);
     const count = allForms.filter((form) => form?.state.invalid).length;
     setErrorCount(count);
   }, [clientContext]);
@@ -28,7 +28,7 @@ export function TestRunButton(props: { disabled: boolean }) {
    * Validate all node and Save
    */
   const onTestRun = useCallback(async () => {
-    const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
+    const allForms = clientContext.document.getAllNodes().map((node) => node.form);
     await Promise.all(allForms.map(async (form) => form?.validate()));
     console.log('>>>>> save data: ', clientContext.document.toJSON());
     panelManager.open(testRunPanelFactory.key, 'right');
@@ -39,7 +39,7 @@ export function TestRunButton(props: { disabled: boolean }) {
    */
   useEffect(() => {
     const listenSingleNodeValidate = (node: FlowNodeEntity) => {
-      const form = getNodeForm(node);
+      const { form } = node;
       if (form) {
         const formValidateDispose = form.onValidate(() => updateValidateData());
         node.onDispose(() => formValidateDispose.dispose());

+ 4 - 4
apps/demo-free-layout/src/components/tools/save.tsx

@@ -5,7 +5,7 @@
 
 import { useState, useEffect, useCallback } from 'react';
 
-import { useClientContext, getNodeForm, FlowNodeEntity } from '@flowgram.ai/free-layout-editor';
+import { useClientContext, FlowNodeEntity } from '@flowgram.ai/free-layout-editor';
 import { Button, Badge } from '@douyinfe/semi-ui';
 
 export function Save(props: { disabled: boolean }) {
@@ -13,7 +13,7 @@ export function Save(props: { disabled: boolean }) {
   const clientContext = useClientContext();
 
   const updateValidateData = useCallback(() => {
-    const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
+    const allForms = clientContext.document.getAllNodes().map((node) => node.form);
     const count = allForms.filter((form) => form?.state.invalid).length;
     setErrorCount(count);
   }, [clientContext]);
@@ -22,7 +22,7 @@ export function Save(props: { disabled: boolean }) {
    * Validate all node and Save
    */
   const onSave = useCallback(async () => {
-    const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
+    const allForms = clientContext.document.getAllNodes().map((node) => node.form);
     await Promise.all(allForms.map(async (form) => form?.validate()));
     console.log('>>>>> save data: ', clientContext.document.toJSON());
   }, [clientContext]);
@@ -32,7 +32,7 @@ export function Save(props: { disabled: boolean }) {
    */
   useEffect(() => {
     const listenSingleNodeValidate = (node: FlowNodeEntity) => {
-      const form = getNodeForm(node);
+      const { form } = node;
       if (form) {
         const formValidateDispose = form.onValidate(() => updateValidateData());
         node.onDispose(() => formValidateDispose.dispose());

+ 1 - 2
apps/demo-free-layout/src/plugins/runtime-plugin/runtime-service/index.ts

@@ -19,7 +19,6 @@ import {
   WorkflowNodeEntity,
   WorkflowNodeLinesData,
   Emitter,
-  getNodeForm,
 } from '@flowgram.ai/free-layout-editor';
 
 import { WorkflowRuntimeClient } from '../client';
@@ -139,7 +138,7 @@ export class WorkflowRuntimeService {
   }
 
   private async validateForm(): Promise<boolean> {
-    const allForms = this.document.getAllNodes().map((node) => getNodeForm(node));
+    const allForms = this.document.getAllNodes().map((node) => node.form);
     const formValidations = await Promise.all(allForms.map(async (form) => form?.validate()));
     const validations = formValidations.filter((validation) => validation !== undefined);
     const isValid = validations.every((validation) => validation);

+ 5 - 3
apps/docs/src/en/guide/advanced/fixed-layout/node.mdx

@@ -165,13 +165,15 @@ function BaseNode({ node }) {
 
 ## Updating Node Data
 
-- Get node data through [useNodeRender](/api/hooks/use-node-render.html) or [getNodeForm](/api/utils/get-node-form.html)
+- Get node data through [useNodeRender](/api/hooks/use-node-render.html) or [node.form](https://flowgram.ai/auto-docs/editor/interfaces/NodeFormProps.html)
 
 ```tsx pure
 function BaseNode() {
-  const { data, updateData } = useNodeRender();
+  const { data, updateData, form } = useNodeRender();
   // Corresponds to node's data
-  console.log(data)
+  // 1. data or form.values: Corresponds to node's data
+  // 2. form.setValueIn('title', 'xxxx'): Update data.title
+  // 3. form.getValueIn('title'): Get data.title
 
   function onChange(e) {
     updateData({

+ 5 - 3
apps/docs/src/en/guide/advanced/free-layout/node.mdx

@@ -119,13 +119,15 @@ function BaseNode({ node }) {
 
 ## Update Node Data
 
-- Get node's data through [useNodeRender](/api/hooks/use-node-render.html) or [getNodeForm](/api/utils/get-node-form.html)
+- Get node's data through [useNodeRender](/api/hooks/use-node-render.html) or [node.form](https://flowgram.ai/auto-docs/editor/interfaces/NodeFormProps.html)
 
 ```tsx pure
 function BaseNode() {
-  const { data, updateData } = useNodeRender();
+  const { data, updateData, form } = useNodeRender();
   // Corresponds to node's data
-  console.log(data)
+  // 1. data or form.values: Corresponds to node's data
+  // 2. form.setValueIn('title', 'xxxx'): Update data.title
+  // 3. form.getValueIn('title'): Get data.title
 
   function onChange(e) {
     updateData({

+ 5 - 4
apps/docs/src/zh/guide/advanced/fixed-layout/node.mdx

@@ -166,13 +166,14 @@ function BaseNode({ node }) {
 
 ## 更新节点 data 数据
 
-- 通过 [useNodeRender](/api/hooks/use-node-render.html) 或 [getNodeForm](/api/utils/get-node-form.html) 获取节点的 data 数据
+- 通过 [useNodeRender](/api/hooks/use-node-render.html) 或 [node.form](https://flowgram.ai/auto-docs/editor/interfaces/NodeFormProps.html) 获取节点的 data 数据
 
 ```tsx pure
 function BaseNode() {
-  const { data, updateData } = useNodeRender();
-  // 对应节点的 data 数据
-  console.log(data)
+  const { data, updateData, form } = useNodeRender();
+  // 1. data, form.values 对应节点的 data 数据
+  // 2. form.setValueIn('title', 'xxxx') 修改 data.title
+  // 3. form.getValueIn('title') 获取 data.title
 
   function onChange(e) {
     updateData({

+ 5 - 3
apps/docs/src/zh/guide/advanced/free-layout/node.mdx

@@ -120,13 +120,15 @@ function BaseNode({ node }) {
 
 ## 更新节点 data 数据
 
-- 通过 [useNodeRender](/api/hooks/use-node-render.html) 或 [getNodeForm](/api/utils/get-node-form.html) 获取节点的 data 数据
+- 通过 [useNodeRender](/api/hooks/use-node-render.html) 或 [node.form](https://flowgram.ai/auto-docs/editor/interfaces/NodeFormProps.html) 获取节点的 data 数据
 
 ```tsx pure
 function BaseNode() {
-  const { data, updateData } = useNodeRender();
+  const { data, updateData, form } = useNodeRender();
   // 对应节点的 data 数据
-  console.log(data)
+  // 1. data, form.values 对应节点的 data 数据
+  // 2. form.setValueIn('title', 'xxxx') 修改 data.title
+  // 3. form.getValueIn('title') 获取 data.title
 
   function onChange(e) {
     updateData({

+ 16 - 0
packages/client/editor/src/preset/editor-default-preset.ts

@@ -8,6 +8,7 @@ import { FlowRendererContainerModule, FlowRendererRegistry } from '@flowgram.ai/
 import { createReduxDevToolPlugin } from '@flowgram.ai/redux-devtool-plugin';
 import { createNodeVariablePlugin } from '@flowgram.ai/node-variable-plugin';
 import { createNodeCorePlugin } from '@flowgram.ai/node-core-plugin';
+import { getNodeForm, NodeFormProps } from '@flowgram.ai/node';
 import { createMaterialsPlugin } from '@flowgram.ai/materials-plugin';
 import { createI18nPlugin } from '@flowgram.ai/i18n-plugin';
 import { createHistoryNodePlugin } from '@flowgram.ai/history-node-plugin';
@@ -89,6 +90,21 @@ export function createDefaultPreset<CTX extends EditorPluginContext = EditorPlug
           // }
           // TODO 这个会触发组件注册,后续要废弃这个,通过 materials 插件来做
           ctx.get<FlowRendererRegistry>(FlowRendererRegistry).init();
+          /**
+           * Define node.form
+           */
+          ctx.document.onNodeCreate(({ node }) => {
+            if (opts.nodeEngine && opts.nodeEngine.enable !== false) {
+              let cache: NodeFormProps<any> | undefined;
+              Object.defineProperty(node, 'form', {
+                get: () => {
+                  if (cache) return cache;
+                  cache = getNodeForm(node);
+                  return cache;
+                },
+              });
+            }
+          });
         },
         onReady(ctx) {
           if (opts.initialData) {

+ 7 - 0
packages/client/editor/src/preset/editor-props.ts

@@ -7,6 +7,7 @@ import { VariablePluginOptions } from '@flowgram.ai/variable-plugin';
 import { ReduxDevToolPluginOptions } from '@flowgram.ai/redux-devtool-plugin';
 import { PlaygroundReactProps, SelectionService } from '@flowgram.ai/playground-react';
 import { NodeCorePluginOptions } from '@flowgram.ai/node-core-plugin';
+import { type NodeFormProps } from '@flowgram.ai/node';
 import { MaterialsPluginOptions } from '@flowgram.ai/materials-plugin';
 import { I18nPluginOptions } from '@flowgram.ai/i18n-plugin';
 import { HistoryPluginOptions } from '@flowgram.ai/history';
@@ -21,6 +22,12 @@ import {
 } from '@flowgram.ai/document';
 import { PluginContext } from '@flowgram.ai/core';
 
+declare module '@flowgram.ai/document' {
+  interface FlowNodeEntity {
+    form: NodeFormProps<any> | undefined;
+  }
+}
+
 export interface EditorPluginContext extends PluginContext {
   document: FlowDocument;
   selection: SelectionService;

+ 1 - 2
packages/client/free-layout-editor/__tests__/history.test.ts

@@ -6,7 +6,6 @@
 import { describe, it, expect } from 'vitest';
 import { WorkflowDocument } from '@flowgram.ai/free-layout-core';
 import { HistoryService } from '@flowgram.ai/free-history-plugin';
-import { getNodeForm } from '@flowgram.ai/editor';
 
 import { mockJSON } from '../__mocks__/flow.mocks';
 import { createEditor } from './create-editor';
@@ -103,7 +102,7 @@ describe('free-layout history', () => {
     const flowDocument = editor.get(WorkflowDocument);
     flowDocument.fromJSON(mockJSON);
     const node = flowDocument.getNode('start_0');
-    const form = getNodeForm(node);
+    const form = node.form;
     form.setValueIn('title', 'title changed');
     expect(historyEvent).toEqual({
       type: 'changeFormValues',

+ 1 - 2
packages/materials/form-antd-materials/src/effects/provide-batch-input/index.ts

@@ -8,7 +8,6 @@ import {
   EffectOptions,
   FlowNodeRegistry,
   createEffectFromVariableProvider,
-  getNodeForm,
 } from '@flowgram.ai/editor';
 
 import { IFlowRefValue } from '../../typings';
@@ -19,7 +18,7 @@ export const provideBatchInputEffect: EffectOptions[] = createEffectFromVariable
     ASTFactory.createVariableDeclaration({
       key: `${ctx.node.id}_locals`,
       meta: {
-        title: getNodeForm(ctx.node)?.getValueIn('title'),
+        title: ctx.node.form?.getValueIn('title'),
         icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
       },
       type: ASTFactory.createObject({

+ 1 - 2
packages/materials/form-antd-materials/src/effects/provide-batch-outputs/index.ts

@@ -8,7 +8,6 @@ import {
   EffectOptions,
   FlowNodeRegistry,
   createEffectFromVariableProvider,
-  getNodeForm,
 } from '@flowgram.ai/editor';
 
 import { IFlowRefValue } from '../../typings';
@@ -18,7 +17,7 @@ export const provideBatchOutputsEffect: EffectOptions[] = createEffectFromVariab
     ASTFactory.createVariableDeclaration({
       key: `${ctx.node.id}`,
       meta: {
-        title: getNodeForm(ctx.node)?.getValueIn('title'),
+        title: ctx.node.form?.getValueIn('title'),
         icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
       },
       type: ASTFactory.createObject({

+ 1 - 2
packages/materials/form-antd-materials/src/effects/provide-json-schema-outputs/index.ts

@@ -8,7 +8,6 @@ import {
   EffectOptions,
   FlowNodeRegistry,
   createEffectFromVariableProvider,
-  getNodeForm,
 } from '@flowgram.ai/editor';
 
 import { JsonSchemaUtils } from '../../utils';
@@ -19,7 +18,7 @@ export const provideJsonSchemaOutputs: EffectOptions[] = createEffectFromVariabl
     ASTFactory.createVariableDeclaration({
       key: `${ctx.node.id}`,
       meta: {
-        title: getNodeForm(ctx.node)?.getValueIn('title') || ctx.node.id,
+        title: ctx.node.form?.getValueIn('title') || ctx.node.id,
         icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
       },
       type: JsonSchemaUtils.schemaToAST(value),

+ 1 - 2
packages/materials/form-antd-materials/src/form-plugins/batch-outputs-plugin/index.ts

@@ -8,7 +8,6 @@ import {
   createEffectFromVariableProvider,
   defineFormPluginCreator,
   FlowNodeRegistry,
-  getNodeForm,
   getNodePrivateScope,
   getNodeScope,
   ScopeChainTransformService,
@@ -24,7 +23,7 @@ export const provideBatchOutputsEffect: EffectOptions[] = createEffectFromVariab
     ASTFactory.createVariableDeclaration({
       key: `${ctx.node.id}`,
       meta: {
-        title: getNodeForm(ctx.node)?.getValueIn('title'),
+        title: ctx.node.form?.getValueIn('title'),
         icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
       },
       type: ASTFactory.createObject({

+ 1 - 2
packages/materials/form-materials/src/effects/provide-batch-input/index.ts

@@ -8,7 +8,6 @@ import {
   EffectOptions,
   FlowNodeRegistry,
   createEffectFromVariableProvider,
-  getNodeForm,
 } from '@flowgram.ai/editor';
 
 import { IFlowRefValue } from '@/shared';
@@ -19,7 +18,7 @@ export const provideBatchInputEffect: EffectOptions[] = createEffectFromVariable
     ASTFactory.createVariableDeclaration({
       key: `${ctx.node.id}_locals`,
       meta: {
-        title: getNodeForm(ctx.node)?.getValueIn('title'),
+        title: ctx.node.form?.getValueIn('title'),
         icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
       },
       type: ASTFactory.createObject({

+ 1 - 2
packages/materials/form-materials/src/effects/provide-json-schema-outputs/index.ts

@@ -9,7 +9,6 @@ import {
   EffectOptions,
   FlowNodeRegistry,
   createEffectFromVariableProvider,
-  getNodeForm,
 } from '@flowgram.ai/editor';
 
 export const provideJsonSchemaOutputs: EffectOptions[] = createEffectFromVariableProvider({
@@ -17,7 +16,7 @@ export const provideJsonSchemaOutputs: EffectOptions[] = createEffectFromVariabl
     ASTFactory.createVariableDeclaration({
       key: `${ctx.node.id}`,
       meta: {
-        title: getNodeForm(ctx.node)?.getValueIn('title') || ctx.node.id,
+        title: ctx.node.form?.getValueIn('title') || ctx.node.id,
         icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
       },
       type: JsonSchemaUtils.schemaToAST(value),

+ 1 - 2
packages/materials/form-materials/src/form-plugins/batch-outputs-plugin/index.ts

@@ -10,7 +10,6 @@ import {
   createEffectFromVariableProvider,
   defineFormPluginCreator,
   FlowNodeRegistry,
-  getNodeForm,
   getNodePrivateScope,
   getNodeScope,
   ScopeChainTransformService,
@@ -26,7 +25,7 @@ export const provideBatchOutputsEffect: EffectOptions[] = createEffectFromVariab
     ASTFactory.createVariableDeclaration({
       key: `${ctx.node.id}`,
       meta: {
-        title: getNodeForm(ctx.node)?.getValueIn('title'),
+        title: ctx.node.form?.getValueIn('title'),
         icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
       },
       type: ASTFactory.createObject({

+ 1 - 2
packages/materials/form-materials/src/form-plugins/infer-assign-plugin/index.ts

@@ -10,7 +10,6 @@ import {
   createEffectFromVariableProvider,
   defineFormPluginCreator,
   FlowNodeRegistry,
-  getNodeForm,
   getNodeScope,
 } from '@flowgram.ai/editor';
 
@@ -51,7 +50,7 @@ export const createInferAssignPlugin = defineFormPluginCreator<InputConfig>({
             ASTFactory.createVariableDeclaration({
               key: `${ctx.node.id}`,
               meta: {
-                title: getNodeForm(ctx.node)?.getValueIn('title'),
+                title: ctx.node.form?.getValueIn('title'),
                 icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
               },
               type: ASTFactory.createObject({

+ 2 - 1
packages/node-engine/node/src/get-node-form.tsx

@@ -68,7 +68,8 @@ export interface NodeFormProps<TValues> {
 }
 
 /**
- * Only support FormModelV2
+ * Use `node.form` instead
+ * @deprecated
  * @param node
  */
 export function getNodeForm<TValues = FieldValue>(