mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
improvement(edges): drag edge over block (#2596)
* drag edge over block * fix minor stale closure --------- Co-authored-by: aadamgough <adam@sim.ai> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
This commit is contained in:
@@ -248,6 +248,9 @@ const WorkflowContent = React.memo(() => {
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** Stores source node/handle info when a connection drag starts for drop-on-block detection. */
|
||||||
|
const connectionSourceRef = useRef<{ nodeId: string; handleId: string } | null>(null)
|
||||||
|
|
||||||
/** Re-applies diff markers when blocks change after socket rehydration. */
|
/** Re-applies diff markers when blocks change after socket rehydration. */
|
||||||
const blocksRef = useRef(blocks)
|
const blocksRef = useRef(blocks)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1702,6 +1705,32 @@ const WorkflowContent = React.memo(() => {
|
|||||||
[removeEdge]
|
[removeEdge]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a node at a given flow position for drop-on-block connection.
|
||||||
|
* Skips subflow containers as they have their own connection logic.
|
||||||
|
*/
|
||||||
|
const findNodeAtPosition = useCallback(
|
||||||
|
(position: { x: number; y: number }) => {
|
||||||
|
const nodes = getNodes()
|
||||||
|
|
||||||
|
return nodes.find((node) => {
|
||||||
|
// Skip subflow containers for drop targets
|
||||||
|
if (node.type === 'subflowNode') return false
|
||||||
|
|
||||||
|
const absPos = getNodeAbsolutePosition(node.id)
|
||||||
|
const dims = getBlockDimensions(node.id)
|
||||||
|
|
||||||
|
return (
|
||||||
|
position.x >= absPos.x &&
|
||||||
|
position.x <= absPos.x + dims.width &&
|
||||||
|
position.y >= absPos.y &&
|
||||||
|
position.y <= absPos.y + dims.height
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[getNodes, getNodeAbsolutePosition, getBlockDimensions]
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Captures the source handle when a connection drag starts
|
* Captures the source handle when a connection drag starts
|
||||||
*/
|
*/
|
||||||
@@ -1709,13 +1738,10 @@ const WorkflowContent = React.memo(() => {
|
|||||||
const handleId: string | undefined = params?.handleId
|
const handleId: string | undefined = params?.handleId
|
||||||
// Treat explicit error handle (id === 'error') as error connection
|
// Treat explicit error handle (id === 'error') as error connection
|
||||||
setIsErrorConnectionDrag(handleId === 'error')
|
setIsErrorConnectionDrag(handleId === 'error')
|
||||||
}, [])
|
connectionSourceRef.current = {
|
||||||
|
nodeId: params?.nodeId,
|
||||||
/**
|
handleId: params?.handleId,
|
||||||
* Resets the source handle when connection drag ends
|
}
|
||||||
*/
|
|
||||||
const onConnectEnd = useCallback(() => {
|
|
||||||
setIsErrorConnectionDrag(false)
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/** Handles new edge connections with container boundary validation. */
|
/** Handles new edge connections with container boundary validation. */
|
||||||
@@ -1806,7 +1832,46 @@ const WorkflowContent = React.memo(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[addEdge, getNodes]
|
[addEdge, getNodes, blocks]
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles connection drag end. Detects if the edge was dropped over a block
|
||||||
|
* and automatically creates a connection to that block's target handle.
|
||||||
|
*/
|
||||||
|
const onConnectEnd = useCallback(
|
||||||
|
(event: MouseEvent | TouchEvent) => {
|
||||||
|
setIsErrorConnectionDrag(false)
|
||||||
|
|
||||||
|
const source = connectionSourceRef.current
|
||||||
|
if (!source?.nodeId) {
|
||||||
|
connectionSourceRef.current = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cursor position in flow coordinates
|
||||||
|
const clientPos = 'changedTouches' in event ? event.changedTouches[0] : event
|
||||||
|
const flowPosition = screenToFlowPosition({
|
||||||
|
x: clientPos.clientX,
|
||||||
|
y: clientPos.clientY,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Find node under cursor
|
||||||
|
const targetNode = findNodeAtPosition(flowPosition)
|
||||||
|
|
||||||
|
// Create connection if valid target found
|
||||||
|
if (targetNode && targetNode.id !== source.nodeId) {
|
||||||
|
onConnect({
|
||||||
|
source: source.nodeId,
|
||||||
|
sourceHandle: source.handleId,
|
||||||
|
target: targetNode.id,
|
||||||
|
targetHandle: 'target',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionSourceRef.current = null
|
||||||
|
},
|
||||||
|
[screenToFlowPosition, findNodeAtPosition, onConnect]
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Handles node drag to detect container intersections and update highlighting. */
|
/** Handles node drag to detect container intersections and update highlighting. */
|
||||||
|
|||||||
Reference in New Issue
Block a user