Skip to content

Commit fd686fc

Browse files
committed
fix(context-menu): preserve selection when right-clicking selected block
1 parent ac91d78 commit fd686fc

File tree

2 files changed

+23
-31
lines changed

2 files changed

+23
-31
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-canvas-context-menu.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ interface UseCanvasContextMenuProps {
1111
setNodes: (updater: (nodes: Node[]) => Node[]) => void
1212
}
1313

14-
/**
15-
* Hook for managing workflow canvas context menus.
16-
* Handles right-click events, menu state, click-outside detection, and block info extraction.
17-
*/
1814
export function useCanvasContextMenu({ blocks, getNodes, setNodes }: UseCanvasContextMenuProps) {
1915
const [activeMenu, setActiveMenu] = useState<MenuType>(null)
2016
const [position, setPosition] = useState({ x: 0, y: 0 })
@@ -46,19 +42,29 @@ export function useCanvasContextMenu({ blocks, getNodes, setNodes }: UseCanvasCo
4642
event.stopPropagation()
4743

4844
const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey
49-
setNodes((nodes) =>
50-
nodes.map((n) => ({
51-
...n,
52-
selected: isMultiSelect ? (n.id === node.id ? true : n.selected) : n.id === node.id,
53-
}))
54-
)
55-
56-
const selectedNodes = getNodes().filter((n) => n.selected)
57-
const nodesToUse = isMultiSelect
58-
? selectedNodes.some((n) => n.id === node.id)
59-
? selectedNodes
60-
: [...selectedNodes, node]
61-
: [node]
45+
const currentSelectedNodes = getNodes().filter((n) => n.selected)
46+
const isClickedNodeSelected = currentSelectedNodes.some((n) => n.id === node.id)
47+
48+
let nodesToUse: Node[]
49+
if (isClickedNodeSelected) {
50+
nodesToUse = currentSelectedNodes
51+
} else if (isMultiSelect) {
52+
nodesToUse = [...currentSelectedNodes, node]
53+
setNodes((nodes) =>
54+
nodes.map((n) => ({
55+
...n,
56+
selected: n.id === node.id ? true : n.selected,
57+
}))
58+
)
59+
} else {
60+
nodesToUse = [node]
61+
setNodes((nodes) =>
62+
nodes.map((n) => ({
63+
...n,
64+
selected: n.id === node.id,
65+
}))
66+
)
67+
}
6268

6369
setPosition({ x: event.clientX, y: event.clientY })
6470
setSelectedBlocks(nodesToBlockInfos(nodesToUse))

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-context-menu.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,13 @@ export function useContextMenu({ onContextMenu }: UseContextMenuProps = {}) {
2727
const [isOpen, setIsOpen] = useState(false)
2828
const [position, setPosition] = useState<ContextMenuPosition>({ x: 0, y: 0 })
2929
const menuRef = useRef<HTMLDivElement>(null)
30-
// Used to prevent click-outside dismissal when trigger is clicked
3130
const dismissPreventedRef = useRef(false)
3231

33-
/**
34-
* Handle right-click event
35-
*/
3632
const handleContextMenu = useCallback(
3733
(e: React.MouseEvent) => {
3834
e.preventDefault()
3935
e.stopPropagation()
4036

41-
// Calculate position relative to viewport
4237
const x = e.clientX
4338
const y = e.clientY
4439

@@ -50,17 +45,10 @@ export function useContextMenu({ onContextMenu }: UseContextMenuProps = {}) {
5045
[onContextMenu]
5146
)
5247

53-
/**
54-
* Close the context menu
55-
*/
5648
const closeMenu = useCallback(() => {
5749
setIsOpen(false)
5850
}, [])
5951

60-
/**
61-
* Prevent the next click-outside from dismissing the menu.
62-
* Call this on pointerdown of a toggle trigger to allow proper toggle behavior.
63-
*/
6452
const preventDismiss = useCallback(() => {
6553
dismissPreventedRef.current = true
6654
}, [])
@@ -72,7 +60,6 @@ export function useContextMenu({ onContextMenu }: UseContextMenuProps = {}) {
7260
if (!isOpen) return
7361

7462
const handleClickOutside = (e: MouseEvent) => {
75-
// Check if dismissal was prevented (e.g., by toggle trigger's pointerdown)
7663
if (dismissPreventedRef.current) {
7764
dismissPreventedRef.current = false
7865
return
@@ -82,7 +69,6 @@ export function useContextMenu({ onContextMenu }: UseContextMenuProps = {}) {
8269
}
8370
}
8471

85-
// Small delay to prevent immediate close from the same click that opened the menu
8672
const timeoutId = setTimeout(() => {
8773
document.addEventListener('click', handleClickOutside)
8874
}, 0)

0 commit comments

Comments
 (0)