diff --git a/packages/vue-flow/src/composables/useHandle.ts b/packages/vue-flow/src/composables/useHandle.ts index 90d16c6f..b98fc48d 100644 --- a/packages/vue-flow/src/composables/useHandle.ts +++ b/packages/vue-flow/src/composables/useHandle.ts @@ -78,11 +78,14 @@ export default () => { connectOnClick, nodesConnectable, connectionStartHandle, - connectionPosition, connectionMode, emits, + startConnection, + updateConnection, + endConnection, setState, getNode, + vueFlowRef, } = $(useVueFlow()) let recentHoveredHandle: Element @@ -97,8 +100,6 @@ export default () => { onEdgeUpdate?: (connection: Connection) => void, onEdgeUpdateEnd?: () => void, ) => { - const flowNode = (event.target as Element).closest('.vue-flow') - const doc = getHostForElement(event.target as HTMLElement) if (!doc) return @@ -116,13 +117,13 @@ export default () => { const elementBelowIsTarget = elementBelow?.classList.contains('target') const elementBelowIsSource = elementBelow?.classList.contains('source') - if (!flowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) return + if (!vueFlowRef || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) return const handleType = elementEdgeUpdaterType ?? (elementBelowIsTarget ? 'target' : 'source') - const containerBounds = flowNode.getBoundingClientRect() + const containerBounds = vueFlowRef.getBoundingClientRect() - setState({ + startConnection({ connectionPosition: { x: event.clientX - containerBounds.left, y: event.clientY - containerBounds.top, @@ -135,8 +136,10 @@ export default () => { emits.connectStart({ event, nodeId, handleId, handleType }) function onMouseMove(event: MouseEvent) { - connectionPosition.x = event.clientX - containerBounds.left - connectionPosition.y = event.clientY - containerBounds.top + updateConnection({ + x: event.clientX - containerBounds.left, + y: event.clientY - containerBounds.top, + }) const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid( event, @@ -189,12 +192,7 @@ export default () => { resetRecentHandle(recentHoveredHandle) - setState({ - connectionNodeId: null, - connectionHandleId: null, - connectionHandleType: null, - connectionPosition: { x: NaN, y: NaN }, - }) + endConnection() doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject) doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject) diff --git a/packages/vue-flow/src/store/actions.ts b/packages/vue-flow/src/store/actions.ts index 4cbbecef..c3efd3a8 100644 --- a/packages/vue-flow/src/store/actions.ts +++ b/packages/vue-flow/src/store/actions.ts @@ -358,6 +358,24 @@ export default (state: State, getters: ComputedGetters): Actions => { const applyEdgeChanges: Actions['applyEdgeChanges'] = (changes) => applyChanges(changes, state.edges) + const startConnection: Actions['startConnection'] = (params) => { + state.connectionPosition = params.connectionPosition + state.connectionNodeId = params.connectionNodeId + state.connectionHandleId = params.connectionHandleId + state.connectionHandleType = params.connectionHandleType + } + + const updateConnection: Actions['updateConnection'] = (position) => { + state.connectionPosition = position + } + + const endConnection: Actions['endConnection'] = () => { + state.connectionPosition = { x: NaN, y: NaN } + state.connectionNodeId = null + state.connectionHandleId = null + state.connectionHandleType = null + } + const setState: Actions['setState'] = (options) => { const opts = options instanceof Function ? options(state) : options const skip: (keyof typeof opts)[] = [ @@ -435,6 +453,9 @@ export default (state: State, getters: ComputedGetters): Actions => { removeSelectedElements, removeSelectedNodes, removeSelectedEdges, + startConnection, + updateConnection, + endConnection, setInteractive, setState, fitView: async (params = { padding: 0.1 }) => { diff --git a/packages/vue-flow/src/types/store.ts b/packages/vue-flow/src/types/store.ts index 955e5735..342b0146 100644 --- a/packages/vue-flow/src/types/store.ts +++ b/packages/vue-flow/src/types/store.ts @@ -16,6 +16,13 @@ export interface UpdateNodeDimensionsParams { forceUpdate?: boolean } +export interface StartConnectionParams { + connectionPosition: XYPosition + connectionNodeId: string + connectionHandleId: string | null + connectionHandleType: HandleType +} + export interface State extends Omit { /** Vue flow element ref */ vueFlowRef: HTMLDivElement | null @@ -60,6 +67,7 @@ export interface State extends Omit { multiSelectionKeyCode: KeyFilter zoomActivationKeyCode: KeyFilter + // todo: remove these and just use connection start handle connectionNodeId: string | null connectionHandleId: string | null connectionHandleType: HandleType | null @@ -130,6 +138,12 @@ export type SetState = ( export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void export type UpdateNodeInternals = (nodeIds: string[]) => void +export type FindNode = = any>( + id: string, +) => GraphNode | undefined +export type FindEdge = = any>( + id: string, +) => GraphEdge | undefined export interface Actions extends ViewportFunctions { /** parses elements (nodes + edges) and re-sets the state */ @@ -147,13 +161,9 @@ export interface Actions extends ViewportFunctions { /** remove edges from state */ removeEdges: RemoveEdges /** find a node by id */ - findNode: = any>( - id: string, - ) => GraphNode | undefined + findNode: FindNode /** find an edge by id */ - findEdge: = any>( - id: string, - ) => GraphEdge | undefined + findEdge: FindEdge /** updates an edge */ updateEdge: UpdateEdge /** applies default edge change handler */ @@ -188,6 +198,12 @@ export interface Actions extends ViewportFunctions { toObject: () => FlowExportObject /** force update node internal data, if handle bounds are incorrect, you might want to use this */ updateNodeInternals: UpdateNodeInternals + /** start a connection */ + startConnection: (params: StartConnectionParams) => void + /** update connection position */ + updateConnection: (position: XYPosition) => void + /** end (or cancel) a connection */ + endConnection: () => void /** internal position updater, you probably don't want to use this */ updateNodePositions: UpdateNodePosition