diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx index 712434f5c0..e6a787bc62 100644 --- a/web/app/components/workflow/block-selector/index.tsx +++ b/web/app/components/workflow/block-selector/index.tsx @@ -57,8 +57,8 @@ const NodeSelector: FC = ({ }, [onOpenChange]) const handleTrigger = useCallback>((e) => { e.stopPropagation() - handleOpenChange(!open) - }, [open, handleOpenChange]) + setLocalOpen(v => !v) + }, []) const handleSelect = useCallback((type: BlockEnum) => { handleOpenChange(false) onSelect(type) diff --git a/web/app/components/workflow/header/index.tsx b/web/app/components/workflow/header/index.tsx index 0bc2f69bd6..b283ad1772 100644 --- a/web/app/components/workflow/header/index.tsx +++ b/web/app/components/workflow/header/index.tsx @@ -14,6 +14,8 @@ import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arr const Header: FC = () => { const mode = useStore(state => state.mode) const setShowFeatures = useStore(state => state.setShowFeatures) + const runStaus = useStore(state => state.runStaus) + const setRunStaus = useStore(state => state.setRunStaus) const handleShowFeatures = useCallback(() => { setShowFeatures(true) @@ -36,13 +38,20 @@ const Header: FC = () => {
- + { + runStaus && ( + + ) + }
{ diff --git a/web/app/components/workflow/header/run-and-history.tsx b/web/app/components/workflow/header/run-and-history.tsx index 7997fa1809..6121a272f2 100644 --- a/web/app/components/workflow/header/run-and-history.tsx +++ b/web/app/components/workflow/header/run-and-history.tsx @@ -4,19 +4,39 @@ import { useStore } from '../store' import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' import TooltipPlus from '@/app/components/base/tooltip-plus' +import { Loading02 } from '@/app/components/base/icons/src/vender/line/general' const RunAndHistory: FC = () => { const showRunHistory = useStore(state => state.showRunHistory) const setShowRunHistory = useStore(state => state.setShowRunHistory) + const runStaus = useStore(state => state.runStaus) + const setRunStaus = useStore(state => state.setRunStaus) return (
-
- - Run +
runStaus !== 'running' && setRunStaus('running')} + > + { + runStaus === 'running' + ? ( + <> + + Running + + ) + : ( + <> + + Run + + ) + }
{ const store = useStoreApi() @@ -78,14 +77,11 @@ export const useWorkflow = () => { } = store.getState() const newNodes = produce(getNodes(), (draft) => { - const selectedNode = draft.find(node => node.id === nodeId) + draft.forEach(node => node.selected = false) + const selectedNode = draft.find(node => node.id === nodeId)! - if (selectedNode) { - if (cancelSelection) - selectedNode.selected = false - else - selectedNode.selected = true - } + if (!cancelSelection) + selectedNode.selected = true }) setNodes(newNodes) }, [store]) @@ -124,9 +120,9 @@ export const useWorkflow = () => { setEdges, } = store.getState() const newEdges = produce(edges, (draft) => { - const currentEdge = draft.find(e => e.id === edge.id) - if (currentEdge) - currentEdge.data = { ...currentEdge.data, hovering: true } + const currentEdge = draft.find(e => e.id === edge.id)! + + currentEdge.data = { ...currentEdge.data, hovering: true } }) setEdges(newEdges) }, [store]) @@ -137,9 +133,9 @@ export const useWorkflow = () => { setEdges, } = store.getState() const newEdges = produce(edges, (draft) => { - const currentEdge = draft.find(e => e.id === edge.id) - if (currentEdge) - currentEdge.data = { ...currentEdge.data, hovering: false } + const currentEdge = draft.find(e => e.id === edge.id)! + + currentEdge.data = { ...currentEdge.data, hovering: false } }) setEdges(newEdges) }, [store]) @@ -281,59 +277,6 @@ export const useWorkflow = () => { setEdges(newEdges) }, [store]) - const handleInitialLayoutNodes = useCallback(() => { - const { - getNodes, - setNodes, - edges, - setEdges, - } = store.getState() - - setNodes(initialNodesPosition(getNodes(), edges)) - setEdges(produce(edges, (draft) => { - draft.forEach((edge) => { - edge.hidden = false - }) - })) - }, [store]) - - const handleUpdateNodesPosition = useCallback(() => { - const { - getNodes, - setNodes, - } = store.getState() - - const nodes = getNodes() - const groups = nodes.reduce((acc, cur) => { - const x = cur.data.position.x - - if (!acc[x]) - acc[x] = [cur] - else - acc[x].push(cur) - - return acc - }, {} as Record) - const heightMap: Record = {} - - Object.keys(groups).forEach((key) => { - let baseHeight = 0 - groups[key].sort((a, b) => a.data.position!.y - b.data.position!.y).forEach((node) => { - heightMap[node.id] = baseHeight - baseHeight = node.height! + 39 - }) - }) - setNodes(produce(nodes, (draft) => { - draft.forEach((node) => { - node.position = { - ...node.position, - x: node.data.position.x * (220 + 64), - y: heightMap[node.id], - } - }) - })) - }, [store]) - return { handleEnterNode, handleLeaveNode, @@ -346,7 +289,5 @@ export const useWorkflow = () => { handleAddNextNode, handleChangeCurrentNode, handleDeleteNode, - handleInitialLayoutNodes, - handleUpdateNodesPosition, } } diff --git a/web/app/components/workflow/nodes/_base/components/node-handle.tsx b/web/app/components/workflow/nodes/_base/components/node-handle.tsx index 8221ee8982..27eef944dc 100644 --- a/web/app/components/workflow/nodes/_base/components/node-handle.tsx +++ b/web/app/components/workflow/nodes/_base/components/node-handle.tsx @@ -1,3 +1,4 @@ +import type { MouseEvent } from 'react' import { useCallback, useState, @@ -35,10 +36,11 @@ export const NodeTargetHandle = ({ const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) - const handleHandleClick = () => { + const handleHandleClick = useCallback((e: MouseEvent) => { + e.stopPropagation() if (!connected) - handleOpenChange(!open) - } + setOpen(v => !v) + }, [connected]) return ( <> @@ -93,10 +95,11 @@ export const NodeSourceHandle = ({ const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) - const handleHandleClick = () => { + const handleHandleClick = useCallback((e: MouseEvent) => { + e.stopPropagation() if (!connected) - handleOpenChange(!open) - } + setOpen(v => !v) + }, [connected]) const handleSelect = useCallback((type: BlockEnum) => { handleAddNextNode(id, type, handleId) }, [handleAddNextNode, id, handleId]) diff --git a/web/app/components/workflow/panel/index.tsx b/web/app/components/workflow/panel/index.tsx index 588068a117..b23b7759d7 100644 --- a/web/app/components/workflow/panel/index.tsx +++ b/web/app/components/workflow/panel/index.tsx @@ -10,9 +10,11 @@ import { useStore } from '../store' import WorkflowInfo from './workflow-info' import DebugAndPreview from './debug-and-preview' import RunHistory from './run-history' +import Record from './record' const Panel: FC = () => { const mode = useStore(state => state.mode) + const runStaus = useStore(state => state.runStaus) const nodes = useNodes() const selectedNode = nodes.find(node => node.selected) const showRunHistory = useStore(state => state.showRunHistory) @@ -30,6 +32,11 @@ const Panel: FC = () => { return (
+ { + runStaus && ( + + ) + } { showNodePanel && ( diff --git a/web/app/components/workflow/panel/record.tsx b/web/app/components/workflow/panel/record.tsx new file mode 100644 index 0000000000..86c6e20586 --- /dev/null +++ b/web/app/components/workflow/panel/record.tsx @@ -0,0 +1,11 @@ +const Record = () => { + return ( +
+
+ Test Run#5 +
+
+ ) +} + +export default Record diff --git a/web/app/components/workflow/panel/run-history.tsx b/web/app/components/workflow/panel/run-history.tsx index 61753846b4..8b3f848d8b 100644 --- a/web/app/components/workflow/panel/run-history.tsx +++ b/web/app/components/workflow/panel/run-history.tsx @@ -8,6 +8,7 @@ import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsA const RunHistory = () => { const mode = useStore(state => state.mode) const setShowRunHistory = useStore(state => state.setShowRunHistory) + const setRunStaus = useStore(state => state.setRunStaus) return (
@@ -34,7 +35,10 @@ const RunHistory = () => {
) } -
+
setRunStaus('finished')} + >
Test Run#6
diff --git a/web/app/components/workflow/store.ts b/web/app/components/workflow/store.ts index 7418b9825d..dcc2ef03f6 100644 --- a/web/app/components/workflow/store.ts +++ b/web/app/components/workflow/store.ts @@ -4,11 +4,13 @@ type State = { mode: string showRunHistory: boolean showFeatures: boolean + runStaus: string } type Action = { setShowRunHistory: (showRunHistory: boolean) => void setShowFeatures: (showFeatures: boolean) => void + setRunStaus: (runStaus: string) => void } export const useStore = create(set => ({ @@ -17,4 +19,6 @@ export const useStore = create(set => ({ setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })), showFeatures: false, setShowFeatures: showFeatures => set(() => ({ showFeatures })), + runStaus: 'finished', + setRunStaus: runStaus => set(() => ({ runStaus })), })) diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index abb0c910de..a7a4a60eb3 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -24,13 +24,12 @@ export type Branch = { } export type CommonNodeType = { - position?: { + index?: { x: number y: number } - sortIndexInBranches?: number hovering?: boolean - branches?: Branch[] + targetBranches?: Branch[] title: string desc: string type: BlockEnum diff --git a/web/app/components/workflow/utils.ts b/web/app/components/workflow/utils.ts index b572c7b02f..91be52872a 100644 --- a/web/app/components/workflow/utils.ts +++ b/web/app/components/workflow/utils.ts @@ -1,54 +1,83 @@ +import produce from 'immer' import { + getConnectedEdges, getOutgoers, } from 'reactflow' -import { cloneDeep } from 'lodash-es' import type { Edge, Node, } from './types' -import { BlockEnum } from './types' -export const initialNodesPosition = (oldNodes: Node[], edges: Edge[]) => { - const nodes = cloneDeep(oldNodes) - const start = nodes.find(node => node.data.type === BlockEnum.Start)! +export const nodesLevelOrderTraverse = ( + firstNode: Node, + nodes: Node[], + edges: Edge[], + callback: (n: Node) => void, +) => { + const queue = [{ + node: firstNode, + }] - start.position.x = 0 - start.position.y = 0 - start.data.position = { - x: 0, - y: 0, - } - const queue = [start] - - let depth = 0 - let breadth = 0 - let baseHeight = 0 while (queue.length) { - const node = queue.shift()! + const { node } = queue.shift()! + callback(node) - if (node.data.position?.x !== depth) { - breadth = 0 - baseHeight = 0 + const targetBranches = node.data.targetBranches + if (targetBranches?.length) { + const targetEdges = getConnectedEdges([node], edges) + + if (targetEdges.length) { + const sortedTargetEdges = targetEdges + .filter(edge => edge.source === node.id) + .sort((a, b) => { + const aIndex = targetBranches.findIndex(branch => branch.id === a.sourceHandle) + const bIndex = targetBranches.findIndex(branch => branch.id === b.sourceHandle) + + return aIndex - bIndex + }) + + const outgoers = getOutgoers(node, nodes, sortedTargetEdges) + queue.push(...outgoers.map((outgoer) => { + return { + node: outgoer, + } + })) + } } + else { + const outgoers = getOutgoers(node, nodes, edges) - depth = node.data.position?.x || 0 - - const outgoers = getOutgoers(node, nodes, edges).sort((a, b) => (a.data.sortIndexInBranches || 0) - (b.data.sortIndexInBranches || 0)) - - if (outgoers.length) { - queue.push(...outgoers.map((outgoer) => { - outgoer.data.position = { - x: depth + 1, - y: breadth, - } - outgoer.position.x = (depth + 1) * (220 + 64) - outgoer.position.y = baseHeight - baseHeight += ((outgoer.height || 0) + 39) - breadth += 1 - return outgoer - })) + if (outgoers.length === 1) { + queue.push({ + node: outgoers[0], + }) + } } } - - return nodes +} + +export const initialNodesAndEdges = (nodes: Node[], edges: Edge[]) => { + const newNodes = produce(nodes, (draft) => { + draft.forEach((node) => { + node.type = 'custom' + }) + }) + const newEdges = produce(edges, (draft) => { + draft.forEach((edge) => { + edge.type = 'custom' + }) + }) + + return [newNodes, newEdges] +} + +export type PositionMap = { + position: { + x: number + y: number + } + index: { + x: number + y: number + } }