From dec523c6e4a9fe4b8a8a82f32f6ebe669ee19a61 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Sun, 18 Jan 2026 13:23:40 -0800 Subject: [PATCH] fix selection --- .../hooks/use-canvas-context-menu.ts | 19 ++++++++++++++++--- .../[workspaceId]/w/[workflowId]/workflow.tsx | 16 ++++++++++++++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-canvas-context-menu.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-canvas-context-menu.ts index fec22a097..5dade163b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-canvas-context-menu.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-canvas-context-menu.ts @@ -8,13 +8,14 @@ type MenuType = 'block' | 'pane' | null interface UseCanvasContextMenuProps { blocks: Record getNodes: () => Node[] + setNodes: (updater: (nodes: Node[]) => Node[]) => void } /** * Hook for managing workflow canvas context menus. * Handles right-click events, menu state, click-outside detection, and block info extraction. */ -export function useCanvasContextMenu({ blocks, getNodes }: UseCanvasContextMenuProps) { +export function useCanvasContextMenu({ blocks, getNodes, setNodes }: UseCanvasContextMenuProps) { const [activeMenu, setActiveMenu] = useState(null) const [position, setPosition] = useState({ x: 0, y: 0 }) const [selectedBlocks, setSelectedBlocks] = useState([]) @@ -44,14 +45,26 @@ export function useCanvasContextMenu({ blocks, getNodes }: UseCanvasContextMenuP event.preventDefault() event.stopPropagation() + const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey + setNodes((nodes) => + nodes.map((n) => ({ + ...n, + selected: isMultiSelect ? (n.id === node.id ? true : n.selected) : n.id === node.id, + })) + ) + const selectedNodes = getNodes().filter((n) => n.selected) - const nodesToUse = selectedNodes.some((n) => n.id === node.id) ? selectedNodes : [node] + const nodesToUse = isMultiSelect + ? selectedNodes.some((n) => n.id === node.id) + ? selectedNodes + : [...selectedNodes, node] + : [node] setPosition({ x: event.clientX, y: event.clientY }) setSelectedBlocks(nodesToBlockInfos(nodesToUse)) setActiveMenu('block') }, - [getNodes, nodesToBlockInfos] + [getNodes, nodesToBlockInfos, setNodes] ) const handlePaneContextMenu = useCallback((event: React.MouseEvent) => { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index 478ab1099..f974a292b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -234,6 +234,7 @@ const WorkflowContent = React.memo(() => { const [potentialParentId, setPotentialParentId] = useState(null) const [selectedEdges, setSelectedEdges] = useState(new Map()) const [isErrorConnectionDrag, setIsErrorConnectionDrag] = useState(false) + const selectedIdsRef = useRef(null) const canvasMode = useCanvasModeStore((state) => state.mode) const isHandMode = canvasMode === 'hand' const { handleCanvasMouseDown, selectionProps } = useShiftSelectionLock({ isHandMode }) @@ -864,7 +865,7 @@ const WorkflowContent = React.memo(() => { handlePaneContextMenu, handleSelectionContextMenu, closeMenu: closeContextMenu, - } = useCanvasContextMenu({ blocks, getNodes }) + } = useCanvasContextMenu({ blocks, getNodes, setNodes }) const handleContextCopy = useCallback(() => { const blockIds = contextMenuBlocks.map((b) => b.id) @@ -2153,11 +2154,22 @@ const WorkflowContent = React.memo(() => { /** Handles node changes - applies changes and resolves parent-child selection conflicts. */ const onNodesChange = useCallback( (changes: NodeChange[]) => { + selectedIdsRef.current = null setDisplayNodes((nds) => { const updated = applyNodeChanges(changes, nds) const hasSelectionChange = changes.some((c) => c.type === 'select') - return hasSelectionChange ? resolveParentChildSelectionConflicts(updated, blocks) : updated + if (!hasSelectionChange) return updated + const resolved = resolveParentChildSelectionConflicts(updated, blocks) + selectedIdsRef.current = resolved.filter((node) => node.selected).map((node) => node.id) + return resolved }) + const selectedIds = selectedIdsRef.current as string[] | null + if (selectedIds !== null) { + const { currentBlockId, clearCurrentBlock } = usePanelEditorStore.getState() + if (currentBlockId && selectedIds.indexOf(currentBlockId) === -1) { + clearCurrentBlock() + } + } }, [blocks] )