Skip to content

Commit 972d0a5

Browse files
committed
make selection context more reliable
1 parent b110966 commit 972d0a5

File tree

2 files changed

+63
-18
lines changed

2 files changed

+63
-18
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-canvas-helpers.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,55 @@ export function resolveParentChildSelectionConflicts(
221221

222222
return hasConflict ? resolved : nodes
223223
}
224+
225+
export function getNodeSelectionContextId(
226+
node: Pick<Node, 'id' | 'parentId'>,
227+
blocks: Record<string, { data?: { parentId?: string } }>
228+
): string | null {
229+
return node.parentId || blocks[node.id]?.data?.parentId || null
230+
}
231+
232+
export function getEdgeSelectionContextId(
233+
edge: Pick<Edge, 'source' | 'target'>,
234+
nodes: Array<Pick<Node, 'id' | 'parentId'>>,
235+
blocks: Record<string, { data?: { parentId?: string } }>
236+
): string | null {
237+
const sourceNode = nodes.find((node) => node.id === edge.source)
238+
const targetNode = nodes.find((node) => node.id === edge.target)
239+
if (sourceNode) return getNodeSelectionContextId(sourceNode, blocks)
240+
if (targetNode) return getNodeSelectionContextId(targetNode, blocks)
241+
return null
242+
}
243+
244+
export function resolveSelectionContextConflicts(
245+
nodes: Node[],
246+
blocks: Record<string, { data?: { parentId?: string } }>
247+
): Node[] {
248+
const selectedNodes = nodes.filter((node) => node.selected)
249+
if (selectedNodes.length <= 1) return nodes
250+
251+
const allowedContextId = getNodeSelectionContextId(selectedNodes[0], blocks)
252+
let hasConflict = false
253+
254+
const resolved = nodes.map((node) => {
255+
if (!node.selected) return node
256+
const contextId = getNodeSelectionContextId(node, blocks)
257+
if (contextId !== allowedContextId) {
258+
hasConflict = true
259+
return { ...node, selected: false }
260+
}
261+
return node
262+
})
263+
264+
return hasConflict ? resolved : nodes
265+
}
266+
267+
export function resolveSelectionConflicts(
268+
nodes: Node[],
269+
blocks: Record<string, { data?: { parentId?: string } }>
270+
): Node[] {
271+
return resolveSelectionContextConflicts(
272+
resolveParentChildSelectionConflicts(nodes, blocks),
273+
blocks
274+
)
275+
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ import {
5959
filterProtectedBlocks,
6060
getClampedPositionForNode,
6161
getDescendantBlockIds,
62+
getEdgeSelectionContextId,
6263
getWorkflowLockToggleIds,
6364
isBlockProtected,
6465
isEdgeProtected,
6566
isInEditableElement,
66-
resolveParentChildSelectionConflicts,
67+
resolveSelectionConflicts,
6768
validateTriggerPaste,
6869
} from '@/app/workspace/[workspaceId]/w/[workflowId]/utils'
6970
import { useSocket } from '@/app/workspace/providers/socket-provider'
@@ -2492,7 +2493,7 @@ const WorkflowContent = React.memo(
24922493
...node,
24932494
selected: pendingSet.has(node.id),
24942495
}))
2495-
const resolved = resolveParentChildSelectionConflicts(withSelection, blocks)
2496+
const resolved = resolveSelectionConflicts(withSelection, blocks)
24962497
setDisplayNodes(resolved)
24972498
const selectedIds = resolved.filter((node) => node.selected).map((node) => node.id)
24982499
syncPanelWithSelection(selectedIds)
@@ -2715,9 +2716,7 @@ const WorkflowContent = React.memo(
27152716
(changes: NodeChange[]) => {
27162717
const updated = applyNodeChanges(changes, displayNodesRef.current)
27172718
const hasSelectionChange = changes.some((c) => c.type === 'select')
2718-
const nextNodes = hasSelectionChange
2719-
? resolveParentChildSelectionConflicts(updated, blocks)
2720-
: updated
2719+
const nextNodes = hasSelectionChange ? resolveSelectionConflicts(updated, blocks) : updated
27212720

27222721
displayNodesRef.current = nextNodes
27232722
setDisplayNodes(nextNodes)
@@ -3457,7 +3456,7 @@ const WorkflowContent = React.memo(
34573456
})
34583457

34593458
// Apply visual deselection of children
3460-
setDisplayNodes((allNodes) => resolveParentChildSelectionConflicts(allNodes, blocks))
3459+
setDisplayNodes((allNodes) => resolveSelectionConflicts(allNodes, blocks))
34613460
},
34623461
[blocks]
34633462
)
@@ -3622,7 +3621,7 @@ const WorkflowContent = React.memo(
36223621
: currentNode.selected
36233622
: currentNode.id === node.id,
36243623
}))
3625-
const resolved = resolveParentChildSelectionConflicts(updated, blocks)
3624+
const resolved = resolveSelectionConflicts(updated, blocks)
36263625
const selectedIds = resolved
36273626
.filter((selectedNode) => selectedNode.selected)
36283627
.map((selectedNode) => selectedNode.id)
@@ -3639,16 +3638,10 @@ const WorkflowContent = React.memo(
36393638
(event: React.MouseEvent, edge: any) => {
36403639
event.stopPropagation() // Prevent bubbling
36413640

3642-
// Determine if edge is inside a loop by checking its source/target nodes
3643-
const sourceNode = getNodes().find((n) => n.id === edge.source)
3644-
const targetNode = getNodes().find((n) => n.id === edge.target)
3645-
3646-
// An edge is inside a loop if either source or target has a parent
3647-
// If source and target have different parents, prioritize source's parent
3648-
const parentLoopId = sourceNode?.parentId || targetNode?.parentId
3649-
3650-
// Create a unique identifier that combines edge ID and parent context
3651-
const contextId = `${edge.id}${parentLoopId ? `-${parentLoopId}` : ''}`
3641+
const contextId = `${edge.id}${(() => {
3642+
const selectionContextId = getEdgeSelectionContextId(edge, getNodes(), blocks)
3643+
return selectionContextId ? `-${selectionContextId}` : ''
3644+
})()}`
36523645

36533646
if (event.shiftKey) {
36543647
// Shift-click: toggle edge in selection
@@ -3666,7 +3659,7 @@ const WorkflowContent = React.memo(
36663659
setSelectedEdges(new Map([[contextId, edge.id]]))
36673660
}
36683661
},
3669-
[getNodes]
3662+
[blocks, getNodes]
36703663
)
36713664

36723665
/** Stable delete handler to avoid creating new function references per edge. */

0 commit comments

Comments
 (0)