From 8e7ec267ecf671d0aac29a138c2c416ca6edfe7d Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:21:06 +0200 Subject: [PATCH 1/9] chore(core): add xyflow system to deps Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- packages/core/package.json | 3 ++- pnpm-lock.yaml | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 76135d0ee..2f9632c05 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -73,7 +73,8 @@ "@vueuse/core": "^10.5.0", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", - "d3-zoom": "^3.0.0" + "d3-zoom": "^3.0.0", + "@xyflow/system": "^0.0.36" }, "devDependencies": { "@rollup/plugin-replace": "^5.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c9cc7511..3451a9ac9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -287,6 +287,9 @@ importers: '@vueuse/core': specifier: ^10.5.0 version: 10.5.0(vue@3.3.4) + '@xyflow/system': + specifier: ^0.0.36 + version: 0.0.36 d3-drag: specifier: ^3.0.0 version: 3.0.0 @@ -2227,15 +2230,27 @@ packages: '@types/d3-drag@3.0.4': resolution: {integrity: sha512-/t53K1erTuUbP7WIX9SE0hlmytpTYRbIthlhbGkBHzCV5vPO++7yrk8OlisWPyIJO5TGowTmqCtGH2tokY5T/g==} + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + '@types/d3-interpolate@3.0.2': resolution: {integrity: sha512-zAbCj9lTqW9J9PlF4FwnvEjXZUy75NQqPm7DMHZXuxCFTpuTrdK2NMYGQekf4hlasL78fCYOLu4EE3/tXElwow==} + '@types/d3-selection@3.0.10': + resolution: {integrity: sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==} + '@types/d3-selection@3.0.7': resolution: {integrity: sha512-qoj2O7KjfqCobmtFOth8FMvjwMVPUAAmn6xiUbLl1ld7vQCPgffvyV5BBcEFfqWdilAUm+3zciU/3P3vZrUMlg==} + '@types/d3-transition@3.0.8': + resolution: {integrity: sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==} + '@types/d3-zoom@3.0.5': resolution: {integrity: sha512-mIefdTLtxuWUWTbBupCUXPAXVPmi8/Uwrq41gQpRh0rD25GMU1ku+oTELqNY2NuuiI0F3wXC5e1liBQi7YS7XQ==} + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + '@types/estree@1.0.2': resolution: {integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==} @@ -2776,6 +2791,9 @@ packages: '@windicss/plugin-utils@1.9.3': resolution: {integrity: sha512-3VG5HEGeuIfG/9iTwLyzWWm/aGKNTbtSVkpkAabdRuDP/2lEmf6Hpo4uo5drwE+2O9gXfc6nSYgAwBjotx5CfQ==} + '@xyflow/system@0.0.36': + resolution: {integrity: sha512-u7zWPnZhgX7Yf7L9hq5NLmP3srMe8Do+SEVeNG5r67cFISo2hul/4lrqFE2BzV+n1gyaSrNDR0XqA8aeH1Hqfg==} + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -9571,17 +9589,32 @@ snapshots: dependencies: '@types/d3-selection': 3.0.7 + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.10 + '@types/d3-interpolate@3.0.2': dependencies: '@types/d3-color': 3.1.1 + '@types/d3-selection@3.0.10': {} + '@types/d3-selection@3.0.7': {} + '@types/d3-transition@3.0.8': + dependencies: + '@types/d3-selection': 3.0.10 + '@types/d3-zoom@3.0.5': dependencies: '@types/d3-interpolate': 3.0.2 '@types/d3-selection': 3.0.7 + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.2 + '@types/d3-selection': 3.0.10 + '@types/estree@1.0.2': {} '@types/estree@1.0.5': {} @@ -10375,6 +10408,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@xyflow/system@0.0.36': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-selection': 3.0.10 + '@types/d3-transition': 3.0.8 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + abbrev@1.1.1: {} abbrev@2.0.0: {} From dc474ed245bf9b2f814c7e8200e4015ab63f765b Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 15 Jul 2024 20:58:47 +0200 Subject: [PATCH 2/9] refactor(core,edges): replace edge utils with existing `@xyflow/system` exports (#1547) * refactor(core,edges): replace edge helpers with xyflow/system exports Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(core): cleanup Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(changeset): add Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(core): cleanup exports Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --------- Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- .changeset/ten-snails-flash.md | 5 + .../src/components/ConnectionLine/index.ts | 3 +- .../core/src/components/Edges/BezierEdge.ts | 2 +- .../src/components/Edges/SimpleBezierEdge.ts | 93 ++++++- .../src/components/Edges/SmoothStepEdge.ts | 2 +- .../core/src/components/Edges/StraightEdge.ts | 2 +- .../core/src/components/Edges/utils/bezier.ts | 114 -------- .../src/components/Edges/utils/general.ts | 51 ---- .../core/src/components/Edges/utils/index.ts | 5 - .../components/Edges/utils/simple-bezier.ts | 95 ------- .../src/components/Edges/utils/smoothstep.ts | 250 ------------------ .../src/components/Edges/utils/straight.ts | 33 --- packages/core/src/index.ts | 11 +- 13 files changed, 105 insertions(+), 561 deletions(-) create mode 100644 .changeset/ten-snails-flash.md delete mode 100644 packages/core/src/components/Edges/utils/bezier.ts delete mode 100644 packages/core/src/components/Edges/utils/general.ts delete mode 100644 packages/core/src/components/Edges/utils/index.ts delete mode 100644 packages/core/src/components/Edges/utils/simple-bezier.ts delete mode 100644 packages/core/src/components/Edges/utils/smoothstep.ts delete mode 100644 packages/core/src/components/Edges/utils/straight.ts diff --git a/.changeset/ten-snails-flash.md b/.changeset/ten-snails-flash.md new file mode 100644 index 000000000..e92f8f092 --- /dev/null +++ b/.changeset/ten-snails-flash.md @@ -0,0 +1,5 @@ +--- +"@vue-flow/core": minor +--- + +Replace existing edge utils with ones that are already provided by `@xyflow/system` and re-export them diff --git a/packages/core/src/components/ConnectionLine/index.ts b/packages/core/src/components/ConnectionLine/index.ts index 025ec5627..70101ac8d 100644 --- a/packages/core/src/components/ConnectionLine/index.ts +++ b/packages/core/src/components/ConnectionLine/index.ts @@ -1,10 +1,11 @@ import { computed, defineComponent, h, inject } from 'vue' +import { getBezierPath, getSmoothStepPath } from '@xyflow/system' import type { HandleElement } from '../../types' import { ConnectionLineType, ConnectionMode, Position } from '../../types' import { getHandlePosition, getMarkerId } from '../../utils' import { useVueFlow } from '../../composables' import { Slots } from '../../context' -import { getBezierPath, getSimpleBezierPath, getSmoothStepPath } from '../Edges/utils' +import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge' const oppositePosition = { [Position.Left]: Position.Right, diff --git a/packages/core/src/components/Edges/BezierEdge.ts b/packages/core/src/components/Edges/BezierEdge.ts index 7dc0751f2..4ed7dd82a 100644 --- a/packages/core/src/components/Edges/BezierEdge.ts +++ b/packages/core/src/components/Edges/BezierEdge.ts @@ -1,8 +1,8 @@ import { defineComponent, h } from 'vue' +import { getBezierPath } from '@xyflow/system' import type { BezierEdgeProps } from '../../types' import { Position } from '../../types' import BaseEdge from './BaseEdge.vue' -import { getBezierPath } from './utils' const BezierEdge = defineComponent({ name: 'BezierEdge', diff --git a/packages/core/src/components/Edges/SimpleBezierEdge.ts b/packages/core/src/components/Edges/SimpleBezierEdge.ts index a33e7c76f..019c9fb8f 100644 --- a/packages/core/src/components/Edges/SimpleBezierEdge.ts +++ b/packages/core/src/components/Edges/SimpleBezierEdge.ts @@ -1,8 +1,99 @@ import { defineComponent, h } from 'vue' +import { getBezierEdgeCenter } from '@xyflow/system' import type { SimpleBezierEdgeProps } from '../../types' import { Position } from '../../types' import BaseEdge from './BaseEdge.vue' -import { getSimpleBezierPath } from './utils' + +export interface GetSimpleBezierPathParams { + sourceX: number + sourceY: number + sourcePosition?: Position + targetX: number + targetY: number + targetPosition?: Position +} + +interface GetControlParams { + pos: Position + x1: number + y1: number + x2: number + y2: number +} + +function getControl({ pos, x1, y1, x2, y2 }: GetControlParams): [number, number] { + let ctX: number, ctY: number + switch (pos) { + case Position.Left: + case Position.Right: + ctX = 0.5 * (x1 + x2) + ctY = y1 + break + case Position.Top: + case Position.Bottom: + ctX = x1 + ctY = 0.5 * (y1 + y2) + break + } + return [ctX, ctY] +} + +/** + * Get a simple bezier path from source to target handle (no curvature) + * @public + * + * @param simpleBezierPathParams + * @param simpleBezierPathParams.sourceX - The x position of the source handle + * @param simpleBezierPathParams.sourceY - The y position of the source handle + * @param simpleBezierPathParams.sourcePosition - The position of the source handle (default: Position.Bottom) + * @param simpleBezierPathParams.targetX - The x position of the target handle + * @param simpleBezierPathParams.targetY - The y position of the target handle + * @param simpleBezierPathParams.targetPosition - The position of the target handle (default: Position.Top) + * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label + */ +export function getSimpleBezierPath({ + sourceX, + sourceY, + sourcePosition = Position.Bottom, + targetX, + targetY, + targetPosition = Position.Top, +}: GetSimpleBezierPathParams): [path: string, labelX: number, labelY: number, offsetX: number, offsetY: number] { + const [sourceControlX, sourceControlY] = getControl({ + pos: sourcePosition, + x1: sourceX, + y1: sourceY, + x2: targetX, + y2: targetY, + }) + + const [targetControlX, targetControlY] = getControl({ + pos: targetPosition, + x1: targetX, + y1: targetY, + x2: sourceX, + y2: sourceY, + }) + + const [labelX, labelY, offsetX, offsetY] = getBezierEdgeCenter({ + sourceX, + sourceY, + targetX, + targetY, + sourceControlX, + sourceControlY, + targetControlX, + targetControlY, + }) + + return [ + `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`, + labelX, + labelY, + offsetX, + offsetY, + ] +} const SimpleBezierEdge = defineComponent({ name: 'SimpleBezierEdge', diff --git a/packages/core/src/components/Edges/SmoothStepEdge.ts b/packages/core/src/components/Edges/SmoothStepEdge.ts index 88fd824d6..f43b49cd8 100644 --- a/packages/core/src/components/Edges/SmoothStepEdge.ts +++ b/packages/core/src/components/Edges/SmoothStepEdge.ts @@ -1,8 +1,8 @@ import { defineComponent, h } from 'vue' +import { getSmoothStepPath } from '@xyflow/system' import type { SmoothStepEdgeProps } from '../../types' import { Position } from '../../types' import BaseEdge from './BaseEdge.vue' -import { getSmoothStepPath } from './utils' const SmoothStepEdge = defineComponent({ name: 'SmoothStepEdge', diff --git a/packages/core/src/components/Edges/StraightEdge.ts b/packages/core/src/components/Edges/StraightEdge.ts index 38752a902..6d5b6b84d 100644 --- a/packages/core/src/components/Edges/StraightEdge.ts +++ b/packages/core/src/components/Edges/StraightEdge.ts @@ -1,7 +1,7 @@ import { defineComponent, h } from 'vue' +import { getStraightPath } from '@xyflow/system' import type { StraightEdgeProps } from '../../types' import BaseEdge from './BaseEdge.vue' -import { getStraightPath } from './utils' const StraightEdge = defineComponent({ name: 'StraightEdge', diff --git a/packages/core/src/components/Edges/utils/bezier.ts b/packages/core/src/components/Edges/utils/bezier.ts deleted file mode 100644 index 5ddca603c..000000000 --- a/packages/core/src/components/Edges/utils/bezier.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Position } from '../../../types' -import type { EdgePathParams } from './general' -import { getBezierEdgeCenter } from './general' - -export interface GetBezierPathParams { - sourceX: number - sourceY: number - sourcePosition?: Position - targetX: number - targetY: number - targetPosition?: Position - curvature?: number -} - -interface GetControlWithCurvatureParams { - pos: Position - x1: number - y1: number - x2: number - y2: number - c: number -} - -function calculateControlOffset(distance: number, curvature: number) { - if (distance >= 0) { - return 0.5 * distance - } else { - return curvature * 25 * Math.sqrt(-distance) - } -} - -function getControlWithCurvature({ pos, x1, y1, x2, y2, c }: GetControlWithCurvatureParams): [number, number] { - let ctX: number, ctY: number - switch (pos) { - case Position.Left: - ctX = x1 - calculateControlOffset(x1 - x2, c) - ctY = y1 - break - case Position.Right: - ctX = x1 + calculateControlOffset(x2 - x1, c) - ctY = y1 - break - case Position.Top: - ctX = x1 - ctY = y1 - calculateControlOffset(y1 - y2, c) - break - case Position.Bottom: - ctX = x1 - ctY = y1 + calculateControlOffset(y2 - y1, c) - break - } - return [ctX, ctY] -} - -/** - * Get a bezier path from source to target handle - * @public - * - * @param bezierPathParams - * @param bezierPathParams.sourceX - The x position of the source handle - * @param bezierPathParams.sourceY - The y position of the source handle - * @param bezierPathParams.sourcePosition - The position of the source handle (default: Position.Bottom) - * @param bezierPathParams.targetX - The x position of the target handle - * @param bezierPathParams.targetY - The y position of the target handle - * @param bezierPathParams.targetPosition - The position of the target handle (default: Position.Top) - * @param bezierPathParams.curvature - The curvature of the edge (default: 0.25) - * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label - */ -export function getBezierPath(bezierPathParams: GetBezierPathParams): EdgePathParams { - const { - sourceX, - sourceY, - sourcePosition = Position.Bottom, - targetX, - targetY, - targetPosition = Position.Top, - curvature = 0.25, - } = bezierPathParams - - const [sourceControlX, sourceControlY] = getControlWithCurvature({ - pos: sourcePosition, - x1: sourceX, - y1: sourceY, - x2: targetX, - y2: targetY, - c: curvature, - }) - const [targetControlX, targetControlY] = getControlWithCurvature({ - pos: targetPosition, - x1: targetX, - y1: targetY, - x2: sourceX, - y2: sourceY, - c: curvature, - }) - const [labelX, labelY, offsetX, offsetY] = getBezierEdgeCenter({ - sourceX, - sourceY, - targetX, - targetY, - sourceControlX, - sourceControlY, - targetControlX, - targetControlY, - }) - - return [ - `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`, - labelX, - labelY, - offsetX, - offsetY, - ] -} diff --git a/packages/core/src/components/Edges/utils/general.ts b/packages/core/src/components/Edges/utils/general.ts deleted file mode 100644 index 1b7bfab9c..000000000 --- a/packages/core/src/components/Edges/utils/general.ts +++ /dev/null @@ -1,51 +0,0 @@ -export type EdgePathParams = [path: string, labelX: number, labelY: number, offsetX: number, offsetY: number] - -// this is used for straight edges and simple smoothstep edges (LTR, RTL, BTT, TTB) -export function getSimpleEdgeCenter({ - sourceX, - sourceY, - targetX, - targetY, -}: { - sourceX: number - sourceY: number - targetX: number - targetY: number -}): [number, number, number, number] { - const xOffset = Math.abs(targetX - sourceX) / 2 - const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset - - const yOffset = Math.abs(targetY - sourceY) / 2 - const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset - - return [centerX, centerY, xOffset, yOffset] -} - -export function getBezierEdgeCenter({ - sourceX, - sourceY, - targetX, - targetY, - sourceControlX, - sourceControlY, - targetControlX, - targetControlY, -}: { - sourceX: number - sourceY: number - targetX: number - targetY: number - sourceControlX: number - sourceControlY: number - targetControlX: number - targetControlY: number -}): [number, number, number, number] { - // cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate - // https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve - const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125 - const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125 - const offsetX = Math.abs(centerX - sourceX) - const offsetY = Math.abs(centerY - sourceY) - - return [centerX, centerY, offsetX, offsetY] -} diff --git a/packages/core/src/components/Edges/utils/index.ts b/packages/core/src/components/Edges/utils/index.ts deleted file mode 100644 index 5993032ad..000000000 --- a/packages/core/src/components/Edges/utils/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './bezier' -export * from './general' -export * from './simple-bezier' -export * from './smoothstep' -export * from './straight' diff --git a/packages/core/src/components/Edges/utils/simple-bezier.ts b/packages/core/src/components/Edges/utils/simple-bezier.ts deleted file mode 100644 index 6f9c5f411..000000000 --- a/packages/core/src/components/Edges/utils/simple-bezier.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Position } from '../../../types' -import type { EdgePathParams } from './general' -import { getBezierEdgeCenter } from './general' - -export interface GetSimpleBezierPathParams { - sourceX: number - sourceY: number - sourcePosition?: Position - targetX: number - targetY: number - targetPosition?: Position -} - -interface GetControlParams { - pos: Position - x1: number - y1: number - x2: number - y2: number -} - -function getControl({ pos, x1, y1, x2, y2 }: GetControlParams): [number, number] { - let ctX: number, ctY: number - switch (pos) { - case Position.Left: - case Position.Right: - ctX = 0.5 * (x1 + x2) - ctY = y1 - break - case Position.Top: - case Position.Bottom: - ctX = x1 - ctY = 0.5 * (y1 + y2) - break - } - return [ctX, ctY] -} - -/** - * Get a simple bezier path from source to target handle (no curvature) - * @public - * - * @param simpleBezierPathParams - * @param simpleBezierPathParams.sourceX - The x position of the source handle - * @param simpleBezierPathParams.sourceY - The y position of the source handle - * @param simpleBezierPathParams.sourcePosition - The position of the source handle (default: Position.Bottom) - * @param simpleBezierPathParams.targetX - The x position of the target handle - * @param simpleBezierPathParams.targetY - The y position of the target handle - * @param simpleBezierPathParams.targetPosition - The position of the target handle (default: Position.Top) - * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label - */ -export function getSimpleBezierPath(simpleBezierPathParams: GetSimpleBezierPathParams): EdgePathParams { - const { - sourceX, - sourceY, - sourcePosition = Position.Bottom, - targetX, - targetY, - targetPosition = Position.Top, - } = simpleBezierPathParams - - const [sourceControlX, sourceControlY] = getControl({ - pos: sourcePosition, - x1: sourceX, - y1: sourceY, - x2: targetX, - y2: targetY, - }) - const [targetControlX, targetControlY] = getControl({ - pos: targetPosition, - x1: targetX, - y1: targetY, - x2: sourceX, - y2: sourceY, - }) - - const [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({ - sourceX, - sourceY, - targetX, - targetY, - sourceControlX, - sourceControlY, - targetControlX, - targetControlY, - }) - - return [ - `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`, - centerX, - centerY, - offsetX, - offsetY, - ] -} diff --git a/packages/core/src/components/Edges/utils/smoothstep.ts b/packages/core/src/components/Edges/utils/smoothstep.ts deleted file mode 100644 index cc6477440..000000000 --- a/packages/core/src/components/Edges/utils/smoothstep.ts +++ /dev/null @@ -1,250 +0,0 @@ -import type { XYPosition } from '../../../types' -import { Position } from '../../../types' -import type { EdgePathParams } from './general' -import { getSimpleEdgeCenter } from './general' - -export interface GetSmoothStepPathParams { - sourceX: number - sourceY: number - sourcePosition?: Position - targetX: number - targetY: number - targetPosition?: Position - borderRadius?: number - centerX?: number - centerY?: number - offset?: number -} - -const handleDirections = { - [Position.Left]: { x: -1, y: 0 }, - [Position.Right]: { x: 1, y: 0 }, - [Position.Top]: { x: 0, y: -1 }, - [Position.Bottom]: { x: 0, y: 1 }, -} - -function getDirection({ - source, - sourcePosition = Position.Bottom, - target, -}: { - source: XYPosition - sourcePosition: Position - target: XYPosition -}): XYPosition { - if (sourcePosition === Position.Left || sourcePosition === Position.Right) { - return source.x < target.x ? { x: 1, y: 0 } : { x: -1, y: 0 } - } - return source.y < target.y ? { x: 0, y: 1 } : { x: 0, y: -1 } -} - -function distance(a: XYPosition, b: XYPosition) { - return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2) -} - -// With this function we try to mimic an orthogonal edge routing behaviour -// It's not as good as a real orthogonal edge routing, but it's faster and good enough as a default for step and smooth step edges -function getPoints({ - source, - sourcePosition = Position.Bottom, - target, - targetPosition = Position.Top, - center, - offset, -}: { - source: XYPosition - sourcePosition: Position - target: XYPosition - targetPosition: Position - center: Partial - offset: number -}): [XYPosition[], number, number, number, number] { - const sourceDir = handleDirections[sourcePosition] - const targetDir = handleDirections[targetPosition] - const sourceGapped: XYPosition = { x: source.x + sourceDir.x * offset, y: source.y + sourceDir.y * offset } - const targetGapped: XYPosition = { x: target.x + targetDir.x * offset, y: target.y + targetDir.y * offset } - const dir = getDirection({ - source: sourceGapped, - sourcePosition, - target: targetGapped, - }) - const dirAccessor = dir.x !== 0 ? 'x' : 'y' - const currDir = dir[dirAccessor] - - let points: XYPosition[] - let centerX, centerY - - const sourceGapOffset = { x: 0, y: 0 } - const targetGapOffset = { x: 0, y: 0 } - - const [defaultCenterX, defaultCenterY, defaultOffsetX, defaultOffsetY] = getSimpleEdgeCenter({ - sourceX: source.x, - sourceY: source.y, - targetX: target.x, - targetY: target.y, - }) - - // opposite handle positions, default case - if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) { - centerX = center.x ?? defaultCenterX - centerY = center.y ?? defaultCenterY - // ---> - // | - // >--- - const verticalSplit: XYPosition[] = [ - { x: centerX, y: sourceGapped.y }, - { x: centerX, y: targetGapped.y }, - ] - // | - // --- - // | - const horizontalSplit: XYPosition[] = [ - { x: sourceGapped.x, y: centerY }, - { x: targetGapped.x, y: centerY }, - ] - - if (sourceDir[dirAccessor] === currDir) { - points = dirAccessor === 'x' ? verticalSplit : horizontalSplit - } else { - points = dirAccessor === 'x' ? horizontalSplit : verticalSplit - } - } else { - // sourceTarget means we take x from source and y from target, targetSource is the opposite - const sourceTarget: XYPosition[] = [{ x: sourceGapped.x, y: targetGapped.y }] - const targetSource: XYPosition[] = [{ x: targetGapped.x, y: sourceGapped.y }] - // this handles edges with same handle positions - if (dirAccessor === 'x') { - points = sourceDir.x === currDir ? targetSource : sourceTarget - } else { - points = sourceDir.y === currDir ? sourceTarget : targetSource - } - - if (sourcePosition === targetPosition) { - const diff = Math.abs(source[dirAccessor] - target[dirAccessor]) - - // if an edge goes from right to right for example (sourcePosition === targetPosition) and the distance between source.x and target.x is less than the offset, the added point and the gapped source/target will overlap. This leads to a weird edge path. To avoid this we add a gapOffset to the source/target - if (diff <= offset) { - const gapOffset = Math.min(offset - 1, offset - diff) - if (sourceDir[dirAccessor] === currDir) { - sourceGapOffset[dirAccessor] = (sourceGapped[dirAccessor] > source[dirAccessor] ? -1 : 1) * gapOffset - } else { - targetGapOffset[dirAccessor] = (targetGapped[dirAccessor] > target[dirAccessor] ? -1 : 1) * gapOffset - } - } - } - - // these are conditions for handling mixed handle positions like Right -> Bottom for example - if (sourcePosition !== targetPosition) { - const dirAccessorOpposite = dirAccessor === 'x' ? 'y' : 'x' - const isSameDir = sourceDir[dirAccessor] === targetDir[dirAccessorOpposite] - const sourceGtTargetOppo = sourceGapped[dirAccessorOpposite] > targetGapped[dirAccessorOpposite] - const sourceLtTargetOppo = sourceGapped[dirAccessorOpposite] < targetGapped[dirAccessorOpposite] - const flipSourceTarget = - (sourceDir[dirAccessor] === 1 && ((!isSameDir && sourceGtTargetOppo) || (isSameDir && sourceLtTargetOppo))) || - (sourceDir[dirAccessor] !== 1 && ((!isSameDir && sourceLtTargetOppo) || (isSameDir && sourceGtTargetOppo))) - - if (flipSourceTarget) { - points = dirAccessor === 'x' ? sourceTarget : targetSource - } - } - - const sourceGapPoint = { x: sourceGapped.x + sourceGapOffset.x, y: sourceGapped.y + sourceGapOffset.y } - const targetGapPoint = { x: targetGapped.x + targetGapOffset.x, y: targetGapped.y + targetGapOffset.y } - const maxXDistance = Math.max(Math.abs(sourceGapPoint.x - points[0].x), Math.abs(targetGapPoint.x - points[0].x)) - const maxYDistance = Math.max(Math.abs(sourceGapPoint.y - points[0].y), Math.abs(targetGapPoint.y - points[0].y)) - - // we want to place the label on the longest segment of the edge - if (maxXDistance >= maxYDistance) { - centerX = (sourceGapPoint.x + targetGapPoint.x) / 2 - centerY = points[0].y - } else { - centerX = points[0].x - centerY = (sourceGapPoint.y + targetGapPoint.y) / 2 - } - } - - const pathPoints = [ - source, - { x: sourceGapped.x + sourceGapOffset.x, y: sourceGapped.y + sourceGapOffset.y }, - ...points, - { x: targetGapped.x + targetGapOffset.x, y: targetGapped.y + targetGapOffset.y }, - target, - ] - - return [pathPoints, centerX, centerY, defaultOffsetX, defaultOffsetY] -} - -function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): string { - const bendSize = Math.min(distance(a, b) / 2, distance(b, c) / 2, size) - const { x, y } = b - - // no bend - if ((a.x === x && x === c.x) || (a.y === y && y === c.y)) { - return `L${x} ${y}` - } - - // first segment is horizontal - if (a.y === y) { - const xDir = a.x < c.x ? -1 : 1 - const yDir = a.y < c.y ? 1 : -1 - return `L ${x + bendSize * xDir},${y}Q ${x},${y} ${x},${y + bendSize * yDir}` - } - - const xDir = a.x < c.x ? 1 : -1 - const yDir = a.y < c.y ? -1 : 1 - return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}` -} - -/** - * Get a smooth step path from source to target handle - * @public - * - * @param smoothStepPathParams - * @param smoothStepPathParams.sourceX - The x position of the source handle - * @param smoothStepPathParams.sourceY - The y position of the source handle - * @param smoothStepPathParams.sourcePosition - The position of the source handle (default: Position.Bottom) - * @param smoothStepPathParams.targetX - The x position of the target handle - * @param smoothStepPathParams.targetY - The y position of the target handle - * @param smoothStepPathParams.targetPosition - The position of the target handle (default: Position.Top) - * @param smoothStepPathParams.borderRadius - The border radius of the edge (default: 5) - * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label - */ -export function getSmoothStepPath(smoothStepPathParams: GetSmoothStepPathParams): EdgePathParams { - const { - sourceX, - sourceY, - sourcePosition = Position.Bottom, - targetX, - targetY, - targetPosition = Position.Top, - borderRadius = 5, - centerX, - centerY, - offset = 20, - } = smoothStepPathParams - - const [points, labelX, labelY, offsetX, offsetY] = getPoints({ - source: { x: sourceX, y: sourceY }, - sourcePosition, - target: { x: targetX, y: targetY }, - targetPosition, - center: { x: centerX, y: centerY }, - offset, - }) - - const path = points.reduce((res, p, i) => { - let segment - - if (i > 0 && i < points.length - 1) { - segment = getBend(points[i - 1], p, points[i + 1], borderRadius) - } else { - segment = `${i === 0 ? 'M' : 'L'}${p.x} ${p.y}` - } - - res += segment - - return res - }, '') - - return [path, labelX, labelY, offsetX, offsetY] -} diff --git a/packages/core/src/components/Edges/utils/straight.ts b/packages/core/src/components/Edges/utils/straight.ts deleted file mode 100644 index f8dba4a93..000000000 --- a/packages/core/src/components/Edges/utils/straight.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { EdgePathParams } from './general' -import { getSimpleEdgeCenter } from './general' - -export interface GetStraightPathParams { - sourceX: number - sourceY: number - targetX: number - targetY: number -} - -/** - * Get a straight path from source to target handle - * @public - * - * @param straightEdgeParams - * @param straightEdgeParams.sourceX - The x position of the source handle - * @param straightEdgeParams.sourceY - The y position of the source handle - * @param straightEdgeParams.targetX - The x position of the target handle - * @param straightEdgeParams.targetY - The y position of the target handle - * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label - */ -export function getStraightPath(straightEdgeParams: GetStraightPathParams): EdgePathParams { - const { sourceX, sourceY, targetX, targetY } = straightEdgeParams - - const [centerX, centerY, offsetX, offsetY] = getSimpleEdgeCenter({ - sourceX, - sourceY, - targetX, - targetY, - }) - - return [`M ${sourceX},${sourceY}L ${targetX},${targetY}`, centerX, centerY, offsetX, offsetY] -} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index db0fb64e3..501f6cd54 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -18,14 +18,9 @@ export { default as BaseEdge } from './components/Edges/BaseEdge.vue' export { default as EdgeText } from './components/Edges/EdgeText.vue' export { default as EdgeLabelRenderer } from './components/Edges/EdgeLabelRenderer.vue' -export { - getBezierPath, - getSimpleBezierPath, - getSmoothStepPath, - getStraightPath, - getSimpleEdgeCenter, - getBezierEdgeCenter, -} from './components/Edges/utils' +// re-export these utils from system +export { getBezierPath, getSmoothStepPath, getStraightPath, getBezierEdgeCenter } from '@xyflow/system' +export { getSimpleBezierPath } from './components/Edges/SimpleBezierEdge' export { isNode, From 4c516f5c50d7e2d9fbeb61fce5088350e14dcd3a Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Tue, 16 Jul 2024 00:53:32 +0200 Subject: [PATCH 3/9] refactor(core,utils): replace exported utils with `@xyflow/system` exports (#1551) * refactor(core): replace `clamp` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `clampPosition` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `getDimensions` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `getHostForElement` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `getOverlappingArea` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `boxToRect` & `rectToBox` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `getBoundsOfRects` & `getBoundsOfBoxe` & `rendererPointToPoint` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(minimap): update import of `getBoundsOfRects` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `getMarkerId` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `isRect` & `isNumeric` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `calcAutoPan` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `isMouseEvent` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): replace `getEventPosition` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(core): remove `isMacOS` Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(changeset): add Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(changeset): add Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(core): cleanup Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --------- Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- .changeset/fifty-rockets-report.md | 5 + .changeset/fluffy-tables-wash.md | 26 +++++ .../src/components/ConnectionLine/index.ts | 4 +- .../core/src/components/Edges/EdgeWrapper.ts | 11 +- .../core/src/components/Handle/Handle.vue | 3 +- packages/core/src/composables/useDrag.ts | 11 +- packages/core/src/composables/useHandle.ts | 14 ++- .../core/src/composables/useResizeHandler.ts | 3 +- .../core/src/composables/useViewportHelper.ts | 5 +- .../EdgeRenderer/MarkerDefinitions.vue | 2 +- packages/core/src/container/Pane/Pane.vue | 3 +- .../core/src/container/Viewport/Viewport.vue | 3 +- packages/core/src/index.ts | 7 +- packages/core/src/store/actions.ts | 7 +- packages/core/src/store/state.ts | 2 +- packages/core/src/utils/autopan.ts | 28 ----- packages/core/src/utils/drag.ts | 3 +- packages/core/src/utils/edge.ts | 3 +- packages/core/src/utils/general.ts | 17 --- packages/core/src/utils/graph.ts | 102 +----------------- packages/core/src/utils/handle.ts | 3 +- packages/core/src/utils/index.ts | 1 - packages/core/src/utils/node.ts | 2 +- packages/minimap/src/MiniMap.vue | 4 +- 24 files changed, 71 insertions(+), 198 deletions(-) create mode 100644 .changeset/fifty-rockets-report.md create mode 100644 .changeset/fluffy-tables-wash.md delete mode 100644 packages/core/src/utils/autopan.ts diff --git a/.changeset/fifty-rockets-report.md b/.changeset/fifty-rockets-report.md new file mode 100644 index 000000000..7ab72fa33 --- /dev/null +++ b/.changeset/fifty-rockets-report.md @@ -0,0 +1,5 @@ +--- +"@vue-flow/minimap": patch +--- + +Update import of `getBoundsOfRects` diff --git a/.changeset/fluffy-tables-wash.md b/.changeset/fluffy-tables-wash.md new file mode 100644 index 000000000..df7f5a246 --- /dev/null +++ b/.changeset/fluffy-tables-wash.md @@ -0,0 +1,26 @@ +--- +"@vue-flow/core": minor +--- + +Replace existing graph utils exports with those already provided by `@xyflow/system`: + +- Replace utils + - `clamp` + - `clampPosition` + - `getDimensions` + - `getHostForElement` + - `getOverlappingArea` + - `rectToBox` + - `boxToRect` + - `getBoundsofRects` + - `getBoundsOfBoxes` + - `rendererPointToPoint` + - `getMarkerId` + - `isRect` + - `isNumeric` + - `calcAutoPan` + - `isMouseEvent` + - `getEventPosition` + +-Remove utils +- `isMacOS` diff --git a/packages/core/src/components/ConnectionLine/index.ts b/packages/core/src/components/ConnectionLine/index.ts index 70101ac8d..fe5ac7fb5 100644 --- a/packages/core/src/components/ConnectionLine/index.ts +++ b/packages/core/src/components/ConnectionLine/index.ts @@ -1,8 +1,8 @@ import { computed, defineComponent, h, inject } from 'vue' -import { getBezierPath, getSmoothStepPath } from '@xyflow/system' +import { getBezierPath, getMarkerId, getSmoothStepPath } from '@xyflow/system' import type { HandleElement } from '../../types' import { ConnectionLineType, ConnectionMode, Position } from '../../types' -import { getHandlePosition, getMarkerId } from '../../utils' +import { getHandlePosition } from '../../utils' import { useVueFlow } from '../../composables' import { Slots } from '../../context' import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge' diff --git a/packages/core/src/components/Edges/EdgeWrapper.ts b/packages/core/src/components/Edges/EdgeWrapper.ts index d8c61e9a5..4d2bc0826 100644 --- a/packages/core/src/components/Edges/EdgeWrapper.ts +++ b/packages/core/src/components/Edges/EdgeWrapper.ts @@ -1,17 +1,10 @@ import { computed, defineComponent, getCurrentInstance, h, inject, provide, ref, resolveComponent, toRef } from 'vue' +import { getMarkerId } from '@xyflow/system' import type { Connection, EdgeComponent, HandleType, MouseTouchEvent } from '../../types' import { ConnectionMode, Position } from '../../types' import { useEdgeHooks, useHandle, useVueFlow } from '../../composables' import { EdgeId, EdgeRef, Slots } from '../../context' -import { - ARIA_EDGE_DESC_KEY, - ErrorCode, - VueFlowError, - elementSelectionKeys, - getHandle, - getHandlePosition, - getMarkerId, -} from '../../utils' +import { ARIA_EDGE_DESC_KEY, ErrorCode, VueFlowError, elementSelectionKeys, getHandle, getHandlePosition } from '../../utils' import EdgeAnchor from './EdgeAnchor' interface Props { diff --git a/packages/core/src/components/Handle/Handle.vue b/packages/core/src/components/Handle/Handle.vue index aa67fac0e..62d59f46c 100644 --- a/packages/core/src/components/Handle/Handle.vue +++ b/packages/core/src/components/Handle/Handle.vue @@ -1,9 +1,10 @@ - - - - diff --git a/packages/core/src/container/Viewport/Viewport.vue b/packages/core/src/container/Viewport/Viewport.vue index cb4ed84e3..d856cf336 100644 --- a/packages/core/src/container/Viewport/Viewport.vue +++ b/packages/core/src/container/Viewport/Viewport.vue @@ -1,419 +1,29 @@ diff --git a/packages/core/src/container/VueFlow/VueFlow.vue b/packages/core/src/container/VueFlow/VueFlow.vue index f71a823b3..e4810efc6 100644 --- a/packages/core/src/container/VueFlow/VueFlow.vue +++ b/packages/core/src/container/VueFlow/VueFlow.vue @@ -1,7 +1,7 @@ + + + + diff --git a/packages/core/src/store/actions.ts b/packages/core/src/store/actions.ts index 4bc93c8aa..ae33f65fa 100644 --- a/packages/core/src/store/actions.ts +++ b/packages/core/src/store/actions.ts @@ -1,10 +1,8 @@ -import { zoomIdentity } from 'd3-zoom' import type { ComputedRef } from 'vue' import { until } from '@vueuse/core' -import { clamp, getDimensions, getOverlappingArea, isRectObject } from '@xyflow/system' +import { getDimensions, getOverlappingArea, isRectObject, panBy as panBySystem } from '@xyflow/system' import type { Actions, - CoordinateExtent, Edge, EdgeAddChange, EdgeLookup, @@ -285,17 +283,17 @@ export function useActions(state: State, nodeLookup: ComputedRef, ed } const setMinZoom: Actions['setMinZoom'] = (minZoom) => { - state.d3Zoom?.scaleExtent([minZoom, state.maxZoom]) + state.panZoom?.setScaleExtent([minZoom, state.maxZoom]) state.minZoom = minZoom } const setMaxZoom: Actions['setMaxZoom'] = (maxZoom) => { - state.d3Zoom?.scaleExtent([state.minZoom, maxZoom]) + state.panZoom?.setScaleExtent([state.minZoom, maxZoom]) state.maxZoom = maxZoom } const setTranslateExtent: Actions['setTranslateExtent'] = (translateExtent) => { - state.d3Zoom?.translateExtent(translateExtent) + state.panZoom?.setTranslateExtent(translateExtent) state.translateExtent = translateExtent } @@ -305,7 +303,7 @@ export function useActions(state: State, nodeLookup: ComputedRef, ed } const setPaneClickDistance: Actions['setPaneClickDistance'] = (clickDistance) => { - state.d3Zoom?.clickDistance(clickDistance) + state.panZoom?.setClickDistance(clickDistance) } const setInteractive: Actions['setInteractive'] = (isInteractive) => { @@ -647,44 +645,16 @@ export function useActions(state: State, nodeLookup: ComputedRef, ed } const panBy: Actions['panBy'] = (delta) => { - const { viewport, dimensions, d3Zoom, d3Selection, translateExtent } = state + const { viewport, dimensions, translateExtent, panZoom } = state - if (!d3Zoom || !d3Selection || (!delta.x && !delta.y)) { - return false - } - - const nextTransform = zoomIdentity.translate(viewport.x + delta.x, viewport.y + delta.y).scale(viewport.zoom) - - const extent: CoordinateExtent = [ - [0, 0], - [dimensions.width, dimensions.height], - ] - - const constrainedTransform = d3Zoom.constrain()(nextTransform, extent, translateExtent) - - const transformChanged = - state.viewport.x !== constrainedTransform.x || - state.viewport.y !== constrainedTransform.y || - state.viewport.zoom !== constrainedTransform.k - - d3Zoom.transform(d3Selection, constrainedTransform) - - return transformChanged + return panBySystem({ delta, panZoom, transform: [viewport.x, viewport.y, viewport.zoom], translateExtent, ...dimensions }) } const setState: Actions['setState'] = (options) => { const opts = options instanceof Function ? options(state) : options // these options cannot be set after initialization - const exclude: (keyof typeof opts)[] = [ - 'd3Zoom', - 'd3Selection', - 'd3ZoomHandler', - 'viewportRef', - 'vueFlowRef', - 'dimensions', - 'hooks', - ] + const exclude: (keyof typeof opts)[] = ['viewportRef', 'vueFlowRef', 'dimensions', 'hooks'] // we need to set the default opts before setting any elements so the options are applied to the elements on first render if (isDef(opts.defaultEdgeOptions)) { @@ -730,7 +700,7 @@ export function useActions(state: State, nodeLookup: ComputedRef, ed } } - until(() => state.d3Zoom) + until(() => state.panZoom) .not.toBeNull() .then(setSkippedOptions) @@ -819,21 +789,12 @@ export function useActions(state: State, nodeLookup: ComputedRef, ed state.edges = [] state.nodes = [] - // reset the zoom state - if (state.d3Zoom && state.d3Selection) { - const updatedTransform = zoomIdentity - .translate(resetState.defaultViewport.x ?? 0, resetState.defaultViewport.y ?? 0) - .scale(clamp(resetState.defaultViewport.zoom ?? 1, resetState.minZoom, resetState.maxZoom)) - - const bbox = state.viewportRef!.getBoundingClientRect() - - const extent: CoordinateExtent = [ - [0, 0], - [bbox.width, bbox.height], - ] - - const constrainedTransform = state.d3Zoom.constrain()(updatedTransform, extent, resetState.translateExtent) - state.d3Zoom.transform(state.d3Selection, constrainedTransform) + if (state.panZoom) { + state.panZoom.setViewport({ + x: state.defaultViewport.x ?? 0, + y: state.defaultViewport.y ?? 0, + zoom: state.defaultViewport.zoom ?? 1, + }) } setState(resetState) @@ -884,9 +845,7 @@ export function useActions(state: State, nodeLookup: ComputedRef, ed zoomOut: (transitionOpts) => viewportHelper.value.zoomOut(transitionOpts), zoomTo: (zoomLevel, transitionOpts) => viewportHelper.value.zoomTo(zoomLevel, transitionOpts), setViewport: (params, transitionOpts) => viewportHelper.value.setViewport(params, transitionOpts), - setTransform: (params, transitionOpts) => viewportHelper.value.setTransform(params, transitionOpts), getViewport: () => viewportHelper.value.getViewport(), - getTransform: () => viewportHelper.value.getTransform(), setCenter: (x, y, opts) => viewportHelper.value.setCenter(x, y, opts), fitBounds: (params, opts) => viewportHelper.value.fitBounds(params, opts), project: (params) => viewportHelper.value.project(params), diff --git a/packages/core/src/store/state.ts b/packages/core/src/store/state.ts index 72c253c25..b7caf66a8 100644 --- a/packages/core/src/store/state.ts +++ b/packages/core/src/store/state.ts @@ -22,9 +22,7 @@ export function useState(): State { }, viewport: { x: 0, y: 0, zoom: 1 }, - d3Zoom: null, - d3Selection: null, - d3ZoomHandler: null, + panZoom: null, minZoom: 0.5, maxZoom: 2, diff --git a/packages/core/src/types/flow.ts b/packages/core/src/types/flow.ts index 85fef24e0..aec8f4ce9 100644 --- a/packages/core/src/types/flow.ts +++ b/packages/core/src/types/flow.ts @@ -1,6 +1,6 @@ import type { CSSProperties } from 'vue' import type { KeyFilter } from '@vueuse/core' -import type { D3ZoomEvent } from 'd3-zoom' +import type { Viewport } from '@xyflow/system' import type { VueFlowError } from '../utils' import type { DefaultEdgeOptions, Edge, EdgeProps, EdgeUpdatable, GraphEdge } from './edge' import type { CoordinateExtent, CoordinateExtentRange, GraphNode, Node, NodeProps } from './node' @@ -13,9 +13,9 @@ import type { Connector, OnConnectStartParams, } from './connection' -import type { PanOnScrollMode, ViewportTransform } from './zoom' +import type { PanOnScrollMode } from './zoom' import type { EdgeTypesObject, NodeTypesObject } from './components' -import type { CustomEvent, EdgeMouseEvent, EdgeUpdateEvent, NodeDragEvent, NodeMouseEvent } from './hooks' +import type { CustomEvent, EdgeMouseEvent, EdgeUpdateEvent, MouseTouchEvent, NodeDragEvent, NodeMouseEvent } from './hooks' import type { ValidConnectionFunc } from './handle' import type { EdgeChange, NodeChange } from './changes' import type { VueFlowStore } from './store' @@ -137,7 +137,7 @@ export interface FlowExportObject { */ zoom: number /** exported viewport (position + zoom) */ - viewport: ViewportTransform + viewport: Viewport } export interface FlowProps { @@ -179,7 +179,7 @@ export interface FlowProps { panOnDrag?: boolean | number[] minZoom?: number maxZoom?: number - defaultViewport?: Partial + defaultViewport?: Partial translateExtent?: CoordinateExtent nodeExtent?: CoordinateExtent | CoordinateExtentRange defaultMarkerColor?: string @@ -261,18 +261,18 @@ export interface FlowEmits { } & OnConnectStartParams, ): void (event: 'clickConnectEnd', connectionEvent?: MouseEvent): void - (event: 'moveStart', moveEvent: { event: D3ZoomEvent; flowTransform: ViewportTransform }): void - (event: 'move', moveEvent: { event: D3ZoomEvent; flowTransform: ViewportTransform }): void - (event: 'moveEnd', moveEvent: { event: D3ZoomEvent; flowTransform: ViewportTransform }): void + (event: 'moveStart', moveEvent: { event: MouseTouchEvent | null; viewport: Viewport }): void + (event: 'move', moveEvent: { event: MouseTouchEvent | null; viewport: Viewport }): void + (event: 'moveEnd', moveEvent: { event: MouseTouchEvent | null; viewport: Viewport }): void (event: 'selectionDragStart', selectionEvent: NodeDragEvent): void (event: 'selectionDrag', selectionEvent: NodeDragEvent): void (event: 'selectionDragStop', selectionEvent: NodeDragEvent): void (event: 'selectionContextMenu', selectionEvent: { event: MouseEvent; nodes: GraphNode[] }): void (event: 'selectionStart', selectionEvent: MouseEvent): void (event: 'selectionEnd', selectionEvent: MouseEvent): void - (event: 'viewportChangeStart', viewport: ViewportTransform): void - (event: 'viewportChange', viewport: ViewportTransform): void - (event: 'viewportChangeEnd', viewport: ViewportTransform): void + (event: 'viewportChangeStart', viewport: Viewport): void + (event: 'viewportChange', viewport: Viewport): void + (event: 'viewportChangeEnd', viewport: Viewport): void /** @deprecated use `init` instead */ (event: 'paneReady', paneEvent: VueFlowStore): void (event: 'init', paneEvent: VueFlowStore): void diff --git a/packages/core/src/types/hooks.ts b/packages/core/src/types/hooks.ts index 03e8f1d17..c38ec0580 100644 --- a/packages/core/src/types/hooks.ts +++ b/packages/core/src/types/hooks.ts @@ -1,10 +1,9 @@ import type { EventHookOn, EventHookTrigger } from '@vueuse/core' -import type { D3ZoomEvent } from 'd3-zoom' +import type { Viewport } from '@xyflow/system' import type { EventHookExtended, VueFlowError } from '../utils' import type { GraphEdge } from './edge' import type { GraphNode } from './node' import type { Connection, OnConnectStartParams } from './connection' -import type { ViewportTransform } from './zoom' import type { EdgeChange, NodeChange } from './changes' import type { VueFlowStore } from './store' @@ -63,18 +62,18 @@ export interface FlowEvents { /** @deprecated use `init` instead */ paneReady: VueFlowStore init: VueFlowStore - move: { event: D3ZoomEvent | WheelEvent; flowTransform: ViewportTransform } - moveStart: { event: D3ZoomEvent | WheelEvent; flowTransform: ViewportTransform } - moveEnd: { event: D3ZoomEvent | WheelEvent; flowTransform: ViewportTransform } + move: { event: MouseTouchEvent | null; viewport: Viewport } + moveStart: { event: MouseTouchEvent | null; viewport: Viewport } + moveEnd: { event: MouseTouchEvent | null; viewport: Viewport } selectionDragStart: NodeDragEvent selectionDrag: NodeDragEvent selectionDragStop: NodeDragEvent selectionContextMenu: { event: MouseEvent; nodes: GraphNode[] } selectionStart: MouseEvent selectionEnd: MouseEvent - viewportChangeStart: ViewportTransform - viewportChange: ViewportTransform - viewportChangeEnd: ViewportTransform + viewportChangeStart: Viewport + viewportChange: Viewport + viewportChangeEnd: Viewport paneScroll: WheelEvent | undefined paneClick: MouseEvent paneContextMenu: MouseEvent diff --git a/packages/core/src/types/store.ts b/packages/core/src/types/store.ts index 481922fb5..b2a6fbb3c 100644 --- a/packages/core/src/types/store.ts +++ b/packages/core/src/types/store.ts @@ -1,5 +1,6 @@ import type { CSSProperties, ComputedRef, ToRefs } from 'vue' import type { KeyFilter } from '@vueuse/core' +import type { PanZoomInstance, Viewport } from '@xyflow/system' import type { ViewportHelper } from '../composables' import type { Dimensions, @@ -27,7 +28,7 @@ import type { } from './connection' import type { DefaultEdgeOptions, Edge, EdgeUpdatable, GraphEdge } from './edge' import type { CoordinateExtent, CoordinateExtentRange, GraphNode, Node } from './node' -import type { D3Selection, D3Zoom, D3ZoomHandler, PanOnScrollMode, ViewportTransform } from './zoom' +import type { PanOnScrollMode } from './zoom' import type { CustomEvent, FlowHooks, FlowHooksEmit, FlowHooksOn } from './hooks' import type { EdgeChange, NodeChange, NodeDragItem } from './changes' import type { ConnectingHandle, ValidConnectionFunc } from './handle' @@ -58,15 +59,14 @@ export interface State extends Omit { connectionLookup: ConnectionLookup - readonly d3Zoom: D3Zoom | null - readonly d3Selection: D3Selection | null - readonly d3ZoomHandler: D3ZoomHandler | null + /** The panzoom instance */ + panZoom: PanZoomInstance | null /** use setMinZoom action to change minZoom */ minZoom: number /** use setMaxZoom action to change maxZoom */ maxZoom: number - defaultViewport: Partial + defaultViewport: Partial /** use setTranslateExtent action to change translateExtent */ translateExtent: CoordinateExtent nodeExtent: CoordinateExtent | CoordinateExtentRange @@ -74,7 +74,7 @@ export interface State extends Omit { /** viewport dimensions - do not change! */ readonly dimensions: Dimensions /** viewport transform x, y, z - do not change! */ - readonly viewport: ViewportTransform + readonly viewport: Viewport /** if true will skip rendering any elements currently not inside viewport until they become visible */ onlyRenderVisibleElements: boolean nodesSelectionActive: boolean @@ -283,11 +283,11 @@ export interface Actions extends Omit { * unselect selected elements (if none are passed, all elements are unselected) */ removeSelectedElements: (elements?: Elements) => void - /** apply min zoom value to d3 */ + /** apply min zoom value to panzoom */ setMinZoom: (zoom: number) => void - /** apply max zoom value to d3 */ + /** apply max zoom value to panzoom */ setMaxZoom: (zoom: number) => void - /** apply translate extent to d3 */ + /** apply translate extent to panzoom */ setTranslateExtent: (translateExtent: CoordinateExtent) => void /** apply extent to nodes */ setNodeExtent: (nodeExtent: CoordinateExtent | CoordinateExtentRange) => void @@ -325,7 +325,7 @@ export interface Actions extends Omit { /** get a node's connected edges */ getConnectedEdges: (nodesOrId: Node[] | string) => GraphEdge[] /** pan the viewport; return indicates if a transform has happened or not */ - panBy: (delta: XYPosition) => boolean + panBy: (delta: XYPosition) => Promise /** viewport helper instance */ viewportHelper: ComputedRef diff --git a/packages/core/src/types/zoom.ts b/packages/core/src/types/zoom.ts index 198baa1e8..ccf1ce36b 100644 --- a/packages/core/src/types/zoom.ts +++ b/packages/core/src/types/zoom.ts @@ -1,11 +1,6 @@ -import type { Selection } from 'd3-selection' -import type { ZoomBehavior } from 'd3-zoom' +import type { Viewport } from '@xyflow/system' import type { Rect, XYPosition } from './flow' -export type D3Zoom = ZoomBehavior -export type D3Selection = Selection -export type D3ZoomHandler = (this: HTMLDivElement, event: any, d: unknown) => void - export enum PanOnScrollMode { Free = 'free', Vertical = 'vertical', @@ -28,12 +23,6 @@ export type FitViewParams = { nodes?: string[] } & TransitionOptions -export interface ViewportTransform { - x: number - y: number - zoom: number -} - export type SetCenterOptions = TransitionOptions & { zoom?: number } @@ -60,22 +49,18 @@ export type ZoomInOut = (options?: TransitionOptions) => Promise /** zoom to a specific level */ export type ZoomTo = (zoomLevel: number, options?: TransitionOptions) => Promise -/** get current viewport transform */ -export type GetViewport = () => ViewportTransform +/** get current viewport */ +export type GetViewport = () => Viewport -/** set current viewport transform */ -export type SetViewport = (transform: ViewportTransform, options?: TransitionOptions) => Promise +/** set current viewport */ +export type SetViewport = (viewport: Viewport, options?: TransitionOptions) => Promise export interface ViewportFunctions { zoomIn: ZoomInOut zoomOut: ZoomInOut zoomTo: ZoomTo setViewport: SetViewport - /** @deprecated use setViewport instead */ - setTransform: SetViewport getViewport: GetViewport - /** @deprecated use getViewport instead */ - getTransform: GetViewport fitView: FitView setCenter: SetCenter fitBounds: FitBounds diff --git a/packages/core/src/utils/edge.ts b/packages/core/src/utils/edge.ts index 99c2021c9..93b427b17 100644 --- a/packages/core/src/utils/edge.ts +++ b/packages/core/src/utils/edge.ts @@ -1,5 +1,6 @@ +import type { Viewport } from '@xyflow/system' import { rectToBox } from '@xyflow/system' -import type { Actions, GraphEdge, GraphNode, HandleElement, ViewportTransform, XYPosition } from '../types' +import type { Actions, GraphEdge, GraphNode, HandleElement, XYPosition } from '../types' import { Position } from '../types' import { getNodeDimensions } from '.' @@ -54,7 +55,7 @@ interface IsEdgeVisibleParams { targetHeight: number width: number height: number - viewport: ViewportTransform + viewport: Viewport } export function isEdgeVisible({ diff --git a/packages/core/src/utils/errors.ts b/packages/core/src/utils/errors.ts index 262a1b50e..ceebebe0c 100644 --- a/packages/core/src/utils/errors.ts +++ b/packages/core/src/utils/errors.ts @@ -23,22 +23,25 @@ const messages = { [ErrorCode.MISSING_STYLES]: () => `It seems that you haven't loaded the necessary styles. Please import '@vue-flow/core/dist/style.css' to ensure that the graph is rendered correctly`, [ErrorCode.MISSING_VIEWPORT_DIMENSIONS]: () => 'The Vue Flow parent container needs a width and a height to render the graph', - [ErrorCode.NODE_INVALID]: (id?: string) => `Node is invalid\nNode: ${id}`, - [ErrorCode.NODE_NOT_FOUND]: (id: string | null) => `Node not found\nNode: ${id}`, - [ErrorCode.NODE_MISSING_PARENT]: (id: string, parentId: string) => `Node is missing a parent\nNode: ${id}\nParent: ${parentId}`, + [ErrorCode.NODE_INVALID]: (id?: string) => `Node is invalid\nNode id: ${id}`, + [ErrorCode.NODE_NOT_FOUND]: (id: string | null) => `Node not found\nNode id: ${id}`, + [ErrorCode.NODE_MISSING_PARENT]: (id: string, parentId: string) => + `Node is missing a parent\nNode id: ${id}\nParent id: ${parentId}`, [ErrorCode.NODE_TYPE_MISSING]: (type: string) => `Node type is missing\nType: ${type}`, - [ErrorCode.NODE_EXTENT_INVALID]: (id: string) => `Only child nodes can use a parent extent\nNode: ${id}`, - [ErrorCode.EDGE_INVALID]: (id: string) => `An edge needs a source and a target\nEdge: ${id}`, - [ErrorCode.EDGE_SOURCE_MISSING]: (id: string, source: string) => `Edge source is missing\nEdge: ${id} \nSource: ${source}`, - [ErrorCode.EDGE_TARGET_MISSING]: (id: string, target: string) => `Edge target is missing\nEdge: ${id} \nTarget: ${target}`, + [ErrorCode.NODE_EXTENT_INVALID]: (id: string) => `Only child nodes can use a parent extent\nNode id: ${id}`, + [ErrorCode.EDGE_INVALID]: (id: string) => `An edge needs a source and a target\nEdge id: ${id}`, + [ErrorCode.EDGE_SOURCE_MISSING]: (id: string, source: string) => + `Edge source is missing\nEdge id: ${id} \nSource id: ${source}`, + [ErrorCode.EDGE_TARGET_MISSING]: (id: string, target: string) => + `Edge target is missing\nEdge id: ${id} \nTarget id: ${target}`, [ErrorCode.EDGE_TYPE_MISSING]: (type: string) => `Edge type is missing\nType: ${type}`, [ErrorCode.EDGE_SOURCE_TARGET_SAME]: (id: string, source: string, target: string) => - `Edge source and target are the same\nEdge: ${id} \nSource: ${source} \nTarget: ${target}`, + `Edge source and target are the same\nEdge id: ${id} \nSource id: ${source} \nTarget id: ${target}`, [ErrorCode.EDGE_SOURCE_TARGET_MISSING]: (id: string, source: string, target: string) => - `Edge source or target is missing\nEdge: ${id} \nSource: ${source} \nTarget: ${target}`, + `Edge source or target is missing\nEdge id: ${id} \nSource id: ${source} \nTarget id: ${target}`, [ErrorCode.EDGE_ORPHANED]: (id: string) => - `Edge was orphaned (suddenly missing source or target) and has been removed\nEdge: ${id}`, - [ErrorCode.EDGE_NOT_FOUND]: (id: string) => `Edge not found\nEdge: ${id}`, + `Edge was orphaned (suddenly missing source or target) and has been removed\nEdge id: ${id}`, + [ErrorCode.EDGE_NOT_FOUND]: (id: string) => `Edge not found\nEdge id: ${id}`, // deprecation errors [ErrorCode.USEVUEFLOW_OPTIONS]: () => diff --git a/packages/core/src/utils/graph.ts b/packages/core/src/utils/graph.ts index aeee4c2be..dafa95e90 100644 --- a/packages/core/src/utils/graph.ts +++ b/packages/core/src/utils/graph.ts @@ -1,4 +1,5 @@ import { markRaw } from 'vue' +import type { Viewport } from '@xyflow/system' import { boxToRect, clamp, getBoundsOfBoxes, getOverlappingArea, rectToBox } from '@xyflow/system' import type { Actions, @@ -15,7 +16,6 @@ import type { MaybeElement, Node, Rect, - ViewportTransform, XYPosition, XYZPosition, } from '../types' @@ -179,7 +179,7 @@ export function connectionExists(edge: Edge | Connection, elements: Elements) { export function pointToRendererPoint( { x, y }: XYPosition, - { x: tx, y: ty, zoom: tScale }: ViewportTransform, + { x: tx, y: ty, zoom: tScale }: Viewport, snapToGrid: boolean = false, [snapX, snapY]: [snapX: number, snapY: number] = [1, 1], ): XYPosition { @@ -223,7 +223,7 @@ export function getRectOfNodes(nodes: GraphNode[]) { export function getNodesInside( nodes: GraphNode[], rect: Rect, - viewport: ViewportTransform = { x: 0, y: 0, zoom: 1 }, + viewport: Viewport = { x: 0, y: 0, zoom: 1 }, partially = false, // set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute excludeNonSelectableNodes = false, @@ -307,7 +307,7 @@ export function getTransformForBounds( x?: number y?: number } = { x: 0, y: 0 }, -): ViewportTransform { +): Viewport { const xZoom = width / (bounds.width * (1 + padding)) const yZoom = height / (bounds.height * (1 + padding)) const zoom = Math.min(xZoom, yZoom) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3451a9ac9..1532832c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -296,9 +296,6 @@ importers: d3-selection: specifier: ^3.0.0 version: 3.0.0 - d3-zoom: - specifier: ^3.0.0 - version: 3.0.0 vue: specifier: ^3.3.0 version: 3.3.4 @@ -318,9 +315,6 @@ importers: '@types/d3-selection': specifier: ^3.0.7 version: 3.0.7 - '@types/d3-zoom': - specifier: ^3.0.5 - version: 3.0.5 '@vitejs/plugin-vue': specifier: ^4.4.0 version: 4.4.0(vite@4.4.11(@types/node@20.14.2)(sass@1.32.12)(terser@5.21.0))(vue@3.3.4) From 3aff262a3fdb481fc126e55364bf524f2ede2867 Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Sat, 27 Jul 2024 13:32:51 +0200 Subject: [PATCH 5/9] refactor(minimap)!: use xy minimap instance (#1560) * refactor(minimap)!: use xy minimap instance Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(minimap): cleanup Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(minimap): cleanup deps Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(changeset): add Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * refactor(node-toolbar): replace `ViewportTransform` type Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(node-toolbar): cleanup deps Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(core): re-export Viewport type Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(core): update system Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(docs): cleanup dead links Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(core): cleanup exports Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --------- Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- .changeset/seven-ads-wait.md | 5 + docs/src/guide/vue-flow/config.md | 2 +- packages/core/package.json | 2 +- packages/core/src/index.ts | 3 + packages/minimap/package.json | 5 +- packages/minimap/src/MiniMap.vue | 111 ++++++++++------------ packages/node-toolbar/package.json | 1 + packages/node-toolbar/src/NodeToolbar.vue | 19 ++-- pnpm-lock.yaml | 36 +++---- 9 files changed, 84 insertions(+), 100 deletions(-) create mode 100644 .changeset/seven-ads-wait.md diff --git a/.changeset/seven-ads-wait.md b/.changeset/seven-ads-wait.md new file mode 100644 index 000000000..6d7a11e61 --- /dev/null +++ b/.changeset/seven-ads-wait.md @@ -0,0 +1,5 @@ +--- +"@vue-flow/minimap": minor +--- + +Replace d3 with xyflow minimap instance diff --git a/docs/src/guide/vue-flow/config.md b/docs/src/guide/vue-flow/config.md index 3e3ecf80c..6fb28582f 100644 --- a/docs/src/guide/vue-flow/config.md +++ b/docs/src/guide/vue-flow/config.md @@ -498,7 +498,7 @@ const edges = ref([ ### default-viewport (optional) -- Type: [`ViewportTransform`](/typedocs/interfaces/ViewportTransform) +- Type: `Viewport` - Default: `{ zoom: 1, position: { x: 0, y: 0 } }` diff --git a/packages/core/package.json b/packages/core/package.json index 8e982c9a0..7aaf706a5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -73,7 +73,7 @@ "@vueuse/core": "^10.5.0", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", - "@xyflow/system": "^0.0.36" + "@xyflow/system": "^0.0.37" }, "devDependencies": { "@rollup/plugin-replace": "^5.0.3", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 24ca39f3d..df0d5dc01 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -67,3 +67,6 @@ export { useKeyPress } from './composables/useKeyPress' export { VueFlowError, ErrorCode, isErrorOfType } from './utils/errors' export * from './types' + +// todo: add more re-exports +export { type Viewport } from '@xyflow/system' diff --git a/packages/minimap/package.json b/packages/minimap/package.json index 3fed8bfff..c51d46e24 100644 --- a/packages/minimap/package.json +++ b/packages/minimap/package.json @@ -61,15 +61,12 @@ "vue": "^3.3.0" }, "dependencies": { - "d3-selection": "^3.0.0", - "d3-zoom": "^3.0.0" + "@xyflow/system": "^0.0.37" }, "devDependencies": { "@tooling/eslint-config": "workspace:*", "@tooling/tsconfig": "workspace:*", "@tooling/vite-config": "workspace:*", - "@types/d3-selection": "^3.0.7", - "@types/d3-zoom": "^3.0.5", "@vue-flow/core": "workspace:*", "vue-tsc": "^1.8.16" }, diff --git a/packages/minimap/src/MiniMap.vue b/packages/minimap/src/MiniMap.vue index e745f60b9..8cd662027 100644 --- a/packages/minimap/src/MiniMap.vue +++ b/packages/minimap/src/MiniMap.vue @@ -1,10 +1,9 @@ diff --git a/packages/core/src/container/ZoomPane/ZoomPane.vue b/packages/core/src/container/ZoomPane/ZoomPane.vue index dbfecea4d..a5dc42f06 100644 --- a/packages/core/src/container/ZoomPane/ZoomPane.vue +++ b/packages/core/src/container/ZoomPane/ZoomPane.vue @@ -138,11 +138,15 @@ export default {