From 33b6fe121ad1507af17900af07fb4df41f82ce8a Mon Sep 17 00:00:00 2001
From: braks <78412429+bcakmakoglu@users.noreply.github.com>
Date: Thu, 15 Jun 2023 19:48:37 +0200
Subject: [PATCH] fix(core): correct connection line calculation
---
.../ConnectionLine/ConnectionLine.vue | 168 ------------------
.../src/components/ConnectionLine/index.ts | 154 ++++++++++++++++
.../container/EdgeRenderer/EdgeRenderer.vue | 30 +---
3 files changed, 156 insertions(+), 196 deletions(-)
delete mode 100644 packages/core/src/components/ConnectionLine/ConnectionLine.vue
create mode 100644 packages/core/src/components/ConnectionLine/index.ts
diff --git a/packages/core/src/components/ConnectionLine/ConnectionLine.vue b/packages/core/src/components/ConnectionLine/ConnectionLine.vue
deleted file mode 100644
index 9d99e27c..00000000
--- a/packages/core/src/components/ConnectionLine/ConnectionLine.vue
+++ /dev/null
@@ -1,168 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/core/src/components/ConnectionLine/index.ts b/packages/core/src/components/ConnectionLine/index.ts
new file mode 100644
index 00000000..24b4fe3c
--- /dev/null
+++ b/packages/core/src/components/ConnectionLine/index.ts
@@ -0,0 +1,154 @@
+import { defineComponent, h, inject } from 'vue'
+import { ConnectionLineType, ConnectionMode, Position } from '~/types'
+import { getMarkerId } from '~/utils'
+import { useVueFlow } from '~/composables'
+import { Slots } from '~/context'
+import { getBezierPath, getSimpleBezierPath, getSmoothStepPath } from '~/components/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 {
+ connectionMode,
+ connectionStartHandle,
+ connectionEndHandle,
+ connectionPosition,
+ connectionLineType,
+ connectionLineStyle,
+ connectionLineOptions,
+ connectionStatus,
+ viewport,
+ findNode,
+ } = useVueFlow()
+
+ const connectionLineComponent = inject(Slots)?.['connection-line']
+
+ return () => {
+ if (!connectionStartHandle.value) {
+ return null
+ }
+
+ const fromNode = findNode(connectionStartHandle.value.nodeId)
+
+ if (!fromNode) {
+ return null
+ }
+
+ const handleId = connectionStartHandle.value.handleId
+
+ const handleType = connectionStartHandle.value.type
+
+ const targetNode = (connectionEndHandle.value?.handleId && 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 = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0]
+ const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.dimensions.width ?? 0) / 2
+ const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.dimensions.height ?? 0
+ const fromX = (fromNode.computedPosition?.x ?? 0) + fromHandleX
+ const fromY = (fromNode.computedPosition?.y ?? 0) + fromHandleY
+ const fromPosition = fromHandle?.position
+ 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
+
+ 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)})`,
+ markerStart: `url(#${getMarkerId(connectionLineOptions.value.markerStart)})`,
+ 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)})`,
+ 'marker-start': `url(#${getMarkerId(connectionLineOptions.value.markerStart)})`,
+ }),
+ ),
+ )
+ }
+ },
+})
+
+export default ConnectionLine
diff --git a/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue b/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue
index d99ef6f6..296ed618 100644
--- a/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue
+++ b/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue
@@ -2,7 +2,7 @@
import { getCurrentInstance, inject, resolveComponent } from 'vue'
import { controlledComputed } from '@vueuse/core'
import EdgeWrapper from '../../components/Edges/EdgeWrapper'
-import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue'
+import ConnectionLine from '../../components/ConnectionLine'
import type { EdgeComponent, EdgeUpdatable, GraphEdge } from '../../types'
import { Slots } from '../../context'
import { useVueFlow } from '../../composables'
@@ -12,8 +12,6 @@ import MarkerDefinitions from './MarkerDefinitions.vue'
const slots = inject(Slots)
const {
- connectionStartHandle,
- nodesConnectable,
edgesUpdatable,
edgesFocusable,
elementsSelectable,
@@ -28,28 +26,6 @@ const {
emits,
} = useVueFlow()
-const sourceNode = controlledComputed(
- () => connectionStartHandle.value?.nodeId,
- () => {
- if (connectionStartHandle.value?.nodeId) {
- return findNode(connectionStartHandle.value.nodeId)
- }
-
- return false
- },
-)
-
-const connectionLineVisible = controlledComputed(
- () => connectionStartHandle.value?.nodeId,
- () =>
- !!(
- sourceNode.value &&
- (typeof sourceNode.value.connectable === 'undefined' ? nodesConnectable.value : sourceNode.value.connectable) &&
- connectionStartHandle.value?.nodeId &&
- connectionStartHandle.value?.type
- ),
-)
-
const groups = controlledComputed(
[
() => edges.value.map((e) => e.zIndex),
@@ -127,8 +103,6 @@ export default {
-
+