Skip to content

Commit b33da47

Browse files
committed
fix(core): use center position of handle as snapping point for connection lines (#1625)
* fix(core): calculate handle positions correctly Signed-off-by: braks <[email protected]> * fix(core): remove handleId check Signed-off-by: braks <[email protected]> * chore(core): cleanup Signed-off-by: braks <[email protected]> * chore(core): cleanup click connect handlers Signed-off-by: braks <[email protected]> * fix(core): add flow id to handle ids Signed-off-by: braks <[email protected]> * tests: correct updateEdge test Signed-off-by: braks <[email protected]> * fix(core): use center position of handle Signed-off-by: braks <[email protected]> * chore(examples): cleanup easy connect example Signed-off-by: braks <[email protected]> * refactor(tests): run actions on chrome Signed-off-by: braks <[email protected]> * chore(workflows): correctly hash lock file Signed-off-by: braks <[email protected]> --------- Signed-off-by: braks <[email protected]>
1 parent 041fd87 commit b33da47

File tree

16 files changed

+387
-314
lines changed

16 files changed

+387
-314
lines changed

.github/actions/install-dependencies/action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ runs:
2626
uses: actions/cache@v4
2727
with:
2828
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
29-
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
29+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
3030
restore-keys: |
3131
${{ runner.os }}-pnpm-store-
3232

.github/workflows/build-and-test.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,3 @@ jobs:
5454
with:
5555
install-command: pnpm cypress install
5656
command: pnpm run test --cache-dir=.turbo
57-
browser: chrome
58-
component: true
Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<script lang="ts" setup>
22
import { ref } from 'vue'
33
import { Background } from '@vue-flow/background'
4-
import { Controls } from '@vue-flow/controls'
5-
import { MiniMap } from '@vue-flow/minimap'
6-
import type { Elements } from '@vue-flow/core'
4+
import type { Node } from '@vue-flow/core'
75
import { MarkerType, VueFlow, useVueFlow } from '@vue-flow/core'
86
import CustomNode from './CustomNode.vue'
9-
import CustomConnectionLine from './CustomConnectionLine.vue'
7+
import FloatingConnectionLine from './FloatingConnectionLine.vue'
108
import FloatingEdge from './FloatingEdge.vue'
119
1210
const { onConnect, addEdges } = useVueFlow()
@@ -20,7 +18,7 @@ const defaultEdgeOptions = {
2018
},
2119
}
2220
23-
const elements = ref<Elements>([
21+
const nodes = ref<Node[]>([
2422
{
2523
id: '1',
2624
type: 'custom',
@@ -43,37 +41,31 @@ const elements = ref<Elements>([
4341
},
4442
])
4543
44+
const edges = ref([])
45+
4646
onConnect(addEdges)
4747
</script>
4848

4949
<template>
50-
<div style="height: 100vh">
51-
<VueFlow
52-
v-model="elements"
53-
:elevate-nodes-on-select="false"
54-
class="vue-flow-basic-example"
55-
:default-zoom="1.5"
56-
:default-edge-options="defaultEdgeOptions"
57-
:min-zoom="0.2"
58-
:max-zoom="4"
59-
>
60-
<Background pattern-color="#aaa" :gap="8" />
61-
62-
<MiniMap />
63-
64-
<Controls />
50+
<VueFlow
51+
v-model:nodes="nodes"
52+
v-model:edges="edges"
53+
:elevate-nodes-on-select="false"
54+
:default-edge-options="defaultEdgeOptions"
55+
fit-view-on-init
56+
>
57+
<Background pattern-color="#aaa" :gap="8" />
6558

66-
<template #node-custom="props">
67-
<CustomNode :id="props.id" />
68-
</template>
59+
<template #node-custom="props">
60+
<CustomNode :id="props.id" />
61+
</template>
6962

70-
<template #edge-floating="fProps">
71-
<FloatingEdge v-bind="fProps" />
72-
</template>
63+
<template #edge-floating="fProps">
64+
<FloatingEdge v-bind="fProps" />
65+
</template>
7366

74-
<template #connection-line="cProps">
75-
<CustomConnectionLine v-bind="cProps" />
76-
</template>
77-
</VueFlow>
78-
</div>
67+
<template #connection-line="cProps">
68+
<FloatingConnectionLine v-bind="cProps" />
69+
</template>
70+
</VueFlow>
7971
</template>

examples/vite/src/EasyConnect/CustomConnectionLine.vue renamed to examples/vite/src/EasyConnect/FloatingConnectionLine.vue

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<script lang="ts" setup>
2-
import type { GraphNode } from '@vue-flow/core'
3-
import { getStraightPath } from '@vue-flow/core'
2+
import type { ConnectionLineProps } from '@vue-flow/core'
3+
import { BaseEdge, getStraightPath } from '@vue-flow/core'
44
import { computed } from 'vue'
55
6-
const props = defineProps<{ sourceNode: GraphNode; sourceX: number; sourceY: number; targetX: number; targetY: number }>()
6+
const props = defineProps<ConnectionLineProps>()
77
88
const edgePath = computed(() =>
99
getStraightPath({
@@ -15,13 +15,12 @@ const edgePath = computed(() =>
1515

1616
<template>
1717
<g>
18-
<path
18+
<BaseEdge
1919
:style="{
2020
strokeWidth: 3,
2121
stroke: 'black',
2222
}"
23-
fill="none"
24-
:d="edgePath[0]"
23+
:path="edgePath[0]"
2524
/>
2625
<circle :cx="targetX" :cy="targetY" fill="black" :r="3" stroke="black" :stroke-width="1.5" />
2726
</g>

examples/vite/src/EasyConnect/FloatingEdge.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script lang="ts" setup>
2-
import type { GraphNode } from '@vue-flow/core'
3-
import { getStraightPath } from '@vue-flow/core'
2+
import type { EdgeProps } from '@vue-flow/core'
3+
import { BaseEdge, getStraightPath } from '@vue-flow/core'
44
import { computed } from 'vue'
55
66
import { getEdgeParams } from './utils'
77
8-
const props = defineProps<{ id: string; sourceNode: GraphNode; targetNode: GraphNode; markerEnd: string; style: any }>()
8+
const props = defineProps<EdgeProps>()
99
1010
const edgeParams = computed(() => getEdgeParams(props.sourceNode, props.targetNode))
1111
@@ -20,5 +20,5 @@ const edgePath = computed(() =>
2020
</script>
2121

2222
<template>
23-
<path :id="id" class="vue-flow__edge-path" :d="edgePath[0]" :marker-end="markerEnd" :style="style" />
23+
<BaseEdge :id="id" :path="edgePath[0]" :marker-end="markerEnd" :style="style" />
2424
</template>

packages/core/src/components/ConnectionLine/index.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
import { computed, defineComponent, h, inject } from 'vue'
22
import type { HandleElement } from '../../types'
33
import { ConnectionLineType, ConnectionMode, Position } from '../../types'
4-
import { getHandlePosition, getMarkerId } from '../../utils'
4+
import { getHandlePosition, getMarkerId, oppositePosition } from '../../utils'
55
import { useVueFlow } from '../../composables'
66
import { Slots } from '../../context'
77
import { getBezierPath, getSimpleBezierPath, getSmoothStepPath } from '../Edges/utils'
88

9-
const oppositePosition = {
10-
[Position.Left]: Position.Right,
11-
[Position.Right]: Position.Left,
12-
[Position.Top]: Position.Bottom,
13-
[Position.Bottom]: Position.Top,
14-
}
15-
169
const ConnectionLine = defineComponent({
1710
name: 'ConnectionLine',
1811
compatConfig: { MODE: 3 },
@@ -57,7 +50,7 @@ const ConnectionLine = defineComponent({
5750
return null
5851
}
5952

60-
const startHandleId = connectionStartHandle.value.handleId
53+
const startHandleId = connectionStartHandle.value.id
6154

6255
const handleType = connectionStartHandle.value.type
6356

@@ -78,18 +71,18 @@ const ConnectionLine = defineComponent({
7871
const { x: fromX, y: fromY } = getHandlePosition(fromNode.value, fromHandle, fromPosition)
7972

8073
let toHandle: HandleElement | null = null
81-
if (toNode.value && connectionEndHandle.value?.handleId) {
74+
if (toNode.value) {
8275
// if connection mode is strict, we only look for handles of the opposite type
8376
if (connectionMode.value === ConnectionMode.Strict) {
8477
toHandle =
8578
toNode.value.handleBounds[handleType === 'source' ? 'target' : 'source']?.find(
86-
(d) => d.id === connectionEndHandle.value?.handleId,
79+
(d) => d.id === connectionEndHandle.value?.id,
8780
) || null
8881
} else {
8982
// if connection mode is loose, look for the handle in both source and target bounds
9083
toHandle =
9184
[...(toNode.value.handleBounds.source || []), ...(toNode.value.handleBounds.target || [])]?.find(
92-
(d) => d.id === connectionEndHandle.value?.handleId,
85+
(d) => d.id === connectionEndHandle.value?.id,
9386
) || null
9487
}
9588
}

packages/core/src/components/Edges/EdgeWrapper.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
ErrorCode,
99
VueFlowError,
1010
elementSelectionKeys,
11-
getHandle,
11+
getEdgeHandle,
1212
getHandlePosition,
1313
getMarkerId,
1414
} from '../../utils'
@@ -150,7 +150,7 @@ const EdgeWrapper = defineComponent({
150150
sourceNodeHandles = [...(sourceNode.handleBounds.source || []), ...(sourceNode.handleBounds.target || [])]
151151
}
152152

153-
const sourceHandle = getHandle(sourceNodeHandles, edge.value.sourceHandle)
153+
const sourceHandle = getEdgeHandle(sourceNodeHandles, edge.value.sourceHandle)
154154

155155
let targetNodeHandles
156156
if (connectionMode.value === ConnectionMode.Strict) {
@@ -159,7 +159,7 @@ const EdgeWrapper = defineComponent({
159159
targetNodeHandles = [...(targetNode.handleBounds.target || []), ...(targetNode.handleBounds.source || [])]
160160
}
161161

162-
const targetHandle = getHandle(targetNodeHandles, edge.value.targetHandle)
162+
const targetHandle = getEdgeHandle(targetNodeHandles, edge.value.targetHandle)
163163

164164
const sourcePosition = sourceHandle?.position || Position.Bottom
165165

packages/core/src/components/Handle/Handle.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const type = toRef(() => props.type ?? 'source')
1919
const isValidConnection = toRef(() => props.isValidConnection ?? null)
2020
2121
const {
22+
id: flowId,
2223
connectionStartHandle,
2324
connectionClickStartHandle,
2425
connectionEndHandle,
@@ -39,17 +40,17 @@ const isConnectableEnd = toRef(() => (typeof connectableEnd !== 'undefined' ? co
3940
const isConnecting = toRef(
4041
() =>
4142
(connectionStartHandle.value?.nodeId === nodeId &&
42-
connectionStartHandle.value?.handleId === handleId &&
43+
connectionStartHandle.value?.id === handleId &&
4344
connectionStartHandle.value?.type === type.value) ||
4445
(connectionEndHandle.value?.nodeId === nodeId &&
45-
connectionEndHandle.value?.handleId === handleId &&
46+
connectionEndHandle.value?.id === handleId &&
4647
connectionEndHandle.value?.type === type.value),
4748
)
4849
4950
const isClickConnecting = toRef(
5051
() =>
5152
connectionClickStartHandle.value?.nodeId === nodeId &&
52-
connectionClickStartHandle.value?.handleId === handleId &&
53+
connectionClickStartHandle.value?.id === handleId &&
5354
connectionClickStartHandle.value?.type === type.value,
5455
)
5556
@@ -127,6 +128,8 @@ onMounted(() => {
127128
position,
128129
x: (handleBounds.left - nodeBounds.left) / zoom,
129130
y: (handleBounds.top - nodeBounds.top) / zoom,
131+
type: type.value,
132+
nodeId,
130133
...getDimensions(handle.value),
131134
}
132135
@@ -177,7 +180,7 @@ export default {
177180
<template>
178181
<div
179182
ref="handle"
180-
:data-id="`${nodeId}-${handleId}-${type}`"
183+
:data-id="`${flowId}-${nodeId}-${handleId}-${type}`"
181184
:data-handleid="handleId"
182185
:data-nodeid="nodeId"
183186
:data-handlepos="position"

0 commit comments

Comments
 (0)