|
| 1 | +import { defineComponent, h, inject } from 'vue' |
| 2 | +import { ConnectionLineType, ConnectionMode, Position } from '~/types' |
| 3 | +import { getMarkerId } from '~/utils' |
| 4 | +import { useVueFlow } from '~/composables' |
| 5 | +import { Slots } from '~/context' |
| 6 | +import { getBezierPath, getSimpleBezierPath, getSmoothStepPath } from '~/components/Edges/utils' |
| 7 | + |
| 8 | +const oppositePosition = { |
| 9 | + [Position.Left]: Position.Right, |
| 10 | + [Position.Right]: Position.Left, |
| 11 | + [Position.Top]: Position.Bottom, |
| 12 | + [Position.Bottom]: Position.Top, |
| 13 | +} |
| 14 | + |
| 15 | +const ConnectionLine = defineComponent({ |
| 16 | + name: 'ConnectionLine', |
| 17 | + compatConfig: { MODE: 3 }, |
| 18 | + setup() { |
| 19 | + const { |
| 20 | + connectionMode, |
| 21 | + connectionStartHandle, |
| 22 | + connectionEndHandle, |
| 23 | + connectionPosition, |
| 24 | + connectionLineType, |
| 25 | + connectionLineStyle, |
| 26 | + connectionLineOptions, |
| 27 | + connectionStatus, |
| 28 | + viewport, |
| 29 | + findNode, |
| 30 | + } = useVueFlow() |
| 31 | + |
| 32 | + const connectionLineComponent = inject(Slots)?.['connection-line'] |
| 33 | + |
| 34 | + return () => { |
| 35 | + if (!connectionStartHandle.value) { |
| 36 | + return null |
| 37 | + } |
| 38 | + |
| 39 | + const fromNode = findNode(connectionStartHandle.value.nodeId) |
| 40 | + |
| 41 | + if (!fromNode) { |
| 42 | + return null |
| 43 | + } |
| 44 | + |
| 45 | + const handleId = connectionStartHandle.value.handleId |
| 46 | + |
| 47 | + const handleType = connectionStartHandle.value.type |
| 48 | + |
| 49 | + const targetNode = (connectionEndHandle.value?.handleId && findNode(connectionEndHandle.value.nodeId)) || null |
| 50 | + |
| 51 | + const toX = (connectionPosition.value.x - viewport.value.x) / viewport.value.zoom |
| 52 | + const toY = (connectionPosition.value.y - viewport.value.y) / viewport.value.zoom |
| 53 | + |
| 54 | + const fromHandleBounds = fromNode.handleBounds |
| 55 | + let handleBounds = fromHandleBounds?.[handleType] |
| 56 | + |
| 57 | + if (connectionMode.value === ConnectionMode.Loose) { |
| 58 | + handleBounds = handleBounds || fromHandleBounds?.[handleType === 'source' ? 'target' : 'source'] |
| 59 | + } |
| 60 | + |
| 61 | + if (!handleBounds) { |
| 62 | + return null |
| 63 | + } |
| 64 | + |
| 65 | + const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0] |
| 66 | + const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.dimensions.width ?? 0) / 2 |
| 67 | + const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.dimensions.height ?? 0 |
| 68 | + const fromX = (fromNode.computedPosition?.x ?? 0) + fromHandleX |
| 69 | + const fromY = (fromNode.computedPosition?.y ?? 0) + fromHandleY |
| 70 | + const fromPosition = fromHandle?.position |
| 71 | + const toHandle = |
| 72 | + (targetNode && |
| 73 | + connectionEndHandle.value?.handleId && |
| 74 | + ((connectionMode.value === ConnectionMode.Strict |
| 75 | + ? targetNode.handleBounds[handleType === 'source' ? 'target' : 'source']?.find( |
| 76 | + (d) => d.id === connectionEndHandle.value?.handleId, |
| 77 | + ) |
| 78 | + : [...(targetNode.handleBounds.source || []), ...(targetNode.handleBounds.target || [])]?.find( |
| 79 | + (d) => d.id === connectionEndHandle.value?.handleId, |
| 80 | + )) || |
| 81 | + targetNode.handleBounds[handleType ?? 'target']?.[0])) || |
| 82 | + null |
| 83 | + |
| 84 | + const toPosition = fromPosition ? oppositePosition[fromPosition] : null |
| 85 | + |
| 86 | + if (!fromPosition || !toPosition) { |
| 87 | + return null |
| 88 | + } |
| 89 | + |
| 90 | + const type = connectionLineType.value ?? connectionLineOptions.value.type |
| 91 | + |
| 92 | + let dAttr = '' |
| 93 | + |
| 94 | + const pathParams = { |
| 95 | + sourceX: fromX, |
| 96 | + sourceY: fromY, |
| 97 | + sourcePosition: fromPosition, |
| 98 | + targetX: toX, |
| 99 | + targetY: toY, |
| 100 | + targetPosition: toPosition, |
| 101 | + } |
| 102 | + |
| 103 | + if (type === ConnectionLineType.Bezier) { |
| 104 | + // we assume the destination position is opposite to the source position |
| 105 | + ;[dAttr] = getBezierPath(pathParams) |
| 106 | + } else if (type === ConnectionLineType.Step) { |
| 107 | + ;[dAttr] = getSmoothStepPath({ |
| 108 | + ...pathParams, |
| 109 | + borderRadius: 0, |
| 110 | + }) |
| 111 | + } else if (type === ConnectionLineType.SmoothStep) { |
| 112 | + ;[dAttr] = getSmoothStepPath(pathParams) |
| 113 | + } else if (type === ConnectionLineType.SimpleBezier) { |
| 114 | + ;[dAttr] = getSimpleBezierPath(pathParams) |
| 115 | + } else { |
| 116 | + dAttr = `M${fromX},${fromY} ${toX},${toY}` |
| 117 | + } |
| 118 | + |
| 119 | + return h( |
| 120 | + 'svg', |
| 121 | + { class: 'vue-flow__edges vue-flow__connectionline vue-flow__container' }, |
| 122 | + h( |
| 123 | + 'g', |
| 124 | + { class: 'vue-flow__connection' }, |
| 125 | + connectionLineComponent |
| 126 | + ? h(connectionLineComponent, { |
| 127 | + sourceX: fromX, |
| 128 | + sourceY: fromY, |
| 129 | + sourcePosition: fromPosition, |
| 130 | + targetX: toX, |
| 131 | + targetY: toY, |
| 132 | + targetPosition: toPosition, |
| 133 | + sourceNode: fromNode, |
| 134 | + sourceHandle: fromHandle, |
| 135 | + targetNode, |
| 136 | + targetHandle: toHandle, |
| 137 | + markerEnd: `url(#${getMarkerId(connectionLineOptions.value.markerEnd)})`, |
| 138 | + markerStart: `url(#${getMarkerId(connectionLineOptions.value.markerStart)})`, |
| 139 | + connectionStatus: connectionStatus.value, |
| 140 | + }) |
| 141 | + : h('path', { |
| 142 | + 'd': dAttr, |
| 143 | + 'class': [connectionLineOptions.value.class, connectionStatus, 'vue-flow__connection-path'], |
| 144 | + 'style': connectionLineStyle.value || connectionLineOptions.value.style, |
| 145 | + 'marker-end': `url(#${getMarkerId(connectionLineOptions.value.markerEnd)})`, |
| 146 | + 'marker-start': `url(#${getMarkerId(connectionLineOptions.value.markerStart)})`, |
| 147 | + }), |
| 148 | + ), |
| 149 | + ) |
| 150 | + } |
| 151 | + }, |
| 152 | +}) |
| 153 | + |
| 154 | +export default ConnectionLine |
0 commit comments