Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,6 @@ const edgeTypes: EdgeTypes = {
const defaultEdgeOptions = { type: 'custom' }

const reactFlowStyles = [
'[&_.react-flow__edges]:!z-0',
'[&_.react-flow__node]:z-[21]',
'[&_.react-flow__handle]:!z-[30]',
'[&_.react-flow__edge-labels]:!z-[1001]',
'[&_.react-flow__pane]:select-none',
Expand Down Expand Up @@ -2478,6 +2476,7 @@ const WorkflowContent = React.memo(

// Local state for nodes - allows smooth drag without store updates on every frame
const [displayNodes, setDisplayNodes] = useState<Node[]>([])
const [lastInteractedNodeId, setLastInteractedNodeId] = useState<string | null>(null)

const selectedNodeIds = useMemo(
() => displayNodes.filter((node) => node.selected).map((node) => node.id),
Expand All @@ -2489,6 +2488,14 @@ const WorkflowContent = React.memo(
syncPanelWithSelection(selectedNodeIds)
}, [selectedNodeIdsKey])

// Keep the most recently selected block on top even after deselection, so a
// dragged block doesn't suddenly drop behind other overlapping blocks.
useEffect(() => {
if (selectedNodeIds.length > 0) {
setLastInteractedNodeId(selectedNodeIds[selectedNodeIds.length - 1])
}
}, [selectedNodeIdsKey])
Comment thread
waleedlatif1 marked this conversation as resolved.

useEffect(() => {
// Check for pending selection (from paste/duplicate), otherwise preserve existing selection
if (pendingSelection && pendingSelection.length > 0) {
Expand Down Expand Up @@ -3723,18 +3730,54 @@ const WorkflowContent = React.memo(
[removeEdge, edges, blocks, addNotification, activeWorkflowId]
)

// Elevate nodes using React Flow's native zIndex so selected/recent blocks
// always sit above edges and other blocks.
//
// Z-index layers (regular blocks):
// 21 — default
// 22 — last interacted (dragged/selected, now deselected) so it stays on
// top of siblings until another block is touched
// 31 — currently selected (above connected edges at z-22 and handles at z-30)
//
// Subflow container nodes are skipped — they use depth-based zIndex for
// correct parent/child layering and must not be bumped.
// Child blocks inside containers already carry zIndex 1000 and are bumped by
// +10 when selected so they stay above their sibling child blocks.
const nodesForRender = useMemo(() => {
return displayNodes.map((node) => {
if (node.type === 'subflowNode') return node
const base = node.zIndex ?? 21
const target = node.selected
? base + 10
: node.id === lastInteractedNodeId
? Math.max(base, 22)
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
: base
if (target === node.zIndex) return node
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
return { ...node, zIndex: target }
})
}, [displayNodes, lastInteractedNodeId])

/** Transforms edges to include selection state and delete handlers. Memoized to prevent re-renders. */
const edgesWithSelection = useMemo(() => {
const nodeMap = new Map(displayNodes.map((n) => [n.id, n]))
Comment thread
waleedlatif1 marked this conversation as resolved.
const elevatedNodeIdSet = new Set(
lastInteractedNodeId ? [...selectedNodeIds, lastInteractedNodeId] : selectedNodeIds
)

return edgesForDisplay.map((edge) => {
const sourceNode = nodeMap.get(edge.source)
const targetNode = nodeMap.get(edge.target)
const parentLoopId = sourceNode?.parentId || targetNode?.parentId
const edgeContextId = `${edge.id}${parentLoopId ? `-${parentLoopId}` : ''}`
const connectedToElevated =
elevatedNodeIdSet.has(edge.source) || elevatedNodeIdSet.has(edge.target)
// Derive elevated z-index from connected nodes so edges inside subflows
// (child nodes at z-1000) stay above their sibling child blocks.
const elevatedZIndex = Math.max(22, sourceNode?.zIndex ?? 21, targetNode?.zIndex ?? 21)
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated

return {
...edge,
zIndex: connectedToElevated ? elevatedZIndex : 0,
data: {
...edge.data,
isSelected: selectedEdges.has(edgeContextId),
Expand All @@ -3745,7 +3788,14 @@ const WorkflowContent = React.memo(
},
}
})
}, [edgesForDisplay, displayNodes, selectedEdges, handleEdgeDelete])
}, [
edgesForDisplay,
displayNodes,
selectedNodeIds,
selectedEdges,
handleEdgeDelete,
lastInteractedNodeId,
])

/** Handles Delete/Backspace to remove selected edges or blocks. */
useEffect(() => {
Expand Down Expand Up @@ -3885,7 +3935,7 @@ const WorkflowContent = React.memo(
{showTrainingModal && <TrainingModal />}

<ReactFlow
nodes={displayNodes}
nodes={nodesForRender}
edges={edgesWithSelection}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
Expand Down Expand Up @@ -3952,7 +4002,7 @@ const WorkflowContent = React.memo(
onNodeDragStart={effectivePermissions.canEdit ? onNodeDragStart : undefined}
snapToGrid={snapToGrid}
snapGrid={snapGrid}
elevateEdgesOnSelect={true}
elevateEdgesOnSelect={false}
onlyRenderVisibleElements={false}
deleteKeyCode={null}
elevateNodesOnSelect={false}
Expand Down
Loading