feat(core): do not snap to invalid handles

Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
braks
2023-02-01 22:57:05 +01:00
committed by Braks
parent 02952b8b6c
commit e0fbefef8f
5 changed files with 42 additions and 39 deletions
-8
View File
@@ -1,8 +0,0 @@
---
'@vue-flow/core': minor
---
Remove `isValidSourcePos` and `isValidTargetPos` options from nodes.
Use custom node components and pass the validator function to the handle yourself.
This option was deprecated and has now been removed.
+2 -2
View File
@@ -75,7 +75,7 @@ declare global {
const getEdgeId: typeof import('./utils/graph')['getEdgeId'] const getEdgeId: typeof import('./utils/graph')['getEdgeId']
const getEdgePositions: typeof import('./utils/edge')['getEdgePositions'] const getEdgePositions: typeof import('./utils/edge')['getEdgePositions']
const getEventHandlerParams: typeof import('./utils/drag')['getEventHandlerParams'] const getEventHandlerParams: typeof import('./utils/drag')['getEventHandlerParams']
const getEventPosition: typeof import('./utils/handle')['getEventPosition'] const getEventPosition: typeof import('./utils/general')['getEventPosition']
const getExtent: typeof import('./utils/drag')['getExtent'] const getExtent: typeof import('./utils/drag')['getExtent']
const getHandle: typeof import('./utils/edge')['getHandle'] const getHandle: typeof import('./utils/edge')['getHandle']
const getHandleBounds: typeof import('./utils/node')['getHandleBounds'] const getHandleBounds: typeof import('./utils/node')['getHandleBounds']
@@ -111,7 +111,7 @@ declare global {
const isGraphEdge: typeof import('./utils/graph')['isGraphEdge'] const isGraphEdge: typeof import('./utils/graph')['isGraphEdge']
const isGraphNode: typeof import('./utils/graph')['isGraphNode'] const isGraphNode: typeof import('./utils/graph')['isGraphNode']
const isInputDOMNode: typeof import('./composables/useKeyPress')['isInputDOMNode'] const isInputDOMNode: typeof import('./composables/useKeyPress')['isInputDOMNode']
const isMouseEvent: typeof import('./utils/handle')['isMouseEvent'] const isMouseEvent: typeof import('./utils/general')['isMouseEvent']
const isNode: typeof import('./utils/graph')['isNode'] const isNode: typeof import('./utils/graph')['isNode']
const isParentSelected: typeof import('./utils/graph')['isParentSelected'] const isParentSelected: typeof import('./utils/graph')['isParentSelected']
const isProxy: typeof import('vue')['isProxy'] const isProxy: typeof import('vue')['isProxy']
+26 -14
View File
@@ -11,6 +11,8 @@ interface UseHandleProps {
onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent) => void onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent) => void
} }
const alwaysValid = () => true
export default function useHandle({ export default function useHandle({
handleId: _handleId, handleId: _handleId,
nodeId: _nodeId, nodeId: _nodeId,
@@ -49,7 +51,13 @@ export default function useHandle({
// when vue-flow is used inside a shadow root we can't use document // when vue-flow is used inside a shadow root we can't use document
const doc = getHostForElement(event.target as HTMLElement) const doc = getHostForElement(event.target as HTMLElement)
const validConnectFunc = isValidConnection || (() => true) const node = findNode(unref(nodeId))
let validConnectFunc = isValidConnection || alwaysValid
if (!isValidConnection) {
if (node) validConnectFunc = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid
}
let prevClosestHandle: ConnectionHandle | null let prevClosestHandle: ConnectionHandle | null
@@ -96,8 +104,19 @@ export default function useHandle({
handleLookup, handleLookup,
) )
const { connection, handleDomNode, isValid } = isValidHandle(
event,
prevClosestHandle,
connectionMode.value,
nodeId,
handleId,
isTarget ? 'target' : 'source',
validConnectFunc,
doc,
)
updateConnection( updateConnection(
prevClosestHandle prevClosestHandle && isValid
? rendererPointToPoint( ? rendererPointToPoint(
{ {
x: prevClosestHandle.x, x: prevClosestHandle.x,
@@ -112,17 +131,6 @@ export default function useHandle({
return resetRecentHandle(prevActiveHandle) return resetRecentHandle(prevActiveHandle)
} }
const { connection, handleDomNode, isValid } = isValidHandle(
event,
prevClosestHandle,
connectionMode.value,
nodeId,
handleId,
isTarget ? 'target' : 'source',
validConnectFunc,
doc,
)
if (connection.source !== connection.target && handleDomNode) { if (connection.source !== connection.target && handleDomNode) {
resetRecentHandle(prevActiveHandle) resetRecentHandle(prevActiveHandle)
prevActiveHandle = handleDomNode prevActiveHandle = handleDomNode
@@ -179,10 +187,14 @@ export default function useHandle({
if (!connectionClickStartHandle.value) { if (!connectionClickStartHandle.value) {
startConnection({ nodeId: unref(nodeId), type: unref(type), handleId: unref(handleId) }, undefined, event, true) startConnection({ nodeId: unref(nodeId), type: unref(type), handleId: unref(handleId) }, undefined, event, true)
} else { } else {
const validConnectFunc: ValidConnectionFunc = isValidConnection ?? (() => true) let validConnectFunc = isValidConnection ?? alwaysValid
const node = findNode(unref(nodeId)) const node = findNode(unref(nodeId))
if (!isValidConnection) {
if (node) validConnectFunc = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid
}
if (node && (typeof node.connectable === 'undefined' ? nodesConnectable : node.connectable) === false) return if (node && (typeof node.connectable === 'undefined' ? nodesConnectable : node.connectable) === false) return
const doc = getHostForElement(event.target as HTMLElement) const doc = getHostForElement(event.target as HTMLElement)
+12
View File
@@ -0,0 +1,12 @@
export const isMouseEvent = (event: MouseEvent | TouchEvent): event is MouseEvent => 'clientX' in event
export const getEventPosition = (event: MouseEvent | TouchEvent, bounds?: DOMRect) => {
const isMouseTriggered = isMouseEvent(event)
const evtX = isMouseTriggered ? event.clientX : event.touches?.[0].clientX
const evtY = isMouseTriggered ? event.clientY : event.touches?.[0].clientY
return {
x: evtX - (bounds?.left ?? 0),
y: evtY - (bounds?.top ?? 0),
}
}
+2 -15
View File
@@ -14,19 +14,6 @@ export function resetRecentHandle(handleDomNode: Element): void {
handleDomNode?.classList.remove('vue-flow__handle-connecting') handleDomNode?.classList.remove('vue-flow__handle-connecting')
} }
export const isMouseEvent = (event: MouseEvent | TouchEvent): event is MouseEvent => 'clientX' in event
export const getEventPosition = (event: MouseEvent | TouchEvent, bounds?: DOMRect) => {
const isMouseTriggered = isMouseEvent(event)
const evtX = isMouseTriggered ? event.clientX : event.touches?.[0].clientX
const evtY = isMouseTriggered ? event.clientY : event.touches?.[0].clientY
return {
x: evtX - (bounds?.left ?? 0),
y: evtY - (bounds?.top ?? 0),
}
}
// this functions collects all handles and adds an absolute position // this functions collects all handles and adds an absolute position
// so that we can later find the closest handle to the mouse position // so that we can later find the closest handle to the mouse position
export function getHandles( export function getHandles(
@@ -77,7 +64,7 @@ interface Result {
// 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 }
export function isValidHandle( export function isValidHandle(
event: MouseEvent | TouchEvent, event: MouseEvent | TouchEvent,
handle: Pick<ConnectionHandle, 'nodeId' | 'id' | 'type'>, handle: Pick<ConnectionHandle, 'nodeId' | 'id' | 'type'> | null,
connectionMode: ConnectionMode, connectionMode: ConnectionMode,
fromNodeId: string, fromNodeId: string,
fromHandleId: string | null, fromHandleId: string | null,
@@ -100,7 +87,7 @@ export function isValidHandle(
connection: { source: '', target: '', sourceHandle: null, targetHandle: null }, connection: { source: '', target: '', sourceHandle: null, targetHandle: null },
} }
if (handleDomNode) { if (handleDomNode && handle) {
const handleNodeId = handleDomNode.getAttribute('data-nodeid') const handleNodeId = handleDomNode.getAttribute('data-nodeid')
const handleId = handleDomNode.getAttribute('data-handleid') const handleId = handleDomNode.getAttribute('data-handleid')