From 1acca66868676304b15c3213a6c6025ab9dd07fe Mon Sep 17 00:00:00 2001 From: Andy Brenneke Date: Wed, 31 Jul 2024 13:28:32 -0700 Subject: [PATCH] Add delete hotkey --- packages/app/src/components/NodeCanvas.tsx | 15 ++++++++++ .../useGraphBuilderContextMenuHandler.ts | 21 ++------------ packages/app/src/hooks/useRemoveNodes.ts | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 packages/app/src/hooks/useRemoveNodes.ts diff --git a/packages/app/src/components/NodeCanvas.tsx b/packages/app/src/components/NodeCanvas.tsx index d12688782..dc42ceb4a 100644 --- a/packages/app/src/components/NodeCanvas.tsx +++ b/packages/app/src/components/NodeCanvas.tsx @@ -51,6 +51,7 @@ import { MouseIcon } from './MouseIcon'; import { PortInfo } from './PortInfo'; import { useNodeTypes } from '../hooks/useNodeTypes'; import { lastRunDataByNodeState, selectedProcessPageNodesState } from '../state/dataFlow'; +import { useRemoveNodes } from '../hooks/useRemoveNodes'; const styles = css` width: 100vw; @@ -177,6 +178,7 @@ export const NodeCanvas: FC = ({ const [dragStart, setDragStart] = useState({ x: 0, y: 0, canvasStartX: 0, canvasStartY: 0 }); const { clientToCanvasPosition } = useCanvasPositioning(); const setLastMousePosition = useSetRecoilState(lastMousePositionState); + const removeNodes = useRemoveNodes(); const { refs, floatingStyles } = useFloating({ placement: 'bottom-end', @@ -545,6 +547,19 @@ export const NodeCanvas: FC = ({ { notWhenInputFocused: true }, ); + useGlobalHotkey( + 'Delete', + (e) => { + e.preventDefault(); + + if (selectedNodeIds.length > 0) { + removeNodes(...selectedNodeIds); + setSelectedNodeIds([]); + } + }, + { notWhenInputFocused: true }, + ); + const handleCanvasContextMenu = useStableCallback((e: React.MouseEvent) => { e.preventDefault(); handleContextMenu(e); diff --git a/packages/app/src/hooks/useGraphBuilderContextMenuHandler.ts b/packages/app/src/hooks/useGraphBuilderContextMenuHandler.ts index 682cd8a65..93399992c 100644 --- a/packages/app/src/hooks/useGraphBuilderContextMenuHandler.ts +++ b/packages/app/src/hooks/useGraphBuilderContextMenuHandler.ts @@ -16,13 +16,13 @@ import { useFactorIntoSubgraph } from './useFactorIntoSubgraph'; import { useGraphExecutor } from './useGraphExecutor'; import { useLoadGraph } from './useLoadGraph'; import { usePasteNodes } from './usePasteNodes'; -import { connectionsState, nodesByIdState, nodesState } from '../state/graph'; +import { nodesByIdState, nodesState } from '../state/graph'; import { useCopyNodes } from './useCopyNodes'; import { useDuplicateNode } from './useDuplicateNode'; +import { useRemoveNodes } from './useRemoveNodes'; export function useGraphBuilderContextMenuHandler() { const [nodes, setNodes] = useRecoilState(nodesState); - const [connections, setConnections] = useRecoilState(connectionsState); const { clientToCanvasPosition } = useCanvasPositioning(); const loadGraph = useLoadGraph(); const project = useRecoilValue(projectState); @@ -34,6 +34,7 @@ export function useGraphBuilderContextMenuHandler() { const setEditingNodeId = useSetRecoilState(editingNodeState); const [selectedNodeIds, setSelectedNodeIds] = useRecoilState(selectedNodesState); const nodesById = useRecoilValue(nodesByIdState); + const removeNodes = useRemoveNodes(); const nodesChanged = useStableCallback((newNodes: ChartNode[]) => { setNodes?.(newNodes); @@ -52,22 +53,6 @@ export function useGraphBuilderContextMenuHandler() { // setSelectedNode(newNode.id); }); - const removeNodes = useStableCallback((...nodeIds: NodeId[]) => { - const newNodes = [...nodes]; - let newConnections = [...connections]; - for (const nodeId of nodeIds) { - const nodeIndex = newNodes.findIndex((n) => n.id === nodeId); - if (nodeIndex >= 0) { - newNodes.splice(nodeIndex, 1); - } - - // Remove all connections associated with the node - newConnections = newConnections.filter((c) => c.inputNodeId !== nodeId && c.outputNodeId !== nodeId); - } - nodesChanged?.(newNodes); - setConnections?.(newConnections); - }); - return useStableCallback( (menuItemId: string, data: unknown, context: ContextMenuContext, meta: { x: number; y: number }) => { match(menuItemId) diff --git a/packages/app/src/hooks/useRemoveNodes.ts b/packages/app/src/hooks/useRemoveNodes.ts new file mode 100644 index 000000000..43fcb2ff0 --- /dev/null +++ b/packages/app/src/hooks/useRemoveNodes.ts @@ -0,0 +1,29 @@ +import { type ChartNode, type NodeId } from '@ironclad/rivet-core'; +import { useStableCallback } from './useStableCallback'; +import { useRecoilState } from 'recoil'; +import { connectionsState, nodesState } from '../state/graph'; + +export const useRemoveNodes = () => { + const [nodes, setNodes] = useRecoilState(nodesState); + const [connections, setConnections] = useRecoilState(connectionsState); + + const nodesChanged = useStableCallback((newNodes: ChartNode[]) => { + setNodes?.(newNodes); + }); + + return useStableCallback((...nodeIds: NodeId[]) => { + const newNodes = [...nodes]; + let newConnections = [...connections]; + for (const nodeId of nodeIds) { + const nodeIndex = newNodes.findIndex((n) => n.id === nodeId); + if (nodeIndex >= 0) { + newNodes.splice(nodeIndex, 1); + } + + // Remove all connections associated with the node + newConnections = newConnections.filter((c) => c.inputNodeId !== nodeId && c.outputNodeId !== nodeId); + } + nodesChanged?.(newNodes); + setConnections?.(newConnections); + }); +};