Skip to content

Commit e42897e

Browse files
committed
fix(core): filter closest handles by validity
1 parent b9c2ca9 commit e42897e

File tree

2 files changed

+51
-47
lines changed

2 files changed

+51
-47
lines changed

packages/core/src/composables/useHandle.ts

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { MaybeRefOrGetter } from '@vueuse/core'
22
import { toValue } from '@vueuse/core'
33
import { useVueFlow } from './useVueFlow'
4-
import type { Connection, ConnectionHandle, HandleType, MouseTouchEvent, ValidConnectionFunc, ValidHandleResult } from '~/types'
4+
import type { Connection, ConnectionHandle, HandleType, MouseTouchEvent, ValidConnectionFunc } from '~/types'
55
import {
66
calcAutoPan,
77
getClosestHandle,
@@ -135,20 +135,12 @@ export function useHandle({
135135
function onPointerMove(event: MouseTouchEvent) {
136136
connectionPosition = getEventPosition(event, containerBounds)
137137

138-
let result: ValidHandleResult | null = {
139-
handleDomNode: null,
140-
isValid: false,
141-
connection: { source: '', target: '', sourceHandle: null, targetHandle: null },
142-
endHandle: null,
143-
}
144-
145-
closestHandle =
146-
getClosestHandle(
147-
pointToRendererPoint(connectionPosition, viewport.value, false, [1, 1]),
148-
connectionRadius.value,
149-
handleLookup,
150-
)?.find((handle) => {
151-
const validHandleResult = isValidHandle(
138+
const { handle, validHandleResult } = getClosestHandle(
139+
pointToRendererPoint(connectionPosition, viewport.value, false, [1, 1]),
140+
connectionRadius.value,
141+
handleLookup,
142+
(handle) =>
143+
isValidHandle(
152144
event,
153145
handle,
154146
connectionMode.value,
@@ -159,27 +151,19 @@ export function useHandle({
159151
doc,
160152
edges.value,
161153
findNode,
162-
)
163-
164-
if (validHandleResult.isValid) {
165-
result = validHandleResult
166-
}
154+
),
155+
)
167156

168-
return validHandleResult.isValid
169-
}) || null
157+
closestHandle = handle
170158

171159
if (!autoPanStarted) {
172160
autoPan()
173161
autoPanStarted = true
174162
}
175163

176-
if (!result) {
177-
return
178-
}
179-
180-
connection = result.connection
181-
isValid = result.isValid
182-
handleDomNode = result.handleDomNode
164+
connection = validHandleResult.connection
165+
isValid = validHandleResult.isValid
166+
handleDomNode = validHandleResult.handleDomNode
183167

184168
updateConnection(
185169
closestHandle && isValid
@@ -191,7 +175,7 @@ export function useHandle({
191175
viewport.value,
192176
)
193177
: connectionPosition,
194-
result.endHandle,
178+
validHandleResult.endHandle,
195179
getConnectionStatus(!!closestHandle, isValid),
196180
)
197181

packages/core/src/utils/handle.ts

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ export interface ConnectionHandle {
2121
y: number
2222
}
2323

24+
function defaultValidHandleResult(): ValidHandleResult {
25+
return {
26+
handleDomNode: null,
27+
isValid: false,
28+
connection: { source: '', target: '', sourceHandle: null, targetHandle: null },
29+
endHandle: null,
30+
}
31+
}
32+
2433
export function resetRecentHandle(handleDomNode: Element): void {
2534
handleDomNode?.classList.remove('valid', 'connecting', 'vue-flow__handle-valid', 'vue-flow__handle-connecting')
2635
}
@@ -53,29 +62,45 @@ export function getHandles(
5362
}, [])
5463
}
5564

56-
export function getClosestHandle(pos: XYPosition, connectionRadius: number, handles: ConnectionHandle[]) {
57-
let closestHandles: ConnectionHandle[] = []
65+
export function getClosestHandle(
66+
pos: XYPosition,
67+
connectionRadius: number,
68+
handles: ConnectionHandle[],
69+
validator: (handle: ConnectionHandle) => ValidHandleResult,
70+
) {
71+
let closestHandles: { handle: ConnectionHandle; validHandleResult: ValidHandleResult }[] = []
5872
let minDistance = Infinity
5973

6074
handles.forEach((handle) => {
6175
const distance = Math.sqrt((handle.x - pos.x) ** 2 + (handle.y - pos.y) ** 2)
76+
6277
if (distance <= connectionRadius) {
63-
if (distance < minDistance) {
64-
closestHandles = [handle]
65-
} else if (distance === minDistance) {
66-
// when multiple handles are on the same distance we collect all of them
67-
closestHandles.push(handle)
78+
const validHandleResult = validator(handle)
79+
80+
if (distance <= minDistance && validHandleResult.isValid) {
81+
if (distance < minDistance) {
82+
closestHandles = [{ handle, validHandleResult }]
83+
} else if (distance === minDistance) {
84+
// when multiple handles are on the same distance we collect all of them
85+
closestHandles.push({
86+
handle,
87+
validHandleResult,
88+
})
89+
}
90+
91+
minDistance = distance
6892
}
69-
70-
minDistance = distance
7193
}
7294
})
7395

7496
if (!closestHandles.length) {
75-
return null
97+
return { handle: null, validHandleResult: defaultValidHandleResult() }
7698
}
7799

78-
return closestHandles
100+
return closestHandles.length === 1
101+
? closestHandles[0]
102+
: // 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
103+
closestHandles.find(({ handle }) => handle.type === 'target') || closestHandles[0]
79104
}
80105

81106
// checks if and returns connection in fom of an object { source: 123, target: 312 }
@@ -101,12 +126,7 @@ export function isValidHandle(
101126
// because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
102127
const handleToCheck = handleBelow?.classList.contains('vue-flow__handle') ? handleBelow : handleDomNode
103128

104-
const result: ValidHandleResult = {
105-
handleDomNode: handleToCheck,
106-
isValid: false,
107-
connection: { source: '', target: '', sourceHandle: null, targetHandle: null },
108-
endHandle: null,
109-
}
129+
const result = defaultValidHandleResult()
110130

111131
if (handleToCheck) {
112132
const handleType = getHandleType(undefined, handleToCheck)

0 commit comments

Comments
 (0)