Преглед изворни кода

feat: add workflow editor shortcuts (#3382) (#3390)

Pascal M пре 1 година
родитељ
комит
17efc3ab79

+ 107 - 0
web/app/components/workflow/hooks/use-nodes-interactions.ts

@@ -715,6 +715,108 @@ export const useNodesInteractions = () => {
     handleSyncWorkflowDraft()
   }, [store, handleSyncWorkflowDraft, getNodesReadOnly, t])
 
+  const handleNodeCopySelected = useCallback((): undefined | Node[] => {
+    if (getNodesReadOnly())
+      return
+
+    const {
+      setClipboardElements,
+    } = workflowStore.getState()
+
+    const {
+      getNodes,
+    } = store.getState()
+
+    const nodes = getNodes()
+    const nodesToCopy = nodes.filter(node => node.data.selected)
+
+    setClipboardElements(nodesToCopy)
+
+    return nodesToCopy
+  }, [getNodesReadOnly, store, workflowStore])
+
+  const handleNodePaste = useCallback((): undefined | Node[] => {
+    if (getNodesReadOnly())
+      return
+
+    const {
+      clipboardElements,
+    } = workflowStore.getState()
+
+    const {
+      getNodes,
+      setNodes,
+    } = store.getState()
+
+    const nodesToPaste: Node[] = []
+    const nodes = getNodes()
+
+    for (const nodeToPaste of clipboardElements) {
+      const nodeType = nodeToPaste.data.type
+      const nodesWithSameType = nodes.filter(node => node.data.type === nodeType)
+
+      const newNode = generateNewNode({
+        data: {
+          ...NODES_INITIAL_DATA[nodeType],
+          ...nodeToPaste.data,
+          _connectedSourceHandleIds: [],
+          _connectedTargetHandleIds: [],
+          title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${nodeType}`)} ${nodesWithSameType.length + 1}` : t(`workflow.blocks.${nodeType}`),
+          selected: true,
+        },
+        position: {
+          x: nodeToPaste.position.x + 10,
+          y: nodeToPaste.position.y + 10,
+        },
+      })
+      nodesToPaste.push(newNode)
+    }
+
+    setNodes([...nodes.map((n: Node) => ({ ...n, selected: false, data: { ...n.data, selected: false } })), ...nodesToPaste])
+
+    handleSyncWorkflowDraft()
+
+    return nodesToPaste
+  }, [getNodesReadOnly, handleSyncWorkflowDraft, store, t, workflowStore])
+
+  const handleNodeDuplicateSelected = useCallback(() => {
+    if (getNodesReadOnly())
+      return
+
+    handleNodeCopySelected()
+    handleNodePaste()
+  }, [getNodesReadOnly, handleNodeCopySelected, handleNodePaste])
+
+  const handleNodeCut = useCallback(() => {
+    if (getNodesReadOnly())
+      return
+
+    const nodesToCut = handleNodeCopySelected()
+    if (!nodesToCut)
+      return
+
+    for (const node of nodesToCut)
+      handleNodeDelete(node.id)
+  }, [getNodesReadOnly, handleNodeCopySelected, handleNodeDelete])
+
+  const handleNodeDeleteSelected = useCallback(() => {
+    if (getNodesReadOnly())
+      return
+
+    const {
+      getNodes,
+    } = store.getState()
+
+    const nodes = getNodes()
+    const nodesToDelete = nodes.filter(node => node.data.selected)
+
+    if (!nodesToDelete)
+      return
+
+    for (const node of nodesToDelete)
+      handleNodeDelete(node.id)
+  }, [getNodesReadOnly, handleNodeDelete, store])
+
   return {
     handleNodeDragStart,
     handleNodeDrag,
@@ -729,5 +831,10 @@ export const useNodesInteractions = () => {
     handleNodeDelete,
     handleNodeChange,
     handleNodeAdd,
+    handleNodeDuplicateSelected,
+    handleNodeCopySelected,
+    handleNodeCut,
+    handleNodeDeleteSelected,
+    handleNodePaste,
   }
 }

+ 11 - 1
web/app/components/workflow/index.tsx

@@ -113,6 +113,11 @@ const Workflow: FC<WorkflowProps> = memo(({
     handleNodeConnect,
     handleNodeConnectStart,
     handleNodeConnectEnd,
+    handleNodeDuplicateSelected,
+    handleNodeCopySelected,
+    handleNodeCut,
+    handleNodeDeleteSelected,
+    handleNodePaste,
   } = useNodesInteractions()
   const {
     handleEdgeEnter,
@@ -128,7 +133,12 @@ const Workflow: FC<WorkflowProps> = memo(({
     },
   })
 
-  useKeyPress('Backspace', handleEdgeDelete)
+  useKeyPress(['delete'], handleEdgeDelete)
+  useKeyPress(['delete'], handleNodeDeleteSelected)
+  useKeyPress(['ctrl.c', 'meta.c'], handleNodeCopySelected)
+  useKeyPress(['ctrl.x', 'meta.x'], handleNodeCut)
+  useKeyPress(['ctrl.v', 'meta.v'], handleNodePaste)
+  useKeyPress(['ctrl.alt.d', 'meta.shift.d'], handleNodeDuplicateSelected)
 
   return (
     <div

+ 4 - 0
web/app/components/workflow/store.ts

@@ -63,6 +63,8 @@ type Shape = {
   setBuildInTools: (tools: ToolWithProvider[]) => void
   customTools: ToolWithProvider[]
   setCustomTools: (tools: ToolWithProvider[]) => void
+  clipboardElements: Node[]
+  setClipboardElements: (clipboardElements: Node[]) => void
 }
 
 export const createWorkflowStore = () => {
@@ -107,6 +109,8 @@ export const createWorkflowStore = () => {
     setBuildInTools: buildInTools => set(() => ({ buildInTools })),
     customTools: [],
     setCustomTools: customTools => set(() => ({ customTools })),
+    clipboardElements: [],
+    setClipboardElements: clipboardElements => set(() => ({ clipboardElements })),
   }))
 }