### What problem does this PR solve? feat: build react flow nodes and edges from mock data #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.7.0
| @@ -0,0 +1,19 @@ | |||
| export type DSLComponents = Record<string, Operator>; | |||
| export interface DSL { | |||
| components: DSLComponents; | |||
| history: any[]; | |||
| path: string[]; | |||
| answer: any[]; | |||
| } | |||
| export interface Operator { | |||
| obj: OperatorNode; | |||
| downstream: string[]; | |||
| upstream: string[]; | |||
| } | |||
| export interface OperatorNode { | |||
| component_name: string; | |||
| params: Record<string, unknown>; | |||
| } | |||
| @@ -8,6 +8,7 @@ import ReactFlow, { | |||
| OnConnect, | |||
| OnEdgesChange, | |||
| OnNodesChange, | |||
| Position, | |||
| addEdge, | |||
| applyEdgeChanges, | |||
| applyNodeChanges, | |||
| @@ -24,21 +25,27 @@ const nodeTypes = { textUpdater: TextUpdaterNode }; | |||
| const initialNodes = [ | |||
| { | |||
| sourcePosition: Position.Left, | |||
| targetPosition: Position.Right, | |||
| id: 'node-1', | |||
| type: 'textUpdater', | |||
| position: { x: 200, y: 50 }, | |||
| data: { value: 123 }, | |||
| position: { x: 400, y: 100 }, | |||
| data: { label: 123 }, | |||
| }, | |||
| { | |||
| sourcePosition: Position.Right, | |||
| targetPosition: Position.Left, | |||
| id: '1', | |||
| data: { label: 'Hello' }, | |||
| position: { x: 0, y: 0 }, | |||
| position: { x: 0, y: 50 }, | |||
| type: 'input', | |||
| }, | |||
| { | |||
| sourcePosition: Position.Right, | |||
| targetPosition: Position.Left, | |||
| id: '2', | |||
| data: { label: 'World' }, | |||
| position: { x: 100, y: 100 }, | |||
| position: { x: 200, y: 50 }, | |||
| }, | |||
| ]; | |||
| @@ -48,7 +55,6 @@ const initialEdges = [ | |||
| interface IProps { | |||
| sideWidth: number; | |||
| showDrawer(): void; | |||
| } | |||
| function FlowCanvas({ sideWidth }: IProps) { | |||
| @@ -1,6 +1,6 @@ | |||
| .textUpdaterNode { | |||
| height: 50px; | |||
| border: 1px solid #eee; | |||
| // height: 50px; | |||
| border: 1px solid black; | |||
| padding: 5px; | |||
| border-radius: 5px; | |||
| background: white; | |||
| @@ -1,41 +1,24 @@ | |||
| import { useCallback } from 'react'; | |||
| import { Handle, NodeProps, Position } from 'reactflow'; | |||
| import styles from './index.less'; | |||
| const handleStyle = { left: 10 }; | |||
| export function TextUpdaterNode({ | |||
| data, | |||
| isConnectable = true, | |||
| }: NodeProps<{ value: number }>) { | |||
| const onChange = useCallback((evt) => { | |||
| console.log(evt.target.value); | |||
| }, []); | |||
| }: NodeProps<{ label: string }>) { | |||
| return ( | |||
| <div className={styles.textUpdaterNode}> | |||
| <Handle | |||
| type="target" | |||
| position={Position.Top} | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| /> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Bottom} | |||
| // style={handleStyle} | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| /> | |||
| <div> | |||
| <label htmlFor="text">Text:</label> | |||
| <input id="text" name="text" onChange={onChange} className="nodrag" /> | |||
| </div> | |||
| {/* <Handle | |||
| type="source" | |||
| position={Position.Bottom} | |||
| id="b" | |||
| isConnectable={isConnectable} | |||
| /> */} | |||
| <div>{data.label}</div> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| import { useSetModalState } from '@/hooks/commonHooks'; | |||
| import React, { Dispatch, SetStateAction, useCallback, useState } from 'react'; | |||
| import { Node, ReactFlowInstance } from 'reactflow'; | |||
| import { Node, Position, ReactFlowInstance } from 'reactflow'; | |||
| import { v4 as uuidv4 } from 'uuid'; | |||
| export const useHandleDrag = () => { | |||
| @@ -44,12 +44,14 @@ export const useHandleDrop = (setNodes: Dispatch<SetStateAction<Node[]>>) => { | |||
| }); | |||
| const newNode = { | |||
| id: uuidv4(), | |||
| type, | |||
| type: 'textUpdater', | |||
| position: position || { | |||
| x: 0, | |||
| y: 0, | |||
| }, | |||
| data: { label: `${type} node` }, | |||
| data: { label: `${type}` }, | |||
| sourcePosition: Position.Right, | |||
| targetPosition: Position.Left, | |||
| }; | |||
| setNodes((nds) => nds.concat(newNode)); | |||
| @@ -0,0 +1,4 @@ | |||
| export interface DSLComponentList { | |||
| id: string; | |||
| name: string; | |||
| } | |||
| @@ -9,3 +9,43 @@ export const componentList = [ | |||
| { name: 'Retrieval', icon: <RocketOutlined />, description: '' }, | |||
| { name: 'Generate', icon: <MergeCellsOutlined />, description: '' }, | |||
| ]; | |||
| export const dsl = { | |||
| components: { | |||
| begin: { | |||
| obj: { | |||
| component_name: 'Begin', | |||
| params: {}, | |||
| }, | |||
| downstream: ['Answer:China'], | |||
| upstream: [], | |||
| }, | |||
| 'Answer:China': { | |||
| obj: { | |||
| component_name: 'Answer', | |||
| params: {}, | |||
| }, | |||
| downstream: ['Retrieval:China'], | |||
| upstream: ['begin', 'Generate:China'], | |||
| }, | |||
| 'Retrieval:China': { | |||
| obj: { | |||
| component_name: 'Retrieval', | |||
| params: {}, | |||
| }, | |||
| downstream: ['Generate:China'], | |||
| upstream: ['Answer:China'], | |||
| }, | |||
| 'Generate:China': { | |||
| obj: { | |||
| component_name: 'Generate', | |||
| params: {}, | |||
| }, | |||
| downstream: ['Answer:China'], | |||
| upstream: ['Retrieval:China'], | |||
| }, | |||
| }, | |||
| history: [], | |||
| path: ['begin'], | |||
| answer: [], | |||
| }; | |||
| @@ -0,0 +1,44 @@ | |||
| import { DSLComponents } from '@/interfaces/database/flow'; | |||
| import { Edge, Node, Position } from 'reactflow'; | |||
| import { v4 as uuidv4 } from 'uuid'; | |||
| export const buildNodesFromDSLComponents = (data: DSLComponents) => { | |||
| const nodes: Node[] = []; | |||
| const edges: Edge[] = []; | |||
| Object.entries(data).forEach(([key, value]) => { | |||
| const downstream = [...value.downstream]; | |||
| const upstream = [...value.upstream]; | |||
| nodes.push({ | |||
| id: key, | |||
| type: 'textUpdater', | |||
| position: { x: 0, y: 0 }, | |||
| data: { | |||
| label: value.obj.component_name, | |||
| params: value.obj.params, | |||
| downstream: downstream, | |||
| upstream: upstream, | |||
| }, | |||
| sourcePosition: Position.Left, | |||
| targetPosition: Position.Right, | |||
| }); | |||
| // intermediate node | |||
| // The first and last nodes do not need to be considered | |||
| if (upstream.length > 0 && downstream.length > 0) { | |||
| for (let i = 0; i < upstream.length; i++) { | |||
| const up = upstream[i]; | |||
| for (let j = 0; j < downstream.length; j++) { | |||
| const down = downstream[j]; | |||
| edges.push({ | |||
| id: uuidv4(), | |||
| label: '', | |||
| type: 'step', | |||
| source: up, | |||
| target: down, | |||
| }); | |||
| } | |||
| } | |||
| } | |||
| }); | |||
| }; | |||