fix(core): filter closest handles by validity
This commit is contained in:
@@ -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),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user