Skip to content

Commit 33b6fe1

Browse files
committed
fix(core): correct connection line calculation
1 parent c4c9283 commit 33b6fe1

File tree

3 files changed

+156
-196
lines changed

3 files changed

+156
-196
lines changed

packages/core/src/components/ConnectionLine/ConnectionLine.vue

Lines changed: 0 additions & 168 deletions
This file was deleted.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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

packages/core/src/container/EdgeRenderer/EdgeRenderer.vue

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { getCurrentInstance, inject, resolveComponent } from 'vue'
33
import { controlledComputed } from '@vueuse/core'
44
import EdgeWrapper from '../../components/Edges/EdgeWrapper'
5-
import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue'
5+
import ConnectionLine from '../../components/ConnectionLine'
66
import type { EdgeComponent, EdgeUpdatable, GraphEdge } from '../../types'
77
import { Slots } from '../../context'
88
import { useVueFlow } from '../../composables'
@@ -12,8 +12,6 @@ import MarkerDefinitions from './MarkerDefinitions.vue'
1212
const slots = inject(Slots)
1313
1414
const {
15-
connectionStartHandle,
16-
nodesConnectable,
1715
edgesUpdatable,
1816
edgesFocusable,
1917
elementsSelectable,
@@ -28,28 +26,6 @@ const {
2826
emits,
2927
} = useVueFlow()
3028
31-
const sourceNode = controlledComputed(
32-
() => connectionStartHandle.value?.nodeId,
33-
() => {
34-
if (connectionStartHandle.value?.nodeId) {
35-
return findNode(connectionStartHandle.value.nodeId)
36-
}
37-
38-
return false
39-
},
40-
)
41-
42-
const connectionLineVisible = controlledComputed(
43-
() => connectionStartHandle.value?.nodeId,
44-
() =>
45-
!!(
46-
sourceNode.value &&
47-
(typeof sourceNode.value.connectable === 'undefined' ? nodesConnectable.value : sourceNode.value.connectable) &&
48-
connectionStartHandle.value?.nodeId &&
49-
connectionStartHandle.value?.type
50-
),
51-
)
52-
5329
const groups = controlledComputed(
5430
[
5531
() => edges.value.map((e) => e.zIndex),
@@ -127,8 +103,6 @@ export default {
127103
</g>
128104
</svg>
129105

130-
<svg v-if="connectionLineVisible && !!sourceNode" class="vue-flow__edges vue-flow__connectionline vue-flow__container">
131-
<ConnectionLine :source-node="sourceNode" />
132-
</svg>
106+
<ConnectionLine />
133107
</template>
134108
</template>

0 commit comments

Comments
 (0)