import type { DefineComponent } from 'vue' import { defineComponent, h, inject } from 'vue' import type { ConnectionLineProps } from '../../types' import { ConnectionLineType, ConnectionMode, Position } from '../../types' import { getHandlePosition, getMarkerId } from '../../utils' import { useVueFlow } from '../../composables' import { Slots } from '../../context' import { getBezierPath, getSimpleBezierPath, getSmoothStepPath } from '../Edges/utils' const oppositePosition = { [Position.Left]: Position.Right, [Position.Right]: Position.Left, [Position.Top]: Position.Bottom, [Position.Bottom]: Position.Top, } const ConnectionLine = defineComponent({ name: 'ConnectionLine', compatConfig: { MODE: 3 }, setup() { const { id, connectionMode, connectionStartHandle, connectionEndHandle, connectionPosition, connectionLineType, connectionLineStyle, connectionLineOptions, connectionStatus, viewport, findNode, } = useVueFlow() const connectionLineComponent = inject(Slots)?.['connection-line'] as DefineComponent | undefined return () => { if (!connectionStartHandle.value) { return null } const fromNode = findNode(connectionStartHandle.value.nodeId) if (!fromNode) { return null } const startHandleId = connectionStartHandle.value.handleId const handleType = connectionStartHandle.value.type const targetNode = (connectionEndHandle.value && findNode(connectionEndHandle.value.nodeId)) || null const toX = (connectionPosition.value.x - viewport.value.x) / viewport.value.zoom const toY = (connectionPosition.value.y - viewport.value.y) / viewport.value.zoom const fromHandleBounds = fromNode.handleBounds let handleBounds = fromHandleBounds?.[handleType] if (connectionMode.value === ConnectionMode.Loose) { handleBounds = handleBounds || fromHandleBounds?.[handleType === 'source' ? 'target' : 'source'] } if (!handleBounds) { return null } const fromHandle = (startHandleId ? handleBounds.find((d) => d.id === startHandleId) : handleBounds[0]) ?? null const fromPosition = fromHandle?.position || Position.Top const { x: fromX, y: fromY } = getHandlePosition( fromPosition, { ...fromNode.dimensions, ...fromNode.computedPosition }, fromHandle, ) // todo: this is a bit of a mess, we should refactor this const toHandle = (targetNode && connectionEndHandle.value?.handleId && ((connectionMode.value === ConnectionMode.Strict ? targetNode.handleBounds[handleType === 'source' ? 'target' : 'source']?.find( (d) => d.id === connectionEndHandle.value?.handleId, ) : [...(targetNode.handleBounds.source || []), ...(targetNode.handleBounds.target || [])]?.find( (d) => d.id === connectionEndHandle.value?.handleId, )) || targetNode.handleBounds[handleType ?? 'target']?.[0])) || null const toPosition = fromPosition ? oppositePosition[fromPosition] : null if (!fromPosition || !toPosition) { return null } const type = connectionLineType.value ?? connectionLineOptions.value.type ?? ConnectionLineType.Bezier let dAttr = '' const pathParams = { sourceX: fromX, sourceY: fromY, sourcePosition: fromPosition, targetX: toX, targetY: toY, targetPosition: toPosition, } if (type === ConnectionLineType.Bezier) { // we assume the destination position is opposite to the source position ;[dAttr] = getBezierPath(pathParams) } else if (type === ConnectionLineType.Step) { ;[dAttr] = getSmoothStepPath({ ...pathParams, borderRadius: 0, }) } else if (type === ConnectionLineType.SmoothStep) { ;[dAttr] = getSmoothStepPath(pathParams) } else if (type === ConnectionLineType.SimpleBezier) { ;[dAttr] = getSimpleBezierPath(pathParams) } else { dAttr = `M${fromX},${fromY} ${toX},${toY}` } return h( 'svg', { class: 'vue-flow__edges vue-flow__connectionline vue-flow__container' }, h( 'g', { class: 'vue-flow__connection' }, connectionLineComponent ? h(connectionLineComponent, { sourceX: fromX, sourceY: fromY, sourcePosition: fromPosition, targetX: toX, targetY: toY, targetPosition: toPosition, sourceNode: fromNode, sourceHandle: fromHandle, targetNode, targetHandle: toHandle, markerEnd: `url(#${getMarkerId(connectionLineOptions.value.markerEnd, id)})`, markerStart: `url(#${getMarkerId(connectionLineOptions.value.markerStart, id)})`, connectionStatus: connectionStatus.value, }) : h('path', { 'd': dAttr, 'class': [connectionLineOptions.value.class, connectionStatus, 'vue-flow__connection-path'], 'style': { ...connectionLineStyle.value, ...connectionLineOptions.value.style, }, 'marker-end': `url(#${getMarkerId(connectionLineOptions.value.markerEnd, id)})`, 'marker-start': `url(#${getMarkerId(connectionLineOptions.value.markerStart, id)})`, }), ), ) } }, }) export default ConnectionLine