diff --git a/web/app/(commonLayout)/workflow/page.tsx b/web/app/(commonLayout)/workflow/page.tsx index 4c5319ee77..245e172940 100644 --- a/web/app/(commonLayout)/workflow/page.tsx +++ b/web/app/(commonLayout)/workflow/page.tsx @@ -8,79 +8,79 @@ const initialNodes = [ { id: '1', type: 'custom', - position: { x: 0, y: 0 }, + position: { x: 180, y: 180 }, data: { type: 'start' }, }, - { - id: '2', - type: 'custom', - position: { x: 0, y: 0 }, - data: { - type: 'if-else', - branches: [ - { - id: 'if-true', - name: 'IS TRUE', - }, - { - id: 'if-false', - name: 'IS FALSE', - }, - ], - }, - }, - { - id: '3', - type: 'custom', - position: { x: 0, y: 0 }, - data: { type: 'question-classifier', sortIndexInBranches: 0 }, - }, - { - id: '4', - type: 'custom', - position: { x: 0, y: 0 }, - data: { - type: 'if-else', - sortIndexInBranches: 1, - branches: [ - { - id: 'if-true', - name: 'IS TRUE', - }, - { - id: 'if-false', - name: 'IS FALSE', - }, - ], - }, - }, + // { + // id: '2', + // type: 'custom', + // position: { x: 0, y: 0 }, + // data: { + // type: 'if-else', + // branches: [ + // { + // id: 'if-true', + // name: 'IS TRUE', + // }, + // { + // id: 'if-false', + // name: 'IS FALSE', + // }, + // ], + // }, + // }, + // { + // id: '3', + // type: 'custom', + // position: { x: 0, y: 0 }, + // data: { type: 'question-classifier', sortIndexInBranches: 0 }, + // }, + // { + // id: '4', + // type: 'custom', + // position: { x: 0, y: 0 }, + // data: { + // type: 'if-else', + // sortIndexInBranches: 1, + // branches: [ + // { + // id: 'if-true', + // name: 'IS TRUE', + // }, + // { + // id: 'if-false', + // name: 'IS FALSE', + // }, + // ], + // }, + // }, ] const initialEdges = [ - { - id: '0', - type: 'custom', - source: '1', - sourceHandle: 'source', - target: '2', - targetHandle: 'target', - }, - { - id: '1', - type: 'custom', - source: '2', - sourceHandle: 'if-true', - target: '3', - targetHandle: 'target', - }, - { - id: '2', - type: 'custom', - source: '2', - sourceHandle: 'if-false', - target: '4', - targetHandle: 'target', - }, + // { + // id: '0', + // type: 'custom', + // source: '1', + // sourceHandle: 'source', + // target: '2', + // targetHandle: 'target', + // }, + // { + // id: '1', + // type: 'custom', + // source: '2', + // sourceHandle: 'if-true', + // target: '3', + // targetHandle: 'target', + // }, + // { + // id: '2', + // type: 'custom', + // source: '2', + // sourceHandle: 'if-false', + // target: '4', + // targetHandle: 'target', + // }, ] const Page: FC = () => { diff --git a/web/app/components/workflow/constants.ts b/web/app/components/workflow/constants.ts index 79064a4071..ff861a8e24 100644 --- a/web/app/components/workflow/constants.ts +++ b/web/app/components/workflow/constants.ts @@ -34,7 +34,7 @@ export const NodeInitialData = { retrieval_mode: 'single', }, [BlockEnum.IfElse]: { - branches: [ + targetBranches: [ { id: 'if-true', name: 'IS TRUE', diff --git a/web/app/components/workflow/hooks.ts b/web/app/components/workflow/hooks.ts index bab7ce0a90..4686c9268f 100644 --- a/web/app/components/workflow/hooks.ts +++ b/web/app/components/workflow/hooks.ts @@ -2,6 +2,7 @@ import { useCallback } from 'react' import produce from 'immer' import type { EdgeMouseHandler, + NodeDragHandler, NodeMouseHandler, OnConnect, } from 'reactflow' @@ -17,6 +18,7 @@ import type { } from './types' import { NodeInitialData } from './constants' import { getLayoutByDagre } from './utils' +import { useStore } from './store' export const useWorkflow = () => { const store = useStoreApi() @@ -42,7 +44,30 @@ export const useWorkflow = () => { setNodes(newNodes) }, [store]) - const handleEnterNode = useCallback((_, node) => { + const handleNodeDragStart = useCallback(() => { + useStore.getState().setIsDragging(true) + }, []) + + const handleNodeDrag = useCallback((e, node: Node) => { + const { + getNodes, + setNodes, + } = store.getState() + e.stopPropagation() + + const newNodes = produce(getNodes(), (draft) => { + const currentNode = draft.find(n => n.id === node.id)! + + currentNode.position = node.position + }) + setNodes(newNodes) + }, [store]) + + const handleNodeDragStop = useCallback(() => { + useStore.getState().setIsDragging(false) + }, []) + + const handleNodeEnter = useCallback((_, node) => { const { getNodes, setNodes, @@ -67,7 +92,7 @@ export const useWorkflow = () => { setEdges(newEdges) }, [store]) - const handleLeaveNode = useCallback((_, node) => { + const handleNodeLeave = useCallback((_, node) => { const { getNodes, setNodes, @@ -81,34 +106,37 @@ export const useWorkflow = () => { }) setNodes(newNodes) const newEdges = produce(edges, (draft) => { - const connectedEdges = getConnectedEdges([node], edges) - - connectedEdges.forEach((edge) => { - const currentEdge = draft.find(e => e.id === edge.id) - if (currentEdge) - currentEdge.data = { ...currentEdge.data, connectedNodeIsHovering: false } + draft.forEach((edge) => { + edge.data = { ...edge.data, connectedNodeIsHovering: false } }) }) setEdges(newEdges) }, [store]) - const handleSelectNode = useCallback((nodeId: string, cancelSelection?: boolean) => { + const handleNodeSelect = useCallback((nodeId: string, cancelSelection?: boolean) => { const { getNodes, setNodes, } = store.getState() const newNodes = produce(getNodes(), (draft) => { - draft.forEach(node => node.selected = false) + draft.forEach(node => node.data.selected = false) const selectedNode = draft.find(node => node.id === nodeId)! if (!cancelSelection) - selectedNode.selected = true + selectedNode.data.selected = true }) setNodes(newNodes) }, [store]) - const handleConnectNode = useCallback(({ + const handleNodeClick = useCallback((_, node) => { + if (useStore.getState().isDragging) + return + + handleNodeSelect(node.id) + }, [handleNodeSelect]) + + const handleNodeConnect = useCallback(({ source, sourceHandle, target, @@ -134,51 +162,31 @@ export const useWorkflow = () => { return filtered }) setEdges(newEdges) - handleLayout() - }, [store, handleLayout]) - - const handleEnterEdge = useCallback((_, edge) => { - const { - edges, - setEdges, - } = store.getState() - const newEdges = produce(edges, (draft) => { - const currentEdge = draft.find(e => e.id === edge.id)! - - currentEdge.data = { ...currentEdge.data, hovering: true } - }) - setEdges(newEdges) }, [store]) - const handleLeaveEdge = useCallback((_, edge) => { + const handleNodeDelete = useCallback((nodeId: string) => { const { + getNodes, + setNodes, edges, setEdges, } = store.getState() - const newEdges = produce(edges, (draft) => { - const currentEdge = draft.find(e => e.id === edge.id)! - currentEdge.data = { ...currentEdge.data, hovering: false } - }) - setEdges(newEdges) - }, [store]) - - const handleDeleteEdge = useCallback(() => { - const { - edges, - setEdges, - } = store.getState() - const newEdges = produce(edges, (draft) => { - const index = draft.findIndex(edge => edge.selected) + const newNodes = produce(getNodes(), (draft) => { + const index = draft.findIndex(node => node.id === nodeId) if (index > -1) draft.splice(index, 1) }) + setNodes(newNodes) + const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges) + const newEdges = produce(edges, (draft) => { + return draft.filter(edge => !connectedEdges.find(connectedEdge => connectedEdge.id === edge.id)) + }) setEdges(newEdges) - handleLayout() - }, [store, handleLayout]) + }, [store]) - const handleUpdateNodeData = useCallback(({ id, data }: SelectedNode) => { + const handleNodeDataUpdate = useCallback(({ id, data }: SelectedNode) => { const { getNodes, setNodes, @@ -191,7 +199,7 @@ export const useWorkflow = () => { setNodes(newNodes) }, [store]) - const handleAddNextNode = useCallback((currentNodeId: string, nodeType: BlockEnum, sourceHandle: string) => { + const handleNodeAddNext = useCallback((currentNodeId: string, nodeType: BlockEnum, sourceHandle: string) => { const { getNodes, setNodes, @@ -203,12 +211,14 @@ export const useWorkflow = () => { const nextNode: Node = { id: `${Date.now()}`, type: 'custom', - data: NodeInitialData[nodeType], + data: { + ...NodeInitialData[nodeType], + selected: true, + }, position: { x: currentNode.position.x + 304, y: currentNode.position.y, }, - selected: true, } const newEdge = { id: `${currentNode.id}-${nextNode.id}`, @@ -220,7 +230,7 @@ export const useWorkflow = () => { } const newNodes = produce(nodes, (draft) => { draft.forEach((node) => { - node.selected = false + node.data.selected = false }) draft.push(nextNode) }) @@ -231,7 +241,7 @@ export const useWorkflow = () => { setEdges(newEdges) }, [store]) - const handleChangeCurrentNode = useCallback((currentNodeId: string, nodeType: BlockEnum, sourceHandle?: string) => { + const handleNodeChange = useCallback((currentNodeId: string, nodeType: BlockEnum, sourceHandle?: string) => { const { getNodes, setNodes, @@ -279,40 +289,64 @@ export const useWorkflow = () => { } }, [store]) - const handleDeleteNode = useCallback((nodeId: string) => { + const handleEdgeEnter = useCallback((_, edge) => { const { - getNodes, - setNodes, edges, setEdges, } = store.getState() + const newEdges = produce(edges, (draft) => { + const currentEdge = draft.find(e => e.id === edge.id)! - const newNodes = produce(getNodes(), (draft) => { - const index = draft.findIndex(node => node.id === nodeId) + currentEdge.data = { ...currentEdge.data, hovering: true } + }) + setEdges(newEdges) + }, [store]) + + const handleEdgeLeave = useCallback((_, edge) => { + const { + edges, + setEdges, + } = store.getState() + const newEdges = produce(edges, (draft) => { + const currentEdge = draft.find(e => e.id === edge.id)! + + currentEdge.data = { ...currentEdge.data, hovering: false } + }) + setEdges(newEdges) + }, [store]) + + const handleEdgeDelete = useCallback(() => { + const { + edges, + setEdges, + } = store.getState() + const newEdges = produce(edges, (draft) => { + const index = draft.findIndex(edge => edge.selected) if (index > -1) draft.splice(index, 1) }) - setNodes(newNodes) - const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges) - const newEdges = produce(edges, (draft) => { - return draft.filter(edge => !connectedEdges.find(connectedEdge => connectedEdge.id === edge.id)) - }) setEdges(newEdges) }, [store]) return { - handleEnterNode, - handleLeaveNode, - handleSelectNode, - handleEnterEdge, - handleLeaveEdge, - handleConnectNode, - handleDeleteEdge, - handleUpdateNodeData, - handleAddNextNode, - handleChangeCurrentNode, - handleDeleteNode, handleLayout, + + handleNodeDragStart, + handleNodeDrag, + handleNodeDragStop, + handleNodeEnter, + handleNodeLeave, + handleNodeSelect, + handleNodeClick, + handleNodeConnect, + handleNodeDelete, + handleNodeDataUpdate, + handleNodeAddNext, + handleNodeChange, + + handleEdgeEnter, + handleEdgeLeave, + handleEdgeDelete, } } diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 70c47a248d..3f4b817059 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -1,15 +1,18 @@ import type { FC } from 'react' -import { memo, useEffect } from 'react' +import { + memo, + // useEffect, +} from 'react' import { useKeyPress } from 'ahooks' import ReactFlow, { Background, ReactFlowProvider, useEdgesState, - useNodesInitialized, + // useNodesInitialized, useNodesState, } from 'reactflow' import 'reactflow/dist/style.css' -import './style.css' +// import './style.css' import type { Edge, Node, @@ -42,24 +45,32 @@ const Workflow: FC = memo(({ const showFeaturesPanel = useStore(state => state.showFeaturesPanel) const [nodes] = useNodesState(initialNodes) const [edges, _, onEdgesChange] = useEdgesState(initialEdges) - const nodesInitialized = useNodesInitialized() + // const nodesInitialized = useNodesInitialized() + + console.log(nodes) const { - handleEnterNode, - handleLeaveNode, - handleConnectNode, - handleEnterEdge, - handleLeaveEdge, - handleDeleteEdge, - handleLayout, + // handleLayout, + + handleNodeDragStart, + handleNodeDrag, + handleNodeDragStop, + handleNodeEnter, + handleNodeLeave, + handleNodeClick, + handleNodeConnect, + + handleEdgeEnter, + handleEdgeLeave, + handleEdgeDelete, } = useWorkflow() - useEffect(() => { - if (nodesInitialized) - handleLayout() - }, [nodesInitialized, handleLayout]) + // useEffect(() => { + // if (nodesInitialized) + // handleLayout() + // }, [nodesInitialized, handleLayout]) - useKeyPress('Backspace', handleDeleteEdge) + useKeyPress('Backspace', handleEdgeDelete) return (
@@ -74,11 +85,15 @@ const Workflow: FC = memo(({ edgeTypes={edgeTypes} nodes={nodes} edges={edges} - onConnect={handleConnectNode} - onNodeMouseEnter={handleEnterNode} - onNodeMouseLeave={handleLeaveNode} - onEdgeMouseEnter={handleEnterEdge} - onEdgeMouseLeave={handleLeaveEdge} + onNodeDragStart={handleNodeDragStart} + onNodeDrag={handleNodeDrag} + onNodeDragStop={handleNodeDragStop} + onNodeMouseEnter={handleNodeEnter} + onNodeMouseLeave={handleNodeLeave} + onNodeClick={handleNodeClick} + onConnect={handleNodeConnect} + onEdgeMouseEnter={handleEdgeEnter} + onEdgeMouseLeave={handleEdgeLeave} onEdgesChange={onEdgesChange} multiSelectionKeyCode={null} connectionLineComponent={CustomConnectionLine} diff --git a/web/app/components/workflow/nodes/_base/components/next-step/add.tsx b/web/app/components/workflow/nodes/_base/components/next-step/add.tsx index 4aad178233..2ff4d49d3d 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/add.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/add.tsx @@ -17,11 +17,11 @@ const Add = ({ sourceHandle, branchName, }: AddProps) => { - const { handleAddNextNode } = useWorkflow() + const { handleNodeAddNext } = useWorkflow() const handleSelect = useCallback((type: BlockEnum) => { - handleAddNextNode(nodeId, type, sourceHandle) - }, [nodeId, sourceHandle, handleAddNextNode]) + handleNodeAddNext(nodeId, type, sourceHandle) + }, [nodeId, sourceHandle, handleNodeAddNext]) const renderTrigger = useCallback((open: boolean) => { return ( diff --git a/web/app/components/workflow/nodes/_base/components/next-step/index.tsx b/web/app/components/workflow/nodes/_base/components/next-step/index.tsx index 8b41025184..76dc7bc880 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/index.tsx @@ -18,7 +18,7 @@ const NextStep = ({ selectedNode, }: NextStepProps) => { const store = useStoreApi() - const branches = selectedNode?.data.branches + const branches = selectedNode?.data.targetBranches const edges = useEdges() const outgoers = getOutgoers(selectedNode as Node, store.getState().getNodes(), edges) const connectedEdges = getConnectedEdges([selectedNode] as Node[], edges).filter(edge => edge.source === selectedNode!.id) diff --git a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx index 884b9aa32f..aed54e80e3 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx @@ -23,10 +23,10 @@ const Item = ({ branchName, data, }: ItemProps) => { - const { handleChangeCurrentNode } = useWorkflow() + const { handleNodeChange } = useWorkflow() const handleSelect = useCallback((type: BlockEnum) => { - handleChangeCurrentNode(nodeId, type, sourceHandle) - }, [nodeId, sourceHandle, handleChangeCurrentNode]) + handleNodeChange(nodeId, type, sourceHandle) + }, [nodeId, sourceHandle, handleNodeChange]) const renderTrigger = useCallback((open: boolean) => { return (