diff --git a/.changeset/cuddly-fans-think.md b/.changeset/cuddly-fans-think.md deleted file mode 100644 index 072a1898..00000000 --- a/.changeset/cuddly-fans-think.md +++ /dev/null @@ -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. diff --git a/packages/core/src/auto-imports.d.ts b/packages/core/src/auto-imports.d.ts index f8c013b5..497b319c 100644 --- a/packages/core/src/auto-imports.d.ts +++ b/packages/core/src/auto-imports.d.ts @@ -75,7 +75,7 @@ declare global { const getEdgeId: typeof import('./utils/graph')['getEdgeId'] const getEdgePositions: typeof import('./utils/edge')['getEdgePositions'] 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 getHandle: typeof import('./utils/edge')['getHandle'] const getHandleBounds: typeof import('./utils/node')['getHandleBounds'] @@ -111,7 +111,7 @@ declare global { const isGraphEdge: typeof import('./utils/graph')['isGraphEdge'] const isGraphNode: typeof import('./utils/graph')['isGraphNode'] 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 isParentSelected: typeof import('./utils/graph')['isParentSelected'] const isProxy: typeof import('vue')['isProxy'] diff --git a/packages/core/src/composables/useHandle.ts b/packages/core/src/composables/useHandle.ts index b9447710..6c0f1fe7 100644 --- a/packages/core/src/composables/useHandle.ts +++ b/packages/core/src/composables/useHandle.ts @@ -11,6 +11,8 @@ interface UseHandleProps { onEdgeUpdateEnd?: (event: MouseEvent | TouchEvent) => void } +const alwaysValid = () => true + export default function useHandle({ handleId: _handleId, nodeId: _nodeId, @@ -49,7 +51,13 @@ export default function useHandle({ // when vue-flow is used inside a shadow root we can't use document 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 @@ -96,8 +104,19 @@ export default function useHandle({ handleLookup, ) + const { connection, handleDomNode, isValid } = isValidHandle( + event, + prevClosestHandle, + connectionMode.value, + nodeId, + handleId, + isTarget ? 'target' : 'source', + validConnectFunc, + doc, + ) + updateConnection( - prevClosestHandle + prevClosestHandle && isValid ? rendererPointToPoint( { x: prevClosestHandle.x, @@ -112,17 +131,6 @@ export default function useHandle({ 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) { resetRecentHandle(prevActiveHandle) prevActiveHandle = handleDomNode @@ -179,10 +187,14 @@ export default function useHandle({ if (!connectionClickStartHandle.value) { startConnection({ nodeId: unref(nodeId), type: unref(type), handleId: unref(handleId) }, undefined, event, true) } else { - const validConnectFunc: ValidConnectionFunc = isValidConnection ?? (() => true) + let validConnectFunc = isValidConnection ?? alwaysValid 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 const doc = getHostForElement(event.target as HTMLElement) diff --git a/packages/core/src/utils/general.ts b/packages/core/src/utils/general.ts new file mode 100644 index 00000000..3f5d46ec --- /dev/null +++ b/packages/core/src/utils/general.ts @@ -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), + } +} diff --git a/packages/core/src/utils/handle.ts b/packages/core/src/utils/handle.ts index 1a432b6c..e2884376 100644 --- a/packages/core/src/utils/handle.ts +++ b/packages/core/src/utils/handle.ts @@ -14,19 +14,6 @@ export function resetRecentHandle(handleDomNode: Element): void { 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 // so that we can later find the closest handle to the mouse position export function getHandles( @@ -77,7 +64,7 @@ interface Result { // checks if and returns connection in fom of an object { source: 123, target: 312 } export function isValidHandle( event: MouseEvent | TouchEvent, - handle: Pick, + handle: Pick | null, connectionMode: ConnectionMode, fromNodeId: string, fromHandleId: string | null, @@ -100,7 +87,7 @@ export function isValidHandle( connection: { source: '', target: '', sourceHandle: null, targetHandle: null }, } - if (handleDomNode) { + if (handleDomNode && handle) { const handleNodeId = handleDomNode.getAttribute('data-nodeid') const handleId = handleDomNode.getAttribute('data-handleid')