import useVueFlow from './useVueFlow' ; import { getHostForElement } from '~/utils' ; import { Connection , ConnectionMode , HandleType , FlowStore , ValidConnectionFunc } from '~/types' ; type Result = { elementBelow : Element | null ; isValid : boolean ; connection : Connection ; isHoveringHandle : boolean ; } ; export const checkElementBelowIsValid = ( event , connectionMode , isTarget , nodeId , handleId , isValidConnection , doc ) => { const elementBelow = doc . elementFromPoint ( event . clientX , event . clientY ) ; const elementBelowIsTarget = elementBelow ?. classList . contains ( 'target' ) || false ; const elementBelowIsSource = elementBelow ?. classList . contains ( 'source' ) || false ; const result = { elementBelow , isValid : false , connection : { source : '' , target : '' , sourceHandle : '' , targetHandle : '' } , isHoveringHandle : false , } ; if ( elementBelow && ( elementBelowIsTarget || elementBelowIsSource ) ) { result . isHoveringHandle = true ; const isValid = connectionMode === ConnectionMode . Strict ? ( isTarget && elementBelowIsSource ) || ( ! isTarget && elementBelowIsTarget ) : true ; if ( isValid ) { const elementBelowNodeId = elementBelow . getAttribute ( 'data-nodeid' ) ?? '' ; const elementBelowHandleId = elementBelow . getAttribute ( 'data-handleid' ) ?? '' ; const connection = isTarget ? { source : elementBelowNodeId , sourceHandle : elementBelowHandleId , target : nodeId , targetHandle : handleId , } : { source : nodeId , sourceHandle : handleId , target : elementBelowNodeId , targetHandle : elementBelowHandleId , } ; result . connection = connection ; result . isValid = isValidConnection ( connection ) ; } } return result ; } ; const resetRecentHandle = ( hoveredHandle ) : void => { hoveredHandle ?. classList . remove ( 'vue-flow__handle-valid' ) ; hoveredHandle ?. classList . remove ( 'vue-flow__handle-connecting' ) ; } ; export default ( store = useVueFlow ( ) . store ) => ( event , handleId , nodeId , isTarget , isValidConnection ? , elementEdgeUpdaterType ? , onEdgeUpdate ? , onEdgeUpdateEnd ? ) => { const flowNode = ( event . target as Element ) . closest ( '.vue-flow' ) ; const doc = getHostForElement ( event . target as HTMLElement ) ; if ( ! doc ) return ; let validConnectFunc = isValidConnection ?? ( ( ) => true ) ; const node = store . getNode ( nodeId ) ; if ( node && ( typeof node . connectable === 'undefined' ? store . nodesConnectable : node . connectable ) === false ) return ; if ( ! isValidConnection ) { if ( node ) validConnectFunc = ( ! isTarget ? node . isValidTargetPos : node . isValidSourcePos ) ?? ( ( ) => true ) ; } const elementBelow = doc . elementFromPoint ( event . clientX , event . clientY ) ; const elementBelowIsTarget = elementBelow ?. classList . contains ( 'target' ) ; const elementBelowIsSource = elementBelow ?. classList . contains ( 'source' ) ; if ( ! flowNode || ( ! elementBelowIsTarget && ! elementBelowIsSource && ! elementEdgeUpdaterType ) ) return ; const handleType = elementEdgeUpdaterType ?? ( elementBelowIsTarget ? 'target' : 'source' ) ; const containerBounds = flowNode . getBoundingClientRect ( ) ; let recentHoveredHandle ; store . connectionPosition . x = event . clientX - containerBounds . left ; store . connectionPosition . y = event . clientY - containerBounds . top ; store . setConnectionNodeId ( { connectionNodeId : nodeId , connectionHandleId : handleId , connectionHandleType : handleType , } ) ; store . hooks . connectStart . trigger ( { event , nodeId , handleId , handleType } ) ; function onMouseMove ( event ) { store . connectionPosition . x = event . clientX - containerBounds . left ; store . connectionPosition . y = event . clientY - containerBounds . top ; const { connection , elementBelow , isValid , isHoveringHandle } = checkElementBelowIsValid ( event , store . connectionMode , isTarget , nodeId , handleId , validConnectFunc , doc ) ; if ( ! isHoveringHandle ) return resetRecentHandle ( recentHoveredHandle ) ; const isOwnHandle = connection . source === connection . target ; if ( ! isOwnHandle && elementBelow ) { recentHoveredHandle = elementBelow ; elementBelow . classList . add ( 'vue-flow__handle-connecting' ) ; elementBelow . classList . toggle ( 'vue-flow__handle-valid' , isValid ) ; } } function onMouseUp ( event ) { const { connection , isValid } = checkElementBelowIsValid ( event , store . connectionMode , isTarget , nodeId , handleId , validConnectFunc , doc ) ; store . hooks . connectStop . trigger ( event ) ; const isOwnHandle = connection . source === connection . target ; if ( isValid && ! isOwnHandle ) { onEdgeUpdate ?. ( connection ) ; } store . hooks . connectEnd . trigger ( event ) ; if ( elementEdgeUpdaterType ) onEdgeUpdateEnd ?. ( ) ; resetRecentHandle ( recentHoveredHandle ) ; store . setConnectionNodeId ( { connectionNodeId : undefined , connectionHandleId : undefined , connectionHandleType : undefined } ) ; store . connectionPosition = { x : NaN , y : NaN } ; doc . removeEventListener ( 'mousemove' , onMouseMove as EventListenerOrEventListenerObject ) ; doc . removeEventListener ( 'mouseup' , onMouseUp as EventListenerOrEventListenerObject ) ; } doc . addEventListener ( 'mousemove' , onMouseMove as EventListenerOrEventListenerObject ) ; doc . addEventListener ( 'mouseup' , onMouseUp as EventListenerOrEventListenerObject ) ; } ;