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 { - - - +