Skip to content

Commit d969652

Browse files
committed
improvement(canvas): cancel an in-flight edge drag with Escape
1 parent cc7f005 commit d969652

1 file changed

Lines changed: 43 additions & 3 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/w/[workflowId]

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

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,9 @@ const WorkflowContent = React.memo(
600600
/** Tracks whether onConnect successfully handled the connection (ReactFlow pattern). */
601601
const connectionCompletedRef = useRef(false)
602602

603+
/** Set when Escape aborts an in-flight connection drag so no edge or selector results. */
604+
const connectionCancelledRef = useRef(false)
605+
603606
/** Stores start positions for multi-node drag undo/redo recording. */
604607
const multiNodeDragStartRef = useRef<Map<string, { x: number; y: number; parentId?: string }>>(
605608
new Map()
@@ -3214,6 +3217,32 @@ const WorkflowContent = React.memo(
32143217
[getNodes]
32153218
)
32163219

3220+
/**
3221+
* Aborts an in-flight connection drag on Escape.
3222+
*
3223+
* React Flow only tears a handle drag down on pointer release, so a synthetic
3224+
* mouseup is dispatched to run its own cleanup: it stops auto-panning, clears
3225+
* the connection line and handle highlights, and detaches its document
3226+
* listeners. `connectionCancelledRef` turns the resulting `onConnect` and
3227+
* `onConnectEnd` into no-ops so the drag leaves behind neither an edge nor the
3228+
* block selector, and the real mouseup that follows is inert.
3229+
*
3230+
* Listens in the capture phase and stops propagation so Escape mid-drag only
3231+
* cancels the edge and never reaches an unrelated Escape handler.
3232+
*/
3233+
const handleConnectionEscape = useCallback((event: KeyboardEvent) => {
3234+
if (event.key !== 'Escape' || !connectionSourceRef.current) return
3235+
event.preventDefault()
3236+
event.stopPropagation()
3237+
connectionCancelledRef.current = true
3238+
document.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }))
3239+
}, [])
3240+
3241+
useEffect(
3242+
() => () => window.removeEventListener('keydown', handleConnectionEscape, true),
3243+
[handleConnectionEscape]
3244+
)
3245+
32173246
/**
32183247
* Captures the source handle when a connection drag starts.
32193248
* Resets connectionCompletedRef to track if onConnect handles this connection.
@@ -3232,13 +3261,16 @@ const WorkflowContent = React.memo(
32323261
handleId: params?.handleId,
32333262
}
32343263
connectionCompletedRef.current = false
3264+
connectionCancelledRef.current = false
3265+
window.addEventListener('keydown', handleConnectionEscape, true)
32353266
},
3236-
[closeConnectionBlockSelector]
3267+
[closeConnectionBlockSelector, handleConnectionEscape]
32373268
)
32383269

32393270
/** Handles new edge connections with container boundary validation. */
32403271
const onConnect = useCallback(
32413272
(connection: any) => {
3273+
if (connectionCancelledRef.current) return
32423274
if (connection.source && connection.target) {
32433275
const normalizedConnection = {
32443276
...connection,
@@ -3336,11 +3368,12 @@ const WorkflowContent = React.memo(
33363368
*/
33373369
const onConnectEnd = useCallback(
33383370
(event: MouseEvent | TouchEvent) => {
3371+
window.removeEventListener('keydown', handleConnectionEscape, true)
33393372
canvasContainerRef.current?.setAttribute('data-connection-line', 'default')
33403373
canvasContainerRef.current?.setAttribute('data-connection-active', 'false')
33413374

33423375
const source = connectionSourceRef.current
3343-
if (!source?.nodeId) {
3376+
if (!source?.nodeId || connectionCancelledRef.current) {
33443377
connectionSourceRef.current = null
33453378
return
33463379
}
@@ -3417,7 +3450,14 @@ const WorkflowContent = React.memo(
34173450

34183451
connectionSourceRef.current = null
34193452
},
3420-
[findNodeAtScreenPosition, onConnect, blocks, reactFlowInstance, screenToFlowPosition]
3453+
[
3454+
findNodeAtScreenPosition,
3455+
onConnect,
3456+
blocks,
3457+
reactFlowInstance,
3458+
screenToFlowPosition,
3459+
handleConnectionEscape,
3460+
]
34213461
)
34223462

34233463
/** Handles node drag to detect container intersections and update highlighting. */

0 commit comments

Comments
 (0)