@@ -16,7 +16,7 @@ import type {
1616
1717export interface ConnectionHandle extends XYPosition , Dimensions {
1818 id : string | null
19- type : HandleType
19+ type : HandleType | null
2020 nodeId : string
2121}
2222
@@ -58,22 +58,54 @@ export function getHandles(
5858}
5959
6060export function getClosestHandle (
61+ event : MouseEvent | TouchEvent ,
62+ doc : Document | ShadowRoot ,
6163 pos : XYPosition ,
6264 connectionRadius : number ,
6365 handles : ConnectionHandle [ ] ,
64- validator : ( handle : ConnectionHandle | null ) => ValidHandleResult ,
66+ validator : ( handle : Pick < ConnectionHandle , 'nodeId' | 'id' | 'type' > ) => ValidHandleResult ,
6567) {
68+ // we always want to prioritize the handle below the mouse cursor over the closest distance handle,
69+ // because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
70+ const { x, y } = getEventPosition ( event )
71+ const domNodes = doc . elementsFromPoint ( x , y )
72+
73+ const handleBelow = domNodes . find ( ( el ) => el . classList . contains ( 'vue-flow__handle' ) )
74+
75+ if ( handleBelow ) {
76+ const handleNodeId = handleBelow . getAttribute ( 'data-nodeid' )
77+
78+ if ( handleNodeId ) {
79+ const handleType = getHandleType ( undefined , handleBelow )
80+ const handleId = handleBelow . getAttribute ( 'data-handleid' )
81+ const validHandleResult = validator ( { nodeId : handleNodeId , id : handleId , type : handleType } )
82+
83+ if ( validHandleResult ) {
84+ return {
85+ handle : {
86+ id : handleId ,
87+ type : handleType ,
88+ nodeId : handleNodeId ,
89+ x : pos . x ,
90+ y : pos . y ,
91+ } ,
92+ validHandleResult,
93+ }
94+ }
95+ }
96+ }
97+
98+ // if we couldn't find a handle below the mouse cursor we look for the closest distance based on the connectionRadius
6699 let closestHandles : { handle : ConnectionHandle ; validHandleResult : ValidHandleResult } [ ] = [ ]
67100 let minDistance = Infinity
68101
69102 handles . forEach ( ( handle ) => {
70- // calculate distance from mouse position to center of handle while considering handle width and height as well as x and y position
71- const distance = Math . sqrt ( ( handle . x - pos . x - handle . width / 2 ) ** 2 + ( handle . y - pos . y - handle . height / 2 ) ** 2 )
103+ const distance = Math . sqrt ( ( handle . x - pos . x ) ** 2 + ( handle . y - pos . y ) ** 2 )
72104
73105 if ( distance <= connectionRadius ) {
74106 const validHandleResult = validator ( handle )
75107
76- if ( distance <= minDistance && validHandleResult . isValid ) {
108+ if ( distance <= minDistance ) {
77109 if ( distance < minDistance ) {
78110 closestHandles = [ { handle, validHandleResult } ]
79111 } else if ( distance === minDistance ) {
@@ -90,13 +122,22 @@ export function getClosestHandle(
90122 } )
91123
92124 if ( ! closestHandles . length ) {
93- return { handle : null , validHandleResult : validator ( null ) }
125+ return { handle : null , validHandleResult : defaultValidHandleResult ( ) }
94126 }
95127
96- return closestHandles . length === 1
97- ? closestHandles [ 0 ]
98- : // if multiple handles are layout on top of each other we take the one with type = target because it's more likely that the user wants to connect to this one
99- closestHandles . find ( ( { handle } ) => handle . type === 'target' ) || closestHandles [ 0 ]
128+ if ( closestHandles . length === 1 ) {
129+ return closestHandles [ 0 ]
130+ }
131+
132+ const hasValidHandle = closestHandles . some ( ( { validHandleResult } ) => validHandleResult . isValid )
133+ const hasTargetHandle = closestHandles . some ( ( { handle } ) => handle . type === 'target' )
134+
135+ // if multiple handles are layouted on top of each other we prefer the one with type = target and the one that is valid
136+ return (
137+ closestHandles . find ( ( { handle, validHandleResult } ) =>
138+ hasTargetHandle ? handle . type === 'target' : hasValidHandle ? validHandleResult . isValid : true ,
139+ ) || closestHandles [ 0 ]
140+ )
100141}
101142
102143// checks if and returns connection in fom of an object { source: 123, target: 312 }
0 commit comments