From 245dd825749166271f374de692838e263fde7944 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:52:05 +0200 Subject: [PATCH] fix(core): prioritize handle below during handle lookup --- packages/core/src/composables/useHandle.ts | 4 +- packages/core/src/types/handle.ts | 2 +- packages/core/src/utils/handle.ts | 61 ++++++++++++++++++---- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/packages/core/src/composables/useHandle.ts b/packages/core/src/composables/useHandle.ts index acbdcb2f..2e3ea7a9 100644 --- a/packages/core/src/composables/useHandle.ts +++ b/packages/core/src/composables/useHandle.ts @@ -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() diff --git a/packages/core/src/types/handle.ts b/packages/core/src/types/handle.ts index d89c376c..f4368b99 100644 --- a/packages/core/src/types/handle.ts +++ b/packages/core/src/types/handle.ts @@ -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 diff --git a/packages/core/src/utils/handle.ts b/packages/core/src/utils/handle.ts index 4c86b531..f0eee719 100644 --- a/packages/core/src/utils/handle.ts +++ b/packages/core/src/utils/handle.ts @@ -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) => 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 }