fix(core): prioritize handle below during handle lookup

This commit is contained in:
braks
2023-07-10 19:20:26 +02:00
committed by Braks
parent 0b60cc3c3e
commit 245dd82574
3 changed files with 55 additions and 12 deletions
+3 -1
View File
@@ -136,6 +136,8 @@ export function useHandle({
connectionPosition = getEventPosition(event, containerBounds)
const { handle, validHandleResult } = getClosestHandle(
event,
doc,
pointToRendererPoint(connectionPosition, viewport.value, false, [1, 1]),
connectionRadius.value,
handleLookup,
@@ -154,7 +156,7 @@ export function useHandle({
),
)
closestHandle = handle || handleLookup.find((handle) => handle.id === validHandleResult.endHandle?.handleId) || null
closestHandle = handle
if (!autoPanStarted) {
autoPan()
+1 -1
View File
@@ -12,7 +12,7 @@ export interface HandleElement extends XYPosition, Dimensions {
export interface ConnectionHandle {
id: string | null
type: HandleType
type: HandleType | null
nodeId: string
x: number
y: number
+51 -10
View File
@@ -16,7 +16,7 @@ import type {
export interface ConnectionHandle extends XYPosition, Dimensions {
id: string | null
type: HandleType
type: HandleType | null
nodeId: string
}
@@ -58,22 +58,54 @@ export function getHandles(
}
export function getClosestHandle(
event: MouseEvent | TouchEvent,
doc: Document | ShadowRoot,
pos: XYPosition,
connectionRadius: number,
handles: ConnectionHandle[],
validator: (handle: ConnectionHandle | null) => ValidHandleResult,
validator: (handle: Pick<ConnectionHandle, 'nodeId' | 'id' | 'type'>) => ValidHandleResult,
) {
// we always want to prioritize the handle below the mouse cursor over the closest distance handle,
// because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
const { x, y } = getEventPosition(event)
const domNodes = doc.elementsFromPoint(x, y)
const handleBelow = domNodes.find((el) => el.classList.contains('vue-flow__handle'))
if (handleBelow) {
const handleNodeId = handleBelow.getAttribute('data-nodeid')
if (handleNodeId) {
const handleType = getHandleType(undefined, handleBelow)
const handleId = handleBelow.getAttribute('data-handleid')
const validHandleResult = validator({ nodeId: handleNodeId, id: handleId, type: handleType })
if (validHandleResult) {
return {
handle: {
id: handleId,
type: handleType,
nodeId: handleNodeId,
x: pos.x,
y: pos.y,
},
validHandleResult,
}
}
}
}
// if we couldn't find a handle below the mouse cursor we look for the closest distance based on the connectionRadius
let closestHandles: { handle: ConnectionHandle; validHandleResult: ValidHandleResult }[] = []
let minDistance = Infinity
handles.forEach((handle) => {
// calculate distance from mouse position to center of handle while considering handle width and height as well as x and y position
const distance = Math.sqrt((handle.x - pos.x - handle.width / 2) ** 2 + (handle.y - pos.y - handle.height / 2) ** 2)
const distance = Math.sqrt((handle.x - pos.x) ** 2 + (handle.y - pos.y) ** 2)
if (distance <= connectionRadius) {
const validHandleResult = validator(handle)
if (distance <= minDistance && validHandleResult.isValid) {
if (distance <= minDistance) {
if (distance < minDistance) {
closestHandles = [{ handle, validHandleResult }]
} else if (distance === minDistance) {
@@ -90,13 +122,22 @@ export function getClosestHandle(
})
if (!closestHandles.length) {
return { handle: null, validHandleResult: validator(null) }
return { handle: null, validHandleResult: defaultValidHandleResult() }
}
return closestHandles.length === 1
? closestHandles[0]
: // 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
closestHandles.find(({ handle }) => handle.type === 'target') || closestHandles[0]
if (closestHandles.length === 1) {
return closestHandles[0]
}
const hasValidHandle = closestHandles.some(({ validHandleResult }) => validHandleResult.isValid)
const hasTargetHandle = closestHandles.some(({ handle }) => handle.type === 'target')
// if multiple handles are layouted on top of each other we prefer the one with type = target and the one that is valid
return (
closestHandles.find(({ handle, validHandleResult }) =>
hasTargetHandle ? handle.type === 'target' : hasValidHandle ? validHandleResult.isValid : true,
) || closestHandles[0]
)
}
// checks if and returns connection in fom of an object { source: 123, target: 312 }