Skip to content

Commit 0a2a349

Browse files
waleedlatif1TheodoreSpeaks
authored andcommitted
fix(subflows): make edges inside subflows directly clickable (#3969)
* fix(subflows): make edges inside subflows directly clickable Edges inside subflows defaulted to z-index 0, causing the subflow body area (pointer-events: auto) to intercept clicks. Derive edge z-index from the container's depth so edges sit just above their parent container but below canvas blocks and child blocks. * Fix edge deletion in nested subflows * Fix bug with multi selecting nested subblock --------- Co-authored-by: Theodore Li <theo@sim.ai>
1 parent e0aa566 commit 0a2a349

File tree

2 files changed

+65
-11
lines changed

2 files changed

+65
-11
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ const WorkflowEdgeComponent = ({
134134
position: 'absolute',
135135
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
136136
pointerEvents: 'all',
137-
zIndex: 100,
137+
zIndex: 1011,
138138
}}
139139
onClick={(e) => {
140140
e.preventDefault()

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

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2772,10 +2772,37 @@ const WorkflowContent = React.memo(
27722772
(changes: NodeChange[]) => {
27732773
const hasSelectionChange = changes.some((c) => c.type === 'select')
27742774
setDisplayNodes((currentNodes) => {
2775-
const updated = applyNodeChanges(changes, currentNodes)
2775+
// Filter out cross-context selection changes before applying so that
2776+
// nodes at a different nesting level never appear selected, even for
2777+
// a single frame.
2778+
let changesToApply = changes
2779+
if (hasSelectionChange) {
2780+
const currentlySelected = currentNodes.filter((n) => n.selected)
2781+
// Only filter on additive multi-select (shift-click), not replacement
2782+
// clicks. A replacement click includes deselections of currently selected
2783+
// nodes; a shift-click only adds selections.
2784+
const isReplacementClick = changes.some(
2785+
(c) =>
2786+
c.type === 'select' &&
2787+
'selected' in c &&
2788+
!c.selected &&
2789+
currentlySelected.some((n) => n.id === c.id)
2790+
)
2791+
if (!isReplacementClick && currentlySelected.length > 0) {
2792+
const selectionContext = getNodeSelectionContextId(currentlySelected[0], blocks)
2793+
changesToApply = changes.filter((c) => {
2794+
if (c.type !== 'select' || !('selected' in c) || !c.selected) return true
2795+
const node = currentNodes.find((n) => n.id === c.id)
2796+
if (!node) return true
2797+
return getNodeSelectionContextId(node, blocks) === selectionContext
2798+
})
2799+
}
2800+
}
2801+
2802+
const updated = applyNodeChanges(changesToApply, currentNodes)
27762803
if (!hasSelectionChange) return updated
27772804

2778-
const preferredNodeId = [...changes]
2805+
const preferredNodeId = [...changesToApply]
27792806
.reverse()
27802807
.find(
27812808
(change): change is NodeChange & { id: string; selected: boolean } =>
@@ -3271,12 +3298,17 @@ const WorkflowContent = React.memo(
32713298
previousPositions: multiNodeDragStartRef.current,
32723299
})
32733300

3274-
// Process parent updates using shared helper
3275-
executeBatchParentUpdate(
3276-
selectedNodes,
3277-
potentialParentId,
3278-
'Batch moved nodes to new parent'
3279-
)
3301+
// Only reparent when an actual drag changed the target container.
3302+
// onNodeDragStart sets both potentialParentId and dragStartParentId to the
3303+
// clicked node's current parent; they only diverge when onNodeDrag detects
3304+
// the selection being dragged over a different container.
3305+
if (potentialParentId !== dragStartParentId) {
3306+
executeBatchParentUpdate(
3307+
selectedNodes,
3308+
potentialParentId,
3309+
'Batch moved nodes to new parent'
3310+
)
3311+
}
32803312

32813313
// Clear drag start state
32823314
setDragStartPosition(null)
@@ -3687,6 +3719,20 @@ const WorkflowContent = React.memo(
36873719
const handleNodeClick = useCallback(
36883720
(event: React.MouseEvent, node: Node) => {
36893721
const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey
3722+
3723+
// Ignore shift-clicks on nodes at a different nesting level
3724+
if (isMultiSelect) {
3725+
const clickedContext = getNodeSelectionContextId(node, blocks)
3726+
const currentlySelected = getNodes().filter((n) => n.selected)
3727+
if (currentlySelected.length > 0) {
3728+
const selectionContext = getNodeSelectionContextId(currentlySelected[0], blocks)
3729+
if (clickedContext !== selectionContext) {
3730+
usePanelEditorStore.getState().clearCurrentBlock()
3731+
return
3732+
}
3733+
}
3734+
}
3735+
36903736
setDisplayNodes((currentNodes) => {
36913737
const updated = currentNodes.map((currentNode) => ({
36923738
...currentNode,
@@ -3699,7 +3745,7 @@ const WorkflowContent = React.memo(
36993745
return resolveSelectionConflicts(updated, blocks, isMultiSelect ? node.id : undefined)
37003746
})
37013747
},
3702-
[blocks]
3748+
[blocks, getNodes]
37033749
)
37043750

37053751
/** Handles edge selection with container context tracking and Shift-click multi-selection. */
@@ -3808,9 +3854,17 @@ const WorkflowContent = React.memo(
38083854
(targetNode?.zIndex ?? 21) + 1
38093855
)
38103856

3857+
// Edges inside subflows need a z-index above the container's body area
3858+
// (which has pointer-events: auto) so they're directly clickable.
3859+
// Derive from the container's depth-based zIndex (+1) so the edge sits
3860+
// just above its parent container but below canvas blocks (z-21+) and
3861+
// child blocks (z-1000).
3862+
const containerNode = parentLoopId ? nodeMap.get(parentLoopId) : null
3863+
const baseZIndex = containerNode ? (containerNode.zIndex ?? 0) + 1 : 0
3864+
38113865
return {
38123866
...edge,
3813-
zIndex: connectedToElevated ? elevatedZIndex : 0,
3867+
zIndex: connectedToElevated ? elevatedZIndex : baseZIndex,
38143868
data: {
38153869
...edge.data,
38163870
isSelected: selectedEdges.has(edgeContextId),

0 commit comments

Comments
 (0)