fix(core): filter closest handles by validity

This commit is contained in:
braks
2023-05-25 20:48:26 +02:00
committed by Braks
parent b9c2ca9141
commit e42897e3ed
2 changed files with 52 additions and 48 deletions
+14 -30
View File
@@ -1,7 +1,7 @@
import type { MaybeRefOrGetter } from '@vueuse/core' import type { MaybeRefOrGetter } from '@vueuse/core'
import { toValue } from '@vueuse/core' import { toValue } from '@vueuse/core'
import { useVueFlow } from './useVueFlow' import { useVueFlow } from './useVueFlow'
import type { Connection, ConnectionHandle, HandleType, MouseTouchEvent, ValidConnectionFunc, ValidHandleResult } from '~/types' import type { Connection, ConnectionHandle, HandleType, MouseTouchEvent, ValidConnectionFunc } from '~/types'
import { import {
calcAutoPan, calcAutoPan,
getClosestHandle, getClosestHandle,
@@ -135,20 +135,12 @@ export function useHandle({
function onPointerMove(event: MouseTouchEvent) { function onPointerMove(event: MouseTouchEvent) {
connectionPosition = getEventPosition(event, containerBounds) connectionPosition = getEventPosition(event, containerBounds)
let result: ValidHandleResult | null = { const { handle, validHandleResult } = getClosestHandle(
handleDomNode: null, pointToRendererPoint(connectionPosition, viewport.value, false, [1, 1]),
isValid: false, connectionRadius.value,
connection: { source: '', target: '', sourceHandle: null, targetHandle: null }, handleLookup,
endHandle: null, (handle) =>
} isValidHandle(
closestHandle =
getClosestHandle(
pointToRendererPoint(connectionPosition, viewport.value, false, [1, 1]),
connectionRadius.value,
handleLookup,
)?.find((handle) => {
const validHandleResult = isValidHandle(
event, event,
handle, handle,
connectionMode.value, connectionMode.value,
@@ -159,27 +151,19 @@ export function useHandle({
doc, doc,
edges.value, edges.value,
findNode, findNode,
) ),
)
if (validHandleResult.isValid) { closestHandle = handle
result = validHandleResult
}
return validHandleResult.isValid
}) || null
if (!autoPanStarted) { if (!autoPanStarted) {
autoPan() autoPan()
autoPanStarted = true autoPanStarted = true
} }
if (!result) { connection = validHandleResult.connection
return isValid = validHandleResult.isValid
} handleDomNode = validHandleResult.handleDomNode
connection = result.connection
isValid = result.isValid
handleDomNode = result.handleDomNode
updateConnection( updateConnection(
closestHandle && isValid closestHandle && isValid
@@ -191,7 +175,7 @@ export function useHandle({
viewport.value, viewport.value,
) )
: connectionPosition, : connectionPosition,
result.endHandle, validHandleResult.endHandle,
getConnectionStatus(!!closestHandle, isValid), getConnectionStatus(!!closestHandle, isValid),
) )
+38 -18
View File
@@ -21,6 +21,15 @@ export interface ConnectionHandle {
y: number y: number
} }
function defaultValidHandleResult(): ValidHandleResult {
return {
handleDomNode: null,
isValid: false,
connection: { source: '', target: '', sourceHandle: null, targetHandle: null },
endHandle: null,
}
}
export function resetRecentHandle(handleDomNode: Element): void { export function resetRecentHandle(handleDomNode: Element): void {
handleDomNode?.classList.remove('valid', 'connecting', 'vue-flow__handle-valid', 'vue-flow__handle-connecting') handleDomNode?.classList.remove('valid', 'connecting', 'vue-flow__handle-valid', 'vue-flow__handle-connecting')
} }
@@ -53,29 +62,45 @@ export function getHandles(
}, []) }, [])
} }
export function getClosestHandle(pos: XYPosition, connectionRadius: number, handles: ConnectionHandle[]) { export function getClosestHandle(
let closestHandles: ConnectionHandle[] = [] pos: XYPosition,
connectionRadius: number,
handles: ConnectionHandle[],
validator: (handle: ConnectionHandle) => ValidHandleResult,
) {
let closestHandles: { handle: ConnectionHandle; validHandleResult: ValidHandleResult }[] = []
let minDistance = Infinity let minDistance = Infinity
handles.forEach((handle) => { handles.forEach((handle) => {
const distance = Math.sqrt((handle.x - pos.x) ** 2 + (handle.y - pos.y) ** 2) const distance = Math.sqrt((handle.x - pos.x) ** 2 + (handle.y - pos.y) ** 2)
if (distance <= connectionRadius) {
if (distance < minDistance) {
closestHandles = [handle]
} else if (distance === minDistance) {
// when multiple handles are on the same distance we collect all of them
closestHandles.push(handle)
}
minDistance = distance if (distance <= connectionRadius) {
const validHandleResult = validator(handle)
if (distance <= minDistance && validHandleResult.isValid) {
if (distance < minDistance) {
closestHandles = [{ handle, validHandleResult }]
} else if (distance === minDistance) {
// when multiple handles are on the same distance we collect all of them
closestHandles.push({
handle,
validHandleResult,
})
}
minDistance = distance
}
} }
}) })
if (!closestHandles.length) { if (!closestHandles.length) {
return null return { handle: null, validHandleResult: defaultValidHandleResult() }
} }
return closestHandles 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]
} }
// checks if and returns connection in fom of an object { source: 123, target: 312 } // checks if and returns connection in fom of an object { source: 123, target: 312 }
@@ -101,12 +126,7 @@ export function isValidHandle(
// because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor // because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
const handleToCheck = handleBelow?.classList.contains('vue-flow__handle') ? handleBelow : handleDomNode const handleToCheck = handleBelow?.classList.contains('vue-flow__handle') ? handleBelow : handleDomNode
const result: ValidHandleResult = { const result = defaultValidHandleResult()
handleDomNode: handleToCheck,
isValid: false,
connection: { source: '', target: '', sourceHandle: null, targetHandle: null },
endHandle: null,
}
if (handleToCheck) { if (handleToCheck) {
const handleType = getHandleType(undefined, handleToCheck) const handleType = getHandleType(undefined, handleToCheck)