Browse Source

feat(free-demo): create loop node

liuyangxing 9 months ago
parent
commit
e7656d4cec

+ 1 - 0
apps/demo-free-layout/package.json

@@ -35,6 +35,7 @@
     "@flowgram.ai/free-lines-plugin": "workspace:*",
     "@flowgram.ai/free-node-panel-plugin": "workspace:*",
     "@flowgram.ai/minimap-plugin": "workspace:*",
+    "@flowgram.ai/free-container-plugin": "workspace:*",
     "lodash-es": "^4.17.21",
     "nanoid": "^4.0.2",
     "react": "^18",

BIN
apps/demo-free-layout/src/assets/icon-loop.jpg


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

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

+ 0 - 2
apps/demo-free-layout/src/components/base-node/styles.tsx

@@ -6,14 +6,12 @@ export const BaseNodeStyle = styled.div`
   background-color: #fff;
   border: 1px solid rgba(6, 7, 9, 0.15);
   border-radius: 8px;
-  box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.04), 0 4px 12px 0 rgba(0, 0, 0, 0.02);
   display: flex;
   flex-direction: column;
   justify-content: center;
   position: relative;
   width: 360px;
 
-  transition: all 0.3s ease;
   &.selected {
     border: 1px solid var(--coz-stroke-hglt, #4e40e5);
   }

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

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

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

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

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

@@ -1,25 +1,22 @@
-import { useContext } from 'react';
-
 import {
   Command,
   Field,
   FieldRenderProps,
   useClientContext,
+  useNodeRender,
 } from '@flowgram.ai/free-layout-editor';
-import { IconButton, Dropdown, Typography, Button } from '@douyinfe/semi-ui';
-import { IconSmallTriangleDown, IconSmallTriangleLeft } from '@douyinfe/semi-icons';
+import { IconButton, Dropdown, Typography } from '@douyinfe/semi-ui';
 import { IconMore } from '@douyinfe/semi-icons';
 
 import { Feedback } from '../feedback';
 import { FlowNodeRegistry } from '../../typings';
-import { NodeRenderContext } from '../../context';
 import { getIcon } from './utils';
 import { Header, Operators, Title } from './styles';
 
 const { Text } = Typography;
 
 function DropdownContent() {
-  const { node, deleteNode } = useContext(NodeRenderContext);
+  const { node, deleteNode } = useNodeRender();
   const clientContext = useClientContext();
   const registry = node.getNodeRegistry<FlowNodeRegistry>();
   const handleCopy = () => {
@@ -41,7 +38,7 @@ function DropdownContent() {
 }
 
 export function FormHeader() {
-  const { node, expanded, toggleExpand, readonly } = useContext(NodeRenderContext);
+  const { node, expanded, toggleExpand, readonly } = useNodeRender();
 
   return (
     <Header>
@@ -56,13 +53,6 @@ export function FormHeader() {
           )}
         </Field>
       </Title>
-      <Button
-        type="primary"
-        icon={expanded ? <IconSmallTriangleDown /> : <IconSmallTriangleLeft />}
-        size="small"
-        theme="borderless"
-        onClick={toggleExpand}
-      />
       {readonly ? undefined : (
         <Operators>
           <Dropdown trigger="hover" position="bottomRight" render={<DropdownContent />}>

+ 2 - 1
apps/demo-free-layout/src/form-components/form-header/styles.tsx

@@ -7,7 +7,8 @@ export const Header = styled.div`
   align-items: center;
   width: 100%;
   column-gap: 8px;
-  border-radius: 8px;
+  border-radius: 8px 8px 0 0;
+  cursor: move;
 
   background: linear-gradient(#f2f2ff 0%, rgba(0, 0, 0, 0.02) 100%);
   overflow: hidden;

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

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

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

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

+ 2 - 0
apps/demo-free-layout/src/hooks/use-editor-props.tsx

@@ -9,6 +9,7 @@ import {
 } from '@flowgram.ai/free-node-panel-plugin';
 import { createFreeLinesPlugin } from '@flowgram.ai/free-lines-plugin';
 import { FreeLayoutProps } from '@flowgram.ai/free-layout-editor';
+import { createContainerNodePlugin } from '@flowgram.ai/free-container-plugin';
 
 import { FlowNodeRegistry, FlowDocumentJSON } from '../typings';
 import { shortcuts } from '../shortcuts';
@@ -199,6 +200,7 @@ export function useEditorProps(
         createFreeNodePanelPlugin({
           renderer: NodePanel,
         }),
+        createContainerNodePlugin({}),
       ],
     }),
     []

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

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

+ 2 - 0
apps/demo-free-layout/src/nodes/index.ts

@@ -1,5 +1,6 @@
 import { FlowNodeRegistry } from '../typings';
 import { StartNodeRegistry } from './start';
+import { LoopNodeRegistry } from './loop';
 import { LLMNodeRegistry } from './llm';
 import { EndNodeRegistry } from './end';
 import { ConditionNodeRegistry } from './condition';
@@ -9,4 +10,5 @@ export const nodeRegistries: FlowNodeRegistry[] = [
   StartNodeRegistry,
   EndNodeRegistry,
   LLMNodeRegistry,
+  LoopNodeRegistry,
 ];

+ 54 - 0
apps/demo-free-layout/src/nodes/loop/index.ts

@@ -0,0 +1,54 @@
+import { nanoid } from 'nanoid';
+import { ContainerNodeRenderKey } from '@flowgram.ai/free-container-plugin';
+
+import { FlowNodeRegistry } from '../../typings';
+import iconLoop from '../../assets/icon-loop.jpg';
+
+let index = 0;
+export const LoopNodeRegistry: FlowNodeRegistry = {
+  type: 'loop',
+  info: {
+    icon: iconLoop,
+    description:
+      'Used to repeatedly execute a series of tasks by setting the number of iterations and logic.',
+  },
+  meta: {
+    renderKey: ContainerNodeRenderKey,
+    isContainer: true,
+    size: {
+      width: 560,
+      height: 400,
+    },
+    padding: () => ({
+      top: 205,
+      bottom: 50,
+      left: 100,
+      right: 100,
+    }),
+  },
+  onAdd() {
+    return {
+      id: `loop_${nanoid(5)}`,
+      type: 'loop',
+      data: {
+        title: `Loop_${++index}`,
+        inputsValues: {},
+        inputs: {
+          type: 'object',
+          required: ['loopTimes'],
+          properties: {
+            loopTimes: {
+              type: 'number',
+            },
+          },
+        },
+        outputs: {
+          type: 'object',
+          properties: {
+            result: { type: 'string' },
+          },
+        },
+      },
+    };
+  },
+};

+ 1 - 0
apps/docs/package.json

@@ -29,6 +29,7 @@
     "@flowgram.ai/free-auto-layout-plugin": "workspace:*",
     "@flowgram.ai/minimap-plugin": "workspace:*",
     "@flowgram.ai/free-stack-plugin": "workspace:*",
+    "@flowgram.ai/free-container-plugin": "workspace:*",
     "@flowgram.ai/free-snap-plugin": "workspace:*",
     "@flowgram.ai/free-node-panel-plugin": "workspace:*",
     "@flowgram.ai/free-lines-plugin": "workspace:*",

+ 85 - 0
common/config/rush/pnpm-lock.yaml

@@ -193,6 +193,9 @@ importers:
       '@douyinfe/semi-ui':
         specifier: ^2.72.3
         version: 2.72.3(acorn@8.14.0)(react-dom@18.3.1)(react@18.3.1)
+      '@flowgram.ai/free-container-plugin':
+        specifier: workspace:*
+        version: link:../../packages/plugins/free-container-plugin
       '@flowgram.ai/free-layout-editor':
         specifier: workspace:*
         version: link:../../packages/client/free-layout-editor
@@ -409,6 +412,9 @@ importers:
       '@flowgram.ai/free-auto-layout-plugin':
         specifier: workspace:*
         version: link:../../packages/plugins/free-auto-layout-plugin
+      '@flowgram.ai/free-container-plugin':
+        specifier: workspace:*
+        version: link:../../packages/plugins/free-container-plugin
       '@flowgram.ai/free-layout-editor':
         specifier: workspace:*
         version: link:../../packages/client/free-layout-editor
@@ -2083,6 +2089,85 @@ importers:
         specifier: ^0.34.6
         version: 0.34.6(jsdom@22.1.0)
 
+  ../../packages/plugins/free-container-plugin:
+    dependencies:
+      '@flowgram.ai/core':
+        specifier: workspace:*
+        version: link:../../canvas-engine/core
+      '@flowgram.ai/document':
+        specifier: workspace:*
+        version: link:../../canvas-engine/document
+      '@flowgram.ai/free-history-plugin':
+        specifier: workspace:*
+        version: link:../free-history-plugin
+      '@flowgram.ai/free-layout-core':
+        specifier: workspace:*
+        version: link:../../canvas-engine/free-layout-core
+      '@flowgram.ai/free-lines-plugin':
+        specifier: workspace:*
+        version: link:../free-lines-plugin
+      '@flowgram.ai/renderer':
+        specifier: workspace:*
+        version: link:../../canvas-engine/renderer
+      '@flowgram.ai/utils':
+        specifier: workspace:*
+        version: link:../../common/utils
+      inversify:
+        specifier: ^6.0.1
+        version: 6.2.0(reflect-metadata@0.2.2)
+      lodash:
+        specifier: ^4.17.21
+        version: 4.17.21
+      reflect-metadata:
+        specifier: ~0.2.2
+        version: 0.2.2
+    devDependencies:
+      '@flowgram.ai/eslint-config':
+        specifier: workspace:*
+        version: link:../../../config/eslint-config
+      '@flowgram.ai/ts-config':
+        specifier: workspace:*
+        version: link:../../../config/ts-config
+      '@types/bezier-js':
+        specifier: 4.1.3
+        version: 4.1.3
+      '@types/lodash':
+        specifier: ^4.14.137
+        version: 4.17.13
+      '@types/react':
+        specifier: ^18
+        version: 18.3.16
+      '@types/react-dom':
+        specifier: ^18
+        version: 18.3.5(@types/react@18.3.16)
+      '@types/styled-components':
+        specifier: ^5
+        version: 5.1.34
+      '@vitest/coverage-v8':
+        specifier: ^0.32.0
+        version: 0.32.4(vitest@0.34.6)
+      eslint:
+        specifier: ^8.54.0
+        version: 8.57.1
+      react:
+        specifier: ^18
+        version: 18.3.1
+      react-dom:
+        specifier: ^18
+        version: 18.3.1(react@18.3.1)
+      styled-components:
+        specifier: ^5
+        version: 5.3.11(@babel/core@7.26.0)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
+      tsup:
+        specifier: ^8.0.1
+        version: 8.3.5(typescript@5.0.4)
+      typescript:
+        specifier: ^5.0.4
+        version: 5.0.4
+      vitest:
+        specifier: ^0.34.6
+        version: 0.34.6(jsdom@22.1.0)
+
   ../../packages/plugins/free-history-plugin:
     dependencies:
       '@flowgram.ai/core':

+ 1 - 0
packages/plugins/free-container-plugin/src/container-node-render/components/form/style.ts

@@ -3,4 +3,5 @@ import styled from 'styled-components';
 export const ContainerNodeFormStyle = styled.div`
   background-color: #fff;
   border-radius: 8px 8px 0 0;
+  width: 100%;
 `;

+ 6 - 0
rush.json

@@ -607,6 +607,12 @@
       "versionPolicyName": "publishPolicy",
       "tags": ["level-1","team-flow"]
     },
+    {
+      "packageName": "@flowgram.ai/free-container-plugin",
+      "projectFolder": "packages/plugins/free-container-plugin",
+      "versionPolicyName": "publishPolicy",
+      "tags": ["level-1","team-flow"]
+    },
     {
       "packageName": "@flowgram.ai/group-plugin",
       "projectFolder": "packages/plugins/group-plugin",