Explorar o código

refactor(container): allow user to customize render content component

liuyangxing hai 10 meses
pai
achega
088368d48c

+ 3 - 1
apps/demo-free-layout/src/components/base-node/index.tsx

@@ -38,7 +38,9 @@ export const BaseNode = ({ node }: { node: FlowNodeEntity }) => {
             outline: form?.state.invalid ? '1px solid red' : 'none',
             outline: form?.state.invalid ? '1px solid red' : 'none',
           }}
           }}
         >
         >
-          <NodeRenderContext.Provider value={{}}>{form?.render()}</NodeRenderContext.Provider>
+          <NodeRenderContext.Provider value={nodeRender}>
+            {form?.render()}
+          </NodeRenderContext.Provider>
         </BaseNodeStyle>
         </BaseNodeStyle>
       </WorkflowNodeRenderer>
       </WorkflowNodeRenderer>
     </ConfigProvider>
     </ConfigProvider>

+ 14 - 0
apps/demo-free-layout/src/components/container-content/index.tsx

@@ -0,0 +1,14 @@
+import { useNodeRender } from '@flowgram.ai/free-layout-editor';
+import { ContainerNodeForm } from '@flowgram.ai/free-container-plugin';
+
+import { NodeRenderContext } from '../../context';
+
+export const ContainerNodeContent = () => {
+  const nodeRender = useNodeRender();
+
+  return (
+    <NodeRenderContext.Provider value={nodeRender}>
+      <ContainerNodeForm />;
+    </NodeRenderContext.Provider>
+  );
+};

+ 1 - 0
apps/demo-free-layout/src/components/index.ts

@@ -1,3 +1,4 @@
 export * from './base-node';
 export * from './base-node';
 export * from './line-add-button';
 export * from './line-add-button';
 export * from './node-panel';
 export * from './node-panel';
+export * from './container-content';

+ 4 - 2
apps/demo-free-layout/src/context/node-render-context.ts

@@ -1,6 +1,8 @@
 import React from 'react';
 import React from 'react';
 
 
-interface INodeRenderContext {}
+import type { NodeRenderReturnType } from '@flowgram.ai/free-layout-editor';
+
+interface INodeRenderContext extends NodeRenderReturnType {}
 
 
 /** 业务自定义节点上下文 */
 /** 业务自定义节点上下文 */
-export const NodeRenderContext = React.createContext<INodeRenderContext>({});
+export const NodeRenderContext = React.createContext<INodeRenderContext>({} as INodeRenderContext);

+ 3 - 2
apps/demo-free-layout/src/form-components/form-content/index.tsx

@@ -1,7 +1,8 @@
 import React from 'react';
 import React from 'react';
 
 
-import { FlowNodeRegistry, useNodeRender } from '@flowgram.ai/free-layout-editor';
+import { FlowNodeRegistry } from '@flowgram.ai/free-layout-editor';
 
 
+import { useNodeRenderContext } from '../../hooks';
 import { FormTitleDescription, FormWrapper } from './styles';
 import { FormTitleDescription, FormWrapper } from './styles';
 
 
 /**
 /**
@@ -9,7 +10,7 @@ import { FormTitleDescription, FormWrapper } from './styles';
  * @constructor
  * @constructor
  */
  */
 export function FormContent(props: { children?: React.ReactNode }) {
 export function FormContent(props: { children?: React.ReactNode }) {
-  const { expanded, node } = useNodeRender();
+  const { expanded, node } = useNodeRenderContext();
   const registry = node.getNodeRegistry<FlowNodeRegistry>();
   const registry = node.getNodeRegistry<FlowNodeRegistry>();
   return (
   return (
     <FormWrapper>
     <FormWrapper>

+ 3 - 3
apps/demo-free-layout/src/form-components/form-header/index.tsx

@@ -5,7 +5,6 @@ import {
   Field,
   Field,
   FieldRenderProps,
   FieldRenderProps,
   useClientContext,
   useClientContext,
-  useNodeRender,
   useService,
   useService,
 } from '@flowgram.ai/free-layout-editor';
 } from '@flowgram.ai/free-layout-editor';
 import { NodeIntoContainerService } from '@flowgram.ai/free-container-plugin';
 import { NodeIntoContainerService } from '@flowgram.ai/free-container-plugin';
@@ -14,6 +13,7 @@ import { IconMore } from '@douyinfe/semi-icons';
 
 
 import { Feedback } from '../feedback';
 import { Feedback } from '../feedback';
 import { FlowNodeRegistry } from '../../typings';
 import { FlowNodeRegistry } from '../../typings';
+import { useNodeRenderContext } from '../../hooks';
 import { getIcon } from './utils';
 import { getIcon } from './utils';
 import { Header, Operators, Title } from './styles';
 import { Header, Operators, Title } from './styles';
 
 
@@ -21,7 +21,7 @@ const { Text } = Typography;
 
 
 function DropdownButton() {
 function DropdownButton() {
   const [key, setKey] = useState(0);
   const [key, setKey] = useState(0);
-  const { node, deleteNode } = useNodeRender();
+  const { node, deleteNode } = useNodeRenderContext();
   const clientContext = useClientContext();
   const clientContext = useClientContext();
   const registry = node.getNodeRegistry<FlowNodeRegistry>();
   const registry = node.getNodeRegistry<FlowNodeRegistry>();
   const nodeIntoContainerService = useService<NodeIntoContainerService>(NodeIntoContainerService);
   const nodeIntoContainerService = useService<NodeIntoContainerService>(NodeIntoContainerService);
@@ -76,7 +76,7 @@ function DropdownButton() {
 }
 }
 
 
 export function FormHeader() {
 export function FormHeader() {
-  const { node, readonly } = useNodeRender();
+  const { node, readonly } = useNodeRenderContext();
 
 
   return (
   return (
     <Header>
     <Header>

+ 3 - 2
apps/demo-free-layout/src/form-components/form-inputs/index.tsx

@@ -1,12 +1,13 @@
-import { Field, useNodeRender } from '@flowgram.ai/free-layout-editor';
+import { Field } from '@flowgram.ai/free-layout-editor';
 
 
 import { FxExpression } from '../fx-expression';
 import { FxExpression } from '../fx-expression';
 import { FormItem } from '../form-item';
 import { FormItem } from '../form-item';
 import { Feedback } from '../feedback';
 import { Feedback } from '../feedback';
 import { JsonSchema } from '../../typings';
 import { JsonSchema } from '../../typings';
+import { useNodeRenderContext } from '../../hooks';
 
 
 export function FormInputs() {
 export function FormInputs() {
-  const { readonly } = useNodeRender();
+  const { readonly } = useNodeRenderContext();
   return (
   return (
     <Field<JsonSchema> name="inputs">
     <Field<JsonSchema> name="inputs">
       {({ field: inputsField }) => {
       {({ field: inputsField }) => {

+ 2 - 2
apps/demo-free-layout/src/form-components/properties-edit/index.tsx

@@ -1,10 +1,10 @@
 import React, { useState } from 'react';
 import React, { useState } from 'react';
 
 
-import { useNodeRender } from '@flowgram.ai/free-layout-editor';
 import { Button } from '@douyinfe/semi-ui';
 import { Button } from '@douyinfe/semi-ui';
 import { IconPlus } from '@douyinfe/semi-icons';
 import { IconPlus } from '@douyinfe/semi-icons';
 
 
 import { JsonSchema } from '../../typings';
 import { JsonSchema } from '../../typings';
+import { useNodeRenderContext } from '../../hooks';
 import { PropertyEdit } from './property-edit';
 import { PropertyEdit } from './property-edit';
 
 
 export interface PropertiesEditProps {
 export interface PropertiesEditProps {
@@ -15,7 +15,7 @@ export interface PropertiesEditProps {
 
 
 export const PropertiesEdit: React.FC<PropertiesEditProps> = (props) => {
 export const PropertiesEdit: React.FC<PropertiesEditProps> = (props) => {
   const value = (props.value || {}) as Record<string, JsonSchema>;
   const value = (props.value || {}) as Record<string, JsonSchema>;
-  const { readonly } = useNodeRender();
+  const { readonly } = useNodeRenderContext();
   const [newProperty, updateNewPropertyFromCache] = useState<{ key: string; value: JsonSchema }>({
   const [newProperty, updateNewPropertyFromCache] = useState<{ key: string; value: JsonSchema }>({
     key: '',
     key: '',
     value: { type: 'string' },
     value: { type: 'string' },

+ 1 - 0
apps/demo-free-layout/src/hooks/index.ts

@@ -1 +1,2 @@
 export { useEditorProps } from './use-editor-props';
 export { useEditorProps } from './use-editor-props';
+export { useNodeRenderContext } from './use-node-render-context';

+ 4 - 1
apps/demo-free-layout/src/hooks/use-editor-props.tsx

@@ -17,6 +17,7 @@ import { createSyncVariablePlugin } from '../plugins';
 import { defaultFormMeta } from '../nodes/default-form-meta';
 import { defaultFormMeta } from '../nodes/default-form-meta';
 import { SelectorBoxPopover } from '../components/selector-box-popover';
 import { SelectorBoxPopover } from '../components/selector-box-popover';
 import { BaseNode, LineAddButton, NodePanel } from '../components';
 import { BaseNode, LineAddButton, NodePanel } from '../components';
+import { ContainerNodeContent } from '../components';
 
 
 export function useEditorProps(
 export function useEditorProps(
   initialData: FlowDocumentJSON,
   initialData: FlowDocumentJSON,
@@ -200,7 +201,9 @@ export function useEditorProps(
         createFreeNodePanelPlugin({
         createFreeNodePanelPlugin({
           renderer: NodePanel,
           renderer: NodePanel,
         }),
         }),
-        createContainerNodePlugin({}),
+        createContainerNodePlugin({
+          renderContent: <ContainerNodeContent />,
+        }),
       ],
       ],
     }),
     }),
     []
     []

+ 5 - 0
apps/demo-free-layout/src/hooks/use-node-render-context.ts

@@ -0,0 +1,5 @@
+import { useContext } from 'react';
+
+import { NodeRenderContext } from '../context';
+
+export const useNodeRenderContext = () => useContext(NodeRenderContext);

+ 3 - 2
apps/demo-free-layout/src/nodes/condition/condition-inputs/index.tsx

@@ -1,9 +1,10 @@
 import { nanoid } from 'nanoid';
 import { nanoid } from 'nanoid';
-import { Field, FieldArray, useNodeRender } from '@flowgram.ai/free-layout-editor';
+import { Field, FieldArray } from '@flowgram.ai/free-layout-editor';
 import { Button } from '@douyinfe/semi-ui';
 import { Button } from '@douyinfe/semi-ui';
 import { IconPlus, IconCrossCircleStroked } from '@douyinfe/semi-icons';
 import { IconPlus, IconCrossCircleStroked } from '@douyinfe/semi-icons';
 
 
 import { FlowLiteralValueSchema, FlowRefValueSchema } from '../../../typings';
 import { FlowLiteralValueSchema, FlowRefValueSchema } from '../../../typings';
+import { useNodeRenderContext } from '../../../hooks';
 import { FxExpression } from '../../../form-components/fx-expression';
 import { FxExpression } from '../../../form-components/fx-expression';
 import { FormItem } from '../../../form-components';
 import { FormItem } from '../../../form-components';
 import { Feedback } from '../../../form-components';
 import { Feedback } from '../../../form-components';
@@ -15,7 +16,7 @@ interface ConditionValue {
 }
 }
 
 
 export function ConditionInputs() {
 export function ConditionInputs() {
-  const { readonly } = useNodeRender();
+  const { readonly } = useNodeRenderContext();
   return (
   return (
     <FieldArray name="inputsValues.conditions">
     <FieldArray name="inputsValues.conditions">
       {({ field }) => (
       {({ field }) => (

+ 5 - 0
packages/canvas-engine/free-layout-core/src/hooks/use-node-render-context.ts

@@ -0,0 +1,5 @@
+import React from 'react';
+
+import { NodeRenderReturnType } from './typings';
+
+export const NodeRenderContext = React.createContext<NodeRenderReturnType>({} as any);

+ 1 - 0
packages/plugins/free-container-plugin/src/container-node-render/index.ts

@@ -1,3 +1,4 @@
 export { ContainerNodeRenderKey } from './constant';
 export { ContainerNodeRenderKey } from './constant';
 export { ContainerNodeRender } from './render';
 export { ContainerNodeRender } from './render';
 export type { ContainerNodeMetaRenderProps, ContainerNodeRenderProps } from './type';
 export type { ContainerNodeMetaRenderProps, ContainerNodeRenderProps } from './type';
+export * from './components';

+ 2 - 5
packages/plugins/free-container-plugin/src/container-node-render/render.tsx

@@ -7,16 +7,13 @@ import {
   ContainerNodePorts,
   ContainerNodePorts,
   ContainerNodeBorder,
   ContainerNodeBorder,
   ContainerNodeContainer,
   ContainerNodeContainer,
-  ContainerNodeForm,
 } from './components';
 } from './components';
 
 
-export const ContainerNodeRender: FC<ContainerNodeRenderProps> = () => (
+export const ContainerNodeRender: FC<ContainerNodeRenderProps> = ({ content }) => (
   <ContainerNodeContainer>
   <ContainerNodeContainer>
     <ContainerNodeBackground />
     <ContainerNodeBackground />
     <ContainerNodeBorder />
     <ContainerNodeBorder />
-    <ContainerNodeHeader>
-      <ContainerNodeForm />
-    </ContainerNodeHeader>
+    <ContainerNodeHeader>{content}</ContainerNodeHeader>
     <ContainerNodePorts />
     <ContainerNodePorts />
   </ContainerNodeContainer>
   </ContainerNodeContainer>
 );
 );

+ 3 - 0
packages/plugins/free-container-plugin/src/container-node-render/type.ts

@@ -1,3 +1,5 @@
+import { ReactNode } from 'react';
+
 import type { WorkflowNodeEntity } from '@flowgram.ai/free-layout-core';
 import type { WorkflowNodeEntity } from '@flowgram.ai/free-layout-core';
 
 
 export interface ContainerNodeMetaRenderProps {
 export interface ContainerNodeMetaRenderProps {
@@ -13,4 +15,5 @@ export interface ContainerNodeMetaRenderProps {
 
 
 export interface ContainerNodeRenderProps {
 export interface ContainerNodeRenderProps {
   node: WorkflowNodeEntity;
   node: WorkflowNodeEntity;
+  content?: ReactNode;
 }
 }

+ 11 - 3
packages/plugins/free-container-plugin/src/plugin.ts → packages/plugins/free-container-plugin/src/plugin.tsx

@@ -1,19 +1,27 @@
+import React from 'react';
+
 import { FlowRendererRegistry } from '@flowgram.ai/renderer';
 import { FlowRendererRegistry } from '@flowgram.ai/renderer';
 import { definePluginCreator } from '@flowgram.ai/core';
 import { definePluginCreator } from '@flowgram.ai/core';
 
 
 import type { WorkflowContainerPluginOptions } from './type';
 import type { WorkflowContainerPluginOptions } from './type';
 import { NodeIntoContainerService } from './node-into-container';
 import { NodeIntoContainerService } from './node-into-container';
-import { ContainerNodeRenderKey, ContainerNodeRender } from './container-node-render';
+import {
+  ContainerNodeRenderKey,
+  ContainerNodeRender,
+  ContainerNodeRenderProps,
+} from './container-node-render';
 
 
 export const createContainerNodePlugin = definePluginCreator<WorkflowContainerPluginOptions>({
 export const createContainerNodePlugin = definePluginCreator<WorkflowContainerPluginOptions>({
   onBind: ({ bind }) => {
   onBind: ({ bind }) => {
     bind(NodeIntoContainerService).toSelf().inSingletonScope();
     bind(NodeIntoContainerService).toSelf().inSingletonScope();
   },
   },
-  onInit(ctx) {
+  onInit(ctx, options) {
     ctx.get(NodeIntoContainerService).init();
     ctx.get(NodeIntoContainerService).init();
 
 
     const registry = ctx.get<FlowRendererRegistry>(FlowRendererRegistry);
     const registry = ctx.get<FlowRendererRegistry>(FlowRendererRegistry);
-    registry.registerReactComponent(ContainerNodeRenderKey, ContainerNodeRender);
+    registry.registerReactComponent(ContainerNodeRenderKey, (props: ContainerNodeRenderProps) => (
+      <ContainerNodeRender {...props} content={options.renderContent} />
+    ));
   },
   },
   onReady(ctx, options) {
   onReady(ctx, options) {
     if (options.disableNodeIntoContainer !== true) {
     if (options.disableNodeIntoContainer !== true) {

+ 3 - 0
packages/plugins/free-container-plugin/src/type.ts

@@ -1,3 +1,6 @@
+import type { ReactNode } from 'react';
+
 export interface WorkflowContainerPluginOptions {
 export interface WorkflowContainerPluginOptions {
   disableNodeIntoContainer?: boolean;
   disableNodeIntoContainer?: boolean;
+  renderContent?: ReactNode;
 }
 }