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 01/10] 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 02/10] 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 03/10] 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 05/10] 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/index.ts b/packages/core/src/index.ts index df0d5dc01..117de9fa5 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -69,4 +69,4 @@ export { VueFlowError, ErrorCode, isErrorOfType } from './utils/errors' export * from './types' // todo: add more re-exports -export { type Viewport } from '@xyflow/system' +export { type Viewport, PanOnScrollMode } from '@xyflow/system' diff --git a/packages/core/src/store/state.ts b/packages/core/src/store/state.ts index b7caf66a8..85a798fd7 100644 --- a/packages/core/src/store/state.ts +++ b/packages/core/src/store/state.ts @@ -1,6 +1,6 @@ -import { isMacOs } from '@xyflow/system' +import { PanOnScrollMode, isMacOs } from '@xyflow/system' import type { FlowOptions, State } from '../types' -import { ConnectionLineType, ConnectionMode, PanOnScrollMode, SelectionMode } from '../types' +import { ConnectionLineType, ConnectionMode, SelectionMode } from '../types' import { createHooks } from './hooks' diff --git a/packages/core/src/types/flow.ts b/packages/core/src/types/flow.ts index aec8f4ce9..36a9b4564 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 { Viewport } from '@xyflow/system' +import type { PanOnScrollMode, 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,7 +13,6 @@ import type { Connector, OnConnectStartParams, } from './connection' -import type { PanOnScrollMode } from './zoom' import type { EdgeTypesObject, NodeTypesObject } from './components' import type { CustomEvent, EdgeMouseEvent, EdgeUpdateEvent, MouseTouchEvent, NodeDragEvent, NodeMouseEvent } from './hooks' import type { ValidConnectionFunc } from './handle' diff --git a/packages/core/src/types/store.ts b/packages/core/src/types/store.ts index b2a6fbb3c..8d725bedf 100644 --- a/packages/core/src/types/store.ts +++ b/packages/core/src/types/store.ts @@ -1,6 +1,6 @@ import type { CSSProperties, ComputedRef, ToRefs } from 'vue' import type { KeyFilter } from '@vueuse/core' -import type { PanZoomInstance, Viewport } from '@xyflow/system' +import type { PanOnScrollMode, PanZoomInstance, Viewport } from '@xyflow/system' import type { ViewportHelper } from '../composables' import type { Dimensions, @@ -28,7 +28,6 @@ import type { } from './connection' import type { DefaultEdgeOptions, Edge, EdgeUpdatable, GraphEdge } from './edge' import type { CoordinateExtent, CoordinateExtentRange, GraphNode, Node } from './node' -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' diff --git a/packages/core/src/types/zoom.ts b/packages/core/src/types/zoom.ts index ccf1ce36b..05ae9994b 100644 --- a/packages/core/src/types/zoom.ts +++ b/packages/core/src/types/zoom.ts @@ -1,12 +1,6 @@ import type { Viewport } from '@xyflow/system' import type { Rect, XYPosition } from './flow' -export enum PanOnScrollMode { - Free = 'free', - Vertical = 'vertical', - Horizontal = 'horizontal', -} - export interface TransitionOptions { duration?: number } From 084d57bf625d438b9024cfb17e84a925d902b694 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Sun, 23 Nov 2025 14:11:40 +0100 Subject: [PATCH 07/10] refactor(core): align node type(s) with xyflow system Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- packages/core/package.json | 2 +- .../core/src/components/Edges/EdgeWrapper.ts | 3 +- .../core/src/components/Handle/Handle.vue | 20 +- .../core/src/components/Nodes/DefaultNode.ts | 17 +- .../core/src/components/Nodes/InputNode.ts | 14 +- .../core/src/components/Nodes/NodeWrapper.ts | 22 +- .../core/src/components/Nodes/OutputNode.ts | 14 +- packages/core/src/composables/useDrag.ts | 6 +- packages/core/src/composables/useEdge.ts | 6 +- packages/core/src/composables/useEdgeHooks.ts | 13 +- packages/core/src/composables/useHandle.ts | 16 +- packages/core/src/composables/useNode.ts | 6 +- packages/core/src/composables/useNodeHooks.ts | 13 +- packages/core/src/composables/useVueFlow.ts | 29 ++- .../core/src/composables/useWatchProps.ts | 60 +---- packages/core/src/container/Pane/Pane.vue | 6 +- .../core/src/container/VueFlow/VueFlow.vue | 15 +- packages/core/src/index.ts | 2 - packages/core/src/store/actions.ts | 207 ++++-------------- packages/core/src/store/getters.ts | 61 ++---- packages/core/src/store/state.ts | 7 +- packages/core/src/types/changes.ts | 11 +- packages/core/src/types/components.ts | 13 +- packages/core/src/types/edge.ts | 54 ++--- packages/core/src/types/flow.ts | 120 +++------- packages/core/src/types/handle.ts | 2 +- packages/core/src/types/hooks.ts | 120 +++++----- packages/core/src/types/node.ts | 182 ++------------- packages/core/src/types/store.ts | 130 ++++------- packages/core/src/utils/changes.ts | 31 +-- packages/core/src/utils/graph.ts | 109 +++------ packages/core/src/utils/storage.ts | 26 +-- packages/core/src/utils/store.ts | 8 +- packages/minimap/package.json | 2 +- packages/node-toolbar/package.json | 2 +- pnpm-lock.yaml | 35 +-- 36 files changed, 415 insertions(+), 969 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 7aaf706a5..4606a7e56 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.37" + "@xyflow/system": "^0.0.73" }, "devDependencies": { "@rollup/plugin-replace": "^5.0.3", diff --git a/packages/core/src/components/Edges/EdgeWrapper.ts b/packages/core/src/components/Edges/EdgeWrapper.ts index 4d2bc0826..99bb1f207 100644 --- a/packages/core/src/components/Edges/EdgeWrapper.ts +++ b/packages/core/src/components/Edges/EdgeWrapper.ts @@ -38,7 +38,7 @@ const EdgeWrapper = defineComponent({ const edge = computed(() => findEdge(props.id)!) - const hooks = useEdgeHooks(edge.value, emits) + const hooks = useEdgeHooks(emits) const slots = inject(Slots) @@ -219,7 +219,6 @@ const EdgeWrapper = defineComponent({ labelBgPadding: edge.value.labelBgPadding, labelBgBorderRadius: edge.value.labelBgBorderRadius, data: edge.value.data, - events: { ...edge.value.events, ...hooks.on }, style: edgeStyle.value, markerStart: `url('#${getMarkerId(edge.value.markerStart, vueFlowId)}')`, markerEnd: `url('#${getMarkerId(edge.value.markerEnd, vueFlowId)}')`, diff --git a/packages/core/src/components/Handle/Handle.vue b/packages/core/src/components/Handle/Handle.vue index 62d59f46c..a53d581f4 100644 --- a/packages/core/src/components/Handle/Handle.vue +++ b/packages/core/src/components/Handle/Handle.vue @@ -8,7 +8,7 @@ import { isDef } from '../../utils' const { position = Position.Top, - connectable = undefined, + isConnectable = undefined, connectableStart = true, connectableEnd = true, id: handleId = null, @@ -61,8 +61,8 @@ const { handlePointerDown, handleClick } = useHandle({ type, }) -const isConnectable = computed(() => { - if (typeof connectable === 'string' && connectable === 'single') { +const isHandleConnectable = computed(() => { + if (typeof isConnectable === 'string' && isConnectable === 'single') { return !connectedEdges.value.some((edge) => { const id = edge[`${type.value}Handle`] @@ -74,7 +74,7 @@ const isConnectable = computed(() => { }) } - if (typeof connectable === 'number') { + if (typeof isConnectable === 'number') { return ( connectedEdges.value.filter((edge) => { const id = edge[`${type.value}Handle`] @@ -84,15 +84,15 @@ const isConnectable = computed(() => { } return id ? id === handleId : true - }).length < connectable + }).length < isConnectable ) } - if (typeof connectable === 'function') { - return connectable(node, connectedEdges.value) + if (typeof isConnectable === 'function') { + return isConnectable(node, connectedEdges.value) } - return isDef(connectable) ? connectable : nodesConnectable.value + return isDef(isConnectable) ? isConnectable : nodesConnectable.value }) // todo: remove this and have users handle this themselves using `updateNodeInternals` @@ -145,7 +145,7 @@ onUnmounted(() => { function onPointerDown(event: MouseEvent | TouchEvent) { const isMouseTriggered = isMouseEvent(event) - if (isConnectable.value && isConnectableStart.value && ((isMouseTriggered && event.button === 0) || !isMouseTriggered)) { + if (isHandleConnectable.value && isConnectableStart.value && ((isMouseTriggered && event.button === 0) || !isMouseTriggered)) { handlePointerDown(event) } } @@ -155,7 +155,7 @@ function onClick(event: MouseEvent) { return } - if (isConnectable.value) { + if (isHandleConnectable.value) { handleClick(event) } } diff --git a/packages/core/src/components/Nodes/DefaultNode.ts b/packages/core/src/components/Nodes/DefaultNode.ts index 839e7a7f7..34d6da107 100644 --- a/packages/core/src/components/Nodes/DefaultNode.ts +++ b/packages/core/src/components/Nodes/DefaultNode.ts @@ -1,28 +1,25 @@ import type { Component, FunctionalComponent } from 'vue' import { h } from 'vue' import Handle from '../Handle/Handle.vue' -import type { NodeProps } from '../../types' +import type { BuiltInNode, NodeProps } from '../../types' import { Position } from '../../types' -const DefaultNode: FunctionalComponent> = function ({ +const DefaultNode: FunctionalComponent> = function ({ sourcePosition = Position.Bottom, targetPosition = Position.Top, - label: _label, - connectable = true, - isValidTargetPos, - isValidSourcePos, + isConnectable = true, data, }) { - const label = data.label || _label + const label = data.label return [ - h(Handle as Component, { type: 'target', position: targetPosition, connectable, isValidConnection: isValidTargetPos }), + h(Handle as Component, { type: 'target', position: targetPosition, isConnectable }), typeof label !== 'string' && label ? h(label) : h('div', { innerHTML: label }), - h(Handle as Component, { type: 'source', position: sourcePosition, connectable, isValidConnection: isValidSourcePos }), + h(Handle as Component, { type: 'source', position: sourcePosition, isConnectable }), ] } -DefaultNode.props = ['sourcePosition', 'targetPosition', 'label', 'isValidTargetPos', 'isValidSourcePos', 'connectable', 'data'] +DefaultNode.props = ['sourcePosition', 'targetPosition', 'isConnectable', 'data'] DefaultNode.inheritAttrs = false DefaultNode.compatConfig = { MODE: 3 } diff --git a/packages/core/src/components/Nodes/InputNode.ts b/packages/core/src/components/Nodes/InputNode.ts index c963faaf0..c7a54676f 100644 --- a/packages/core/src/components/Nodes/InputNode.ts +++ b/packages/core/src/components/Nodes/InputNode.ts @@ -1,25 +1,23 @@ import type { Component, FunctionalComponent } from 'vue' import { h } from 'vue' import Handle from '../Handle/Handle.vue' -import type { NodeProps } from '../../types' +import type { BuiltInNode, NodeProps } from '../../types' import { Position } from '../../types' -const InputNode: FunctionalComponent> = function ({ +const InputNode: FunctionalComponent> = function ({ sourcePosition = Position.Bottom, - label: _label, - connectable = true, - isValidSourcePos, + isConnectable = true, data, }) { - const label = data.label || _label + const label = data.label return [ typeof label !== 'string' && label ? h(label) : h('div', { innerHTML: label }), - h(Handle as Component, { type: 'source', position: sourcePosition, connectable, isValidConnection: isValidSourcePos }), + h(Handle as Component, { type: 'source', position: sourcePosition, isConnectable }), ] } -InputNode.props = ['sourcePosition', 'label', 'isValidSourcePos', 'connectable', 'data'] +InputNode.props = ['sourcePosition', 'isConnectable', 'data'] InputNode.inheritAttrs = false InputNode.compatConfig = { MODE: 3 } diff --git a/packages/core/src/components/Nodes/NodeWrapper.ts b/packages/core/src/components/Nodes/NodeWrapper.ts index 9c443ea49..ffb49addb 100644 --- a/packages/core/src/components/Nodes/NodeWrapper.ts +++ b/packages/core/src/components/Nodes/NodeWrapper.ts @@ -25,7 +25,7 @@ import { } from '../../utils' import { NodeId, NodeRef, Slots } from '../../context' import { isInputDOMNode, useDrag, useNode, useNodeHooks, useUpdateNodePositions, useVueFlow } from '../../composables' -import type { NodeComponent } from '../../types' +import type { BuiltInNode, NodeComponent } from '../../types' interface Props { id: string @@ -92,7 +92,7 @@ const NodeWrapper = defineComponent({ return slot } - let nodeType = node.template || getNodeTypes.value[name] + let nodeType = getNodeTypes.value[name] if (typeof nodeType === 'string') { if (instance) { @@ -112,7 +112,7 @@ const NodeWrapper = defineComponent({ return false }) - const { emit, on } = useNodeHooks(node, emits) + const { emit } = useNodeHooks(emits) const dragging = useDrag({ id: props.id, @@ -139,15 +139,15 @@ const NodeWrapper = defineComponent({ const getStyle = computed(() => { const styles = (node.style instanceof Function ? node.style(node) : node.style) || {} - const width = node.width instanceof Function ? node.width(node) : node.width - const height = node.height instanceof Function ? node.height(node) : node.height + const width = node.width + const height = node.height if (width) { - styles.width = typeof width === 'string' ? width : `${width}px` + styles.width = `${width}px` } if (height) { - styles.height = typeof height === 'string' ? height : `${height}px` + styles.height = `${height}px` } return styles @@ -228,7 +228,7 @@ const NodeWrapper = defineComponent({ // if extent is parent, we need dimensions to properly clamp the position if ( node.extent === 'parent' || - (typeof node.extent === 'object' && 'range' in node.extent && node.extent.range === 'parent') + (!!node.extent && typeof node.extent === 'object' && 'range' in node.extent && node.extent.range === 'parent') ) { until(() => isInit) .toBe(true) @@ -282,25 +282,21 @@ const NodeWrapper = defineComponent({ 'onKeydown': onKeyDown, }, [ - h(nodeCmp.value === false ? getNodeTypes.value.default : (nodeCmp.value as any), { + h(nodeCmp.value === false ? (getNodeTypes.value.default as NodeComponent) : (nodeCmp.value as any), { id: node.id, type: node.type, data: node.data, - events: { ...node.events, ...on }, selected: node.selected, resizing: node.resizing, dragging: dragging.value, connectable: isConnectable.value, position: node.computedPosition, dimensions: node.dimensions, - isValidTargetPos: node.isValidTargetPos, - isValidSourcePos: node.isValidSourcePos, parent: node.parentNode, parentNodeId: node.parentNode, zIndex: node.computedPosition.z ?? zIndex.value, targetPosition: node.targetPosition, sourcePosition: node.sourcePosition, - label: node.label, dragHandle: node.dragHandle, onUpdateNodeInternals: updateInternals, }), diff --git a/packages/core/src/components/Nodes/OutputNode.ts b/packages/core/src/components/Nodes/OutputNode.ts index 02fb7933b..4ac9c85c7 100644 --- a/packages/core/src/components/Nodes/OutputNode.ts +++ b/packages/core/src/components/Nodes/OutputNode.ts @@ -1,25 +1,23 @@ import type { Component, FunctionalComponent } from 'vue' import { h } from 'vue' import Handle from '../Handle/Handle.vue' -import type { NodeProps } from '../../types' +import type { BuiltInNode, NodeProps } from '../../types' import { Position } from '../../types' -const OutputNode: FunctionalComponent> = function ({ +const OutputNode: FunctionalComponent> = function ({ targetPosition = Position.Top, - label: _label, - connectable = true, - isValidTargetPos, + isConnectable, data, }) { - const label = data.label || _label + const label = data.label return [ - h(Handle as Component, { type: 'target', position: targetPosition, connectable, isValidConnection: isValidTargetPos }), + h(Handle as Component, { type: 'target', position: targetPosition, isConnectable }), typeof label !== 'string' && label ? h(label) : h('div', { innerHTML: label }), ] } -OutputNode.props = ['targetPosition', 'label', 'isValidTargetPos', 'connectable', 'data'] +OutputNode.props = ['targetPosition', 'isConnectable', 'data'] OutputNode.inheritAttrs = false OutputNode.compatConfig = { MODE: 3 } diff --git a/packages/core/src/composables/useDrag.ts b/packages/core/src/composables/useDrag.ts index 4ae62884f..950c7e001 100644 --- a/packages/core/src/composables/useDrag.ts +++ b/packages/core/src/composables/useDrag.ts @@ -46,7 +46,7 @@ export function useDrag(params: UseDragParams) { multiSelectionActive, nodesSelectionActive, selectNodesOnDrag, - removeSelectedElements, + removeSelectedNodes, addSelectedNodes, updateNodePositions, emits, @@ -148,7 +148,7 @@ export function useDrag(params: UseDragParams) { if (!selectNodesOnDrag.value && !multiSelectionActive.value && node) { if (!node.selected) { // we need to reset selected nodes when selectNodesOnDrag=false - removeSelectedElements() + removeSelectedNodes() } } @@ -157,7 +157,7 @@ export function useDrag(params: UseDragParams) { node, multiSelectionActive.value, addSelectedNodes, - removeSelectedElements, + removeSelectedNodes, nodesSelectionActive, false, nodeEl as HTMLDivElement, diff --git a/packages/core/src/composables/useEdge.ts b/packages/core/src/composables/useEdge.ts index 8a1d92871..375f6f5fb 100644 --- a/packages/core/src/composables/useEdge.ts +++ b/packages/core/src/composables/useEdge.ts @@ -1,5 +1,5 @@ import { inject, ref } from 'vue' -import type { CustomEvent, ElementData } from '../types' +import type { ElementData } from '../types' import { ErrorCode, VueFlowError } from '../utils' import { EdgeId, EdgeRef } from '../context' import { useVueFlow } from './useVueFlow' @@ -15,13 +15,13 @@ import { useVueFlow } from './useVueFlow' * @param id - The id of the edge to access * @returns the edge id, the edge and the edge dom element */ -export function useEdge = any>(id?: string) { +export function useEdge(id?: string) { const edgeId = id ?? inject(EdgeId, '') const edgeEl = inject(EdgeRef, ref(null)) const { findEdge, emits } = useVueFlow() - const edge = findEdge(edgeId)! + const edge = findEdge(edgeId)! if (!edge) { emits.error(new VueFlowError(ErrorCode.EDGE_NOT_FOUND, edgeId)) diff --git a/packages/core/src/composables/useEdgeHooks.ts b/packages/core/src/composables/useEdgeHooks.ts index cd6dd8144..aad6eff59 100644 --- a/packages/core/src/composables/useEdgeHooks.ts +++ b/packages/core/src/composables/useEdgeHooks.ts @@ -1,4 +1,4 @@ -import type { EdgeEventsEmit, EdgeEventsOn, GraphEdge, VueFlowStore } from '../types' +import type { EdgeEventsEmit, EdgeEventsOn, VueFlowStore } from '../types' import { createExtendedEventHook } from '../utils' function createEdgeHooks() { @@ -20,52 +20,43 @@ function createEdgeHooks() { * * @internal */ -export function useEdgeHooks(edge: GraphEdge, emits: VueFlowStore['emits']): { emit: EdgeEventsEmit; on: EdgeEventsOn } { +export function useEdgeHooks(emits: VueFlowStore['emits']): { emit: EdgeEventsEmit; on: EdgeEventsOn } { const edgeHooks = createEdgeHooks() edgeHooks.doubleClick.on((event) => { emits.edgeDoubleClick(event) - edge.events?.doubleClick?.(event) }) edgeHooks.click.on((event) => { emits.edgeClick(event) - edge.events?.click?.(event) }) edgeHooks.mouseEnter.on((event) => { emits.edgeMouseEnter(event) - edge.events?.mouseEnter?.(event) }) edgeHooks.mouseMove.on((event) => { emits.edgeMouseMove(event) - edge.events?.mouseMove?.(event) }) edgeHooks.mouseLeave.on((event) => { emits.edgeMouseLeave(event) - edge.events?.mouseLeave?.(event) }) edgeHooks.contextMenu.on((event) => { emits.edgeContextMenu(event) - edge.events?.contextMenu?.(event) }) edgeHooks.updateStart.on((event) => { emits.edgeUpdateStart(event) - edge.events?.updateStart?.(event) }) edgeHooks.update.on((event) => { emits.edgeUpdate(event) - edge.events?.update?.(event) }) edgeHooks.updateEnd.on((event) => { emits.edgeUpdateEnd(event) - edge.events?.updateEnd?.(event) }) return Object.entries(edgeHooks).reduce( diff --git a/packages/core/src/composables/useHandle.ts b/packages/core/src/composables/useHandle.ts index 5cce1c969..d85042957 100644 --- a/packages/core/src/composables/useHandle.ts +++ b/packages/core/src/composables/useHandle.ts @@ -79,13 +79,7 @@ export function useHandle({ const doc = getHostForElement(event.target as HTMLElement) if ((isMouseTriggered && event.button === 0) || !isMouseTriggered) { - const node = findNode(toValue(nodeId)) - - let isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid - - if (!isValidConnectionHandler && node) { - isValidConnectionHandler = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid - } + const isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid let closestHandle: ConnectionHandle | null @@ -268,21 +262,15 @@ export function useHandle({ return } - const isTarget = toValue(type) === 'target' - if (!connectionClickStartHandle.value) { emits.clickConnectStart({ event, nodeId: toValue(nodeId), handleId: toValue(handleId) }) startConnection({ nodeId: toValue(nodeId), type: toValue(type), handleId: toValue(handleId) }, undefined, true) } else { - let isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid + const isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid const node = findNode(toValue(nodeId)) - if (!isValidConnectionHandler && node) { - isValidConnectionHandler = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid - } - if (node && (typeof node.connectable === 'undefined' ? nodesConnectable.value : node.connectable) === false) { return } diff --git a/packages/core/src/composables/useNode.ts b/packages/core/src/composables/useNode.ts index 2355a98fb..a7ed0770c 100644 --- a/packages/core/src/composables/useNode.ts +++ b/packages/core/src/composables/useNode.ts @@ -1,5 +1,5 @@ import { computed, inject, ref } from 'vue' -import type { CustomEvent, ElementData } from '../types' +import type { GraphNode, Node } from '../types' import { ErrorCode, VueFlowError, getConnectedEdges } from '../utils' import { NodeRef } from '../context' import { useVueFlow } from './useVueFlow' @@ -16,13 +16,13 @@ import { useNodeId } from './useNodeId' * @param id - The id of the node to access * @returns the node id, the node, the node dom element, it's parent and connected edges */ -export function useNode = any>(id?: string) { +export function useNode(id?: string) { const nodeId = id ?? useNodeId() ?? '' const nodeEl = inject(NodeRef, ref(null)) const { findNode, edges, emits } = useVueFlow() - const node = findNode(nodeId)! + const node = findNode(nodeId)! as GraphNode if (!node) { emits.error(new VueFlowError(ErrorCode.NODE_NOT_FOUND, nodeId)) diff --git a/packages/core/src/composables/useNodeHooks.ts b/packages/core/src/composables/useNodeHooks.ts index 4ab047624..c7cdd2aa5 100644 --- a/packages/core/src/composables/useNodeHooks.ts +++ b/packages/core/src/composables/useNodeHooks.ts @@ -1,4 +1,4 @@ -import type { GraphNode, NodeEventsEmit, NodeEventsOn, VueFlowStore } from '../types' +import type { NodeEventsEmit, NodeEventsOn, VueFlowStore } from '../types' import { createExtendedEventHook } from '../utils' function createNodeHooks() { @@ -20,52 +20,43 @@ function createNodeHooks() { * * @internal */ -export function useNodeHooks(node: GraphNode, emits: VueFlowStore['emits']): { emit: NodeEventsEmit; on: NodeEventsOn } { +export function useNodeHooks(emits: VueFlowStore['emits']): { emit: NodeEventsEmit; on: NodeEventsOn } { const nodeHooks = createNodeHooks() nodeHooks.doubleClick.on((event) => { emits.nodeDoubleClick(event) - node.events?.doubleClick?.(event) }) nodeHooks.click.on((event) => { emits.nodeClick(event) - node.events?.click?.(event) }) nodeHooks.mouseEnter.on((event) => { emits.nodeMouseEnter(event) - node.events?.mouseEnter?.(event) }) nodeHooks.mouseMove.on((event) => { emits.nodeMouseMove(event) - node.events?.mouseMove?.(event) }) nodeHooks.mouseLeave.on((event) => { emits.nodeMouseLeave(event) - node.events?.mouseLeave?.(event) }) nodeHooks.contextMenu.on((event) => { emits.nodeContextMenu(event) - node.events?.contextMenu?.(event) }) nodeHooks.dragStart.on((event) => { emits.nodeDragStart(event) - node.events?.dragStart?.(event) }) nodeHooks.drag.on((event) => { emits.nodeDrag(event) - node.events?.drag?.(event) }) nodeHooks.dragStop.on((event) => { emits.nodeDragStop(event) - node.events?.dragStop?.(event) }) return Object.entries(nodeHooks).reduce( diff --git a/packages/core/src/composables/useVueFlow.ts b/packages/core/src/composables/useVueFlow.ts index c4f7427a0..dc755f546 100644 --- a/packages/core/src/composables/useVueFlow.ts +++ b/packages/core/src/composables/useVueFlow.ts @@ -1,12 +1,11 @@ import { tryOnScopeDispose } from '@vueuse/core' import type { EffectScope } from 'vue' import { effectScope, getCurrentInstance, getCurrentScope, inject, provide, watch } from 'vue' -import type { EdgeChange, FlowOptions, NodeChange, VueFlowStore } from '../types' +import type { EdgeChange, FlowProps, Node, NodeChange, VueFlowStore } from '../types' import { ErrorCode, VueFlowError, warn } from '../utils' import { VueFlow } from '../context' import { Storage } from '../utils/storage' -type Injection = VueFlowStore | null | undefined type Scope = (EffectScope & { vueFlowId: string }) | undefined // todo: maybe replace the storage with a context based solution; This would break calling useVueFlow outside a setup function though, which should be fine @@ -21,9 +20,9 @@ type Scope = (EffectScope & { vueFlowId: string }) | undefined * @returns a vue flow store instance * @param idOrOpts - id of the store instance or options to create a new store instance */ -export function useVueFlow(id?: string): VueFlowStore -export function useVueFlow(options?: FlowOptions): VueFlowStore -export function useVueFlow(idOrOpts?: any): VueFlowStore { +export function useVueFlow(id?: string): VueFlowStore +export function useVueFlow(options?: FlowProps): VueFlowStore +export function useVueFlow(idOrOpts?: any): VueFlowStore { const storage = Storage.getInstance() const scope = getCurrentScope() as Scope @@ -35,14 +34,14 @@ export function useVueFlow(idOrOpts?: any): VueFlowStore { const id = options.id const vueFlowId = scope?.vueFlowId || id - let vueFlow: Injection + let vueFlow: VueFlowStore | null | undefined /** * check if we can get a store instance through injections * this should be the regular way after initialization */ if (scope) { - const injection = inject(VueFlow, null) + const injection = inject(VueFlow, null) as VueFlowStore | null if (typeof injection !== 'undefined' && injection !== null) { vueFlow = injection } @@ -54,7 +53,7 @@ export function useVueFlow(idOrOpts?: any): VueFlowStore { */ if (!vueFlow) { if (vueFlowId) { - vueFlow = storage.get(vueFlowId) + vueFlow = storage.get(vueFlowId) as unknown as VueFlowStore } } @@ -66,7 +65,7 @@ export function useVueFlow(idOrOpts?: any): VueFlowStore { if (!vueFlow || (vueFlow && id && id !== vueFlow.id)) { const name = id ?? storage.getId() - const state = storage.create(name, options) + const state = storage.create(name, options) as unknown as VueFlowStore vueFlow = state @@ -80,7 +79,7 @@ export function useVueFlow(idOrOpts?: any): VueFlowStore { watch( state.applyDefault, (shouldApplyDefault, __, onCleanup) => { - const nodesChangeHandler = (changes: NodeChange[]) => { + const nodesChangeHandler = (changes: NodeChange[]) => { state.applyNodeChanges(changes) } @@ -89,16 +88,16 @@ export function useVueFlow(idOrOpts?: any): VueFlowStore { } if (shouldApplyDefault) { - state.onNodesChange(nodesChangeHandler) + state.onNodesChange(nodesChangeHandler as any) state.onEdgesChange(edgesChangeHandler) } else { - state.hooks.value.nodesChange.off(nodesChangeHandler) + state.hooks.value.nodesChange.off(nodesChangeHandler as any) state.hooks.value.edgesChange.off(edgesChangeHandler) } // Release handlers on cleanup onCleanup(() => { - state.hooks.value.nodesChange.off(nodesChangeHandler) + state.hooks.value.nodesChange.off(nodesChangeHandler as any) state.hooks.value.edgesChange.off(edgesChangeHandler) }) }, @@ -127,7 +126,7 @@ export function useVueFlow(idOrOpts?: any): VueFlowStore { // Provide a fresh instance into context if we are in a scope if (scope) { - provide(VueFlow, vueFlow) + provide(VueFlow, vueFlow as unknown as VueFlowStore) scope.vueFlowId = vueFlow.id } @@ -141,5 +140,5 @@ export function useVueFlow(idOrOpts?: any): VueFlowStore { } } - return vueFlow + return vueFlow as VueFlowStore } diff --git a/packages/core/src/composables/useWatchProps.ts b/packages/core/src/composables/useWatchProps.ts index 4dd9da6a6..a2b5d7e03 100644 --- a/packages/core/src/composables/useWatchProps.ts +++ b/packages/core/src/composables/useWatchProps.ts @@ -2,7 +2,7 @@ import type { ToRefs } from 'vue' import { effectScope, isRef, nextTick, onScopeDispose, toRef, watch } from 'vue' import type { WatchPausableReturn } from '@vueuse/core' import { watchPausable } from '@vueuse/core' -import type { Connection, FlowProps, VueFlowStore } from '../types' +import type { Connection, FlowProps, Node, VueFlowStore } from '../types' import { isDef } from '../utils' /** @@ -13,60 +13,14 @@ import { isDef } from '../utils' * @param props * @param store */ -export function useWatchProps( - models: ToRefs>, - props: FlowProps, - store: VueFlowStore, +export function useWatchProps( + models: ToRefs, 'nodes' | 'edges'>>, + props: FlowProps, + store: VueFlowStore, ) { const scope = effectScope(true) scope.run(() => { - const watchModelValue = () => { - scope.run(() => { - let pauseModel: WatchPausableReturn - let pauseStore: WatchPausableReturn - - let immediateStore = !!(store.nodes.value.length || store.edges.value.length) - - // eslint-disable-next-line prefer-const - pauseModel = watchPausable([models.modelValue, () => models.modelValue?.value?.length], ([elements]) => { - if (elements && Array.isArray(elements)) { - pauseStore?.pause() - - store.setElements(elements) - - // only trigger store watcher immediately if we actually set any elements to the store - if (!pauseStore && !immediateStore && elements.length) { - immediateStore = true - } else { - pauseStore?.resume() - } - } - }) - - pauseStore = watchPausable( - [store.nodes, store.edges, () => store.edges.value.length, () => store.nodes.value.length], - ([nodes, edges]) => { - if (models.modelValue?.value && Array.isArray(models.modelValue.value)) { - pauseModel?.pause() - - models.modelValue.value = [...nodes, ...edges] - - nextTick(() => { - pauseModel?.resume() - }) - } - }, - { immediate: immediateStore }, - ) - - onScopeDispose(() => { - pauseModel?.stop() - pauseStore?.stop() - }) - }) - } - const watchNodesValue = () => { scope.run(() => { let pauseModel: WatchPausableReturn @@ -96,7 +50,7 @@ export function useWatchProps( if (models.nodes?.value && Array.isArray(models.nodes.value)) { pauseModel?.pause() - models.nodes.value = [...nodes] + models.nodes.value = [...nodes] as unknown as NodeType[] nextTick(() => { pauseModel?.resume() @@ -282,7 +236,6 @@ export function useWatchProps( const watchRest = () => { const skip: (keyof typeof props)[] = [ 'id', - 'modelValue', 'translateExtent', 'nodeExtent', 'edges', @@ -318,7 +271,6 @@ export function useWatchProps( } const runAll = () => { - watchModelValue() watchNodesValue() watchEdgesValue() watchMinZoom() diff --git a/packages/core/src/container/Pane/Pane.vue b/packages/core/src/container/Pane/Pane.vue index 4e19c6217..1745d4dff 100644 --- a/packages/core/src/container/Pane/Pane.vue +++ b/packages/core/src/container/Pane/Pane.vue @@ -17,7 +17,7 @@ const { viewport, emits, userSelectionActive, - removeSelectedElements, + removeSelectedNodes, panOnDrag, userSelectionRect, elementsSelectable, @@ -96,7 +96,7 @@ function onClick(event: MouseEvent) { emits.paneClick(event) - removeSelectedElements() + removeSelectedNodes() nodesSelectionActive.value = false } @@ -139,7 +139,7 @@ function onPointerDown(event: PointerEvent) { edgeIdLookup.value.set(edge.target, edgeIdLookup.value.get(edge.target)?.add(id) || new Set([id])) } - removeSelectedElements() + removeSelectedNodes() userSelectionRect.value = { width: 0, diff --git a/packages/core/src/container/VueFlow/VueFlow.vue b/packages/core/src/container/VueFlow/VueFlow.vue index e4810efc6..c1897cf1d 100644 --- a/packages/core/src/container/VueFlow/VueFlow.vue +++ b/packages/core/src/container/VueFlow/VueFlow.vue @@ -1,9 +1,9 @@ - diff --git a/packages/core/src/types/store.ts b/packages/core/src/types/store.ts index 36e290c1e..d589d59b6 100644 --- a/packages/core/src/types/store.ts +++ b/packages/core/src/types/store.ts @@ -46,7 +46,7 @@ export interface State extends Omit /** all stored nodes */ nodes: GraphNode[] diff --git a/packages/core/src/utils/storage.ts b/packages/core/src/utils/storage.ts index a53fbe0f9..83c4d3147 100644 --- a/packages/core/src/utils/storage.ts +++ b/packages/core/src/utils/storage.ts @@ -43,7 +43,7 @@ export class Storage { public create(id: string, preloadedState?: FlowProps): VueFlowStore { const state = useState() - const reactiveState = reactive(state) + const reactiveState = reactive(state) as any const hooksOn = {} for (const [n, h] of Object.entries(reactiveState.hooks)) { @@ -76,7 +76,7 @@ export class Storage { return edgesMap }) - const getters = useGetters(reactiveState, nodeLookup, edgeLookup) + const getters = useGetters(reactiveState, nodeLookup, edgeLookup) const actions = useActions(reactiveState, nodeLookup, edgeLookup) From b2b850ffe6222b6352f1e2eec3f30864d273fb20 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Sun, 23 Nov 2025 19:07:05 +0100 Subject: [PATCH 09/10] chore: cleanup Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- .../core/src/composables/useViewportHelper.ts | 4 +- packages/core/src/composables/useVueFlow.ts | 99 ++----------------- .../core/src/container/VueFlow/VueFlow.vue | 2 +- packages/core/src/store/actions.ts | 2 +- packages/core/src/store/hooks.ts | 6 +- packages/core/src/types/store.ts | 10 +- packages/core/src/utils/drag.ts | 2 +- packages/core/src/utils/storage.ts | 4 +- packages/minimap/src/MiniMap.vue | 10 +- packages/minimap/src/types.ts | 2 +- 10 files changed, 27 insertions(+), 114 deletions(-) diff --git a/packages/core/src/composables/useViewportHelper.ts b/packages/core/src/composables/useViewportHelper.ts index 544d06801..5c7565d18 100644 --- a/packages/core/src/composables/useViewportHelper.ts +++ b/packages/core/src/composables/useViewportHelper.ts @@ -1,6 +1,6 @@ import { computed } from 'vue' import { rendererPointToPoint } from '@xyflow/system' -import type { GraphNode, Project, State, ViewportFunctions } from '../types' +import type { GraphNode, Node, Project, State, ViewportFunctions } from '../types' import { getRectOfNodes, getTransformForBounds, pointToRendererPoint, warn } from '../utils' export interface ViewportHelper extends ViewportFunctions { @@ -38,7 +38,7 @@ const initialViewportHelper: ViewportHelper = { * @internal * @param state */ -export function useViewportHelper(state: State) { +export function useViewportHelper(state: State) { return computed(() => { const panZoom = state.panZoom const isInitialized = state.panZoom && state.dimensions.width && state.dimensions.height diff --git a/packages/core/src/composables/useVueFlow.ts b/packages/core/src/composables/useVueFlow.ts index dc755f546..7d59b5369 100644 --- a/packages/core/src/composables/useVueFlow.ts +++ b/packages/core/src/composables/useVueFlow.ts @@ -1,13 +1,8 @@ -import { tryOnScopeDispose } from '@vueuse/core' -import type { EffectScope } from 'vue' -import { effectScope, getCurrentInstance, getCurrentScope, inject, provide, watch } from 'vue' -import type { EdgeChange, FlowProps, Node, NodeChange, VueFlowStore } from '../types' -import { ErrorCode, VueFlowError, warn } from '../utils' +import { getCurrentScope, inject, provide } from 'vue' +import type { Node, VueFlowStore } from '../types' import { VueFlow } from '../context' import { Storage } from '../utils/storage' -type Scope = (EffectScope & { vueFlowId: string }) | undefined - // todo: maybe replace the storage with a context based solution; This would break calling useVueFlow outside a setup function though, which should be fine /** * Composable that provides access to a store instance @@ -18,21 +13,12 @@ type Scope = (EffectScope & { vueFlowId: string }) | undefined * * @public * @returns a vue flow store instance - * @param idOrOpts - id of the store instance or options to create a new store instance + * @param id - id of the store instance or options to create a new store instance */ -export function useVueFlow(id?: string): VueFlowStore -export function useVueFlow(options?: FlowProps): VueFlowStore -export function useVueFlow(idOrOpts?: any): VueFlowStore { +export function useVueFlow(id?: string): VueFlowStore { const storage = Storage.getInstance() - const scope = getCurrentScope() as Scope - - const isOptsObj = typeof idOrOpts === 'object' - - const options = isOptsObj ? idOrOpts : { id: idOrOpts } - - const id = options.id - const vueFlowId = scope?.vueFlowId || id + const scope = getCurrentScope() let vueFlow: VueFlowStore | null | undefined @@ -51,10 +37,8 @@ export function useVueFlow(idOrOpts?: any): VueFlo * check if we can get a store instance through storage * this requires options id or an id on the current scope */ - if (!vueFlow) { - if (vueFlowId) { - vueFlow = storage.get(vueFlowId) as unknown as VueFlowStore - } + if (!vueFlow && id) { + vueFlow = storage.get(id) as unknown as VueFlowStore } /** @@ -65,79 +49,12 @@ export function useVueFlow(idOrOpts?: any): VueFlo if (!vueFlow || (vueFlow && id && id !== vueFlow.id)) { const name = id ?? storage.getId() - const state = storage.create(name, options) as unknown as VueFlowStore - - vueFlow = state - - const vfScope = scope ?? effectScope(true) - vfScope.run(() => { - /** - * We have to watch the applyDefault option here, - * because we need to register the default hooks before the `VueFlow` component is actually mounted and props passed - * Otherwise calling `addNodes` while the component is not mounted will not trigger any changes unless change handlers are explicitly bound - */ - watch( - state.applyDefault, - (shouldApplyDefault, __, onCleanup) => { - const nodesChangeHandler = (changes: NodeChange[]) => { - state.applyNodeChanges(changes) - } - - const edgesChangeHandler = (changes: EdgeChange[]) => { - state.applyEdgeChanges(changes) - } - - if (shouldApplyDefault) { - state.onNodesChange(nodesChangeHandler as any) - state.onEdgesChange(edgesChangeHandler) - } else { - state.hooks.value.nodesChange.off(nodesChangeHandler as any) - state.hooks.value.edgesChange.off(edgesChangeHandler) - } - - // Release handlers on cleanup - onCleanup(() => { - state.hooks.value.nodesChange.off(nodesChangeHandler as any) - state.hooks.value.edgesChange.off(edgesChangeHandler) - }) - }, - { immediate: true }, - ) - - // Destroy store instance on scope dispose - tryOnScopeDispose(() => { - if (vueFlow) { - const storedInstance = storage.get(vueFlow.id) - - if (storedInstance) { - storedInstance.$destroy() - } else { - warn(`No store instance found for id ${vueFlow.id} in storage.`) - } - } - }) - }) - } else { - // If options were passed, overwrite state with the options' values - if (isOptsObj) { - vueFlow.setState(options) - } + vueFlow = storage.create(name) as unknown as VueFlowStore } // Provide a fresh instance into context if we are in a scope if (scope) { provide(VueFlow, vueFlow as unknown as VueFlowStore) - - scope.vueFlowId = vueFlow.id - } - - if (isOptsObj) { - const instance = getCurrentInstance() - - // ignore the warning if we are in a VueFlow component - if (instance?.type.name !== 'VueFlow') { - vueFlow.emits.error(new VueFlowError(ErrorCode.USEVUEFLOW_OPTIONS)) - } } return vueFlow as VueFlowStore diff --git a/packages/core/src/container/VueFlow/VueFlow.vue b/packages/core/src/container/VueFlow/VueFlow.vue index c1897cf1d..b44094aaa 100644 --- a/packages/core/src/container/VueFlow/VueFlow.vue +++ b/packages/core/src/container/VueFlow/VueFlow.vue @@ -53,7 +53,7 @@ const slots = defineSlots>() const modelNodes = useVModel(props, 'nodes', emit) const modelEdges = useVModel(props, 'edges', emit) -const instance = useVueFlow(props) +const instance = useVueFlow() // watch props and update store state const dispose = useWatchProps({ nodes: modelNodes, edges: modelEdges }, props, instance) diff --git a/packages/core/src/store/actions.ts b/packages/core/src/store/actions.ts index 91def34b3..47acaca28 100644 --- a/packages/core/src/store/actions.ts +++ b/packages/core/src/store/actions.ts @@ -678,7 +678,7 @@ export function useActions( } const $reset: Actions['$reset'] = () => { - const resetState = useState() + const { nodes: _nodes, edges: _edges, ...resetState } = useState() state.edges = [] state.nodes = [] diff --git a/packages/core/src/store/hooks.ts b/packages/core/src/store/hooks.ts index 60ee5dbf3..72aa5054c 100644 --- a/packages/core/src/store/hooks.ts +++ b/packages/core/src/store/hooks.ts @@ -1,11 +1,11 @@ import { tryOnScopeDispose } from '@vueuse/core' import type { Ref } from 'vue' import { onBeforeMount } from 'vue' -import type { FlowHooks } from '../types' +import type { FlowHooks, Node } from '../types' import { createExtendedEventHook, warn } from '../utils' // flow event hooks -export function createHooks(): FlowHooks { +export function createHooks(): FlowHooks { return { edgesChange: createExtendedEventHook(), nodesChange: createExtendedEventHook(), @@ -63,7 +63,7 @@ export function createHooks(): FlowHooks { } } -export function useHooks(emit: (...args: any[]) => void, hooks: Ref) { +export function useHooks(emit: (...args: any[]) => void, hooks: Ref>) { onBeforeMount(() => { for (const [key, value] of Object.entries(hooks.value)) { const listener = (data: any) => { diff --git a/packages/core/src/types/store.ts b/packages/core/src/types/store.ts index d589d59b6..27c68a2b6 100644 --- a/packages/core/src/types/store.ts +++ b/packages/core/src/types/store.ts @@ -39,7 +39,7 @@ export interface UpdateNodeDimensionsParams { forceUpdate?: boolean } -export interface State extends Omit { +export interface State extends Omit { /** Vue flow element ref */ vueFlowRef: HTMLDivElement | null /** Vue flow viewport element */ @@ -190,10 +190,8 @@ export type UpdateEdgeData = ( options?: { replace: boolean }, ) => void -export type SetState = ( - state: - | Partial> - | ((state: State) => Partial>), +export type SetState = ( + state: Partial> | ((state: State) => Partial>), ) => void export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void @@ -275,7 +273,7 @@ export interface Actions extends Omit void /** set new state */ - setState: SetState + setState: SetState /** return an object of graph values (elements, viewport transform) for storage and re-loading a graph */ toObject: () => FlowExportObject /** load graph from export obj */ diff --git a/packages/core/src/utils/drag.ts b/packages/core/src/utils/drag.ts index ecc0bf452..ef78fc41a 100644 --- a/packages/core/src/utils/drag.ts +++ b/packages/core/src/utils/drag.ts @@ -195,7 +195,7 @@ export function calcNextPosition( ) { const extent = clampNodeExtent(node.dimensions, getExtent(node, triggerError, nodeExtent, parentNode)) - const clampedPos = clampPosition(nextPosition, extent) + const clampedPos = clampPosition(nextPosition, extent, node.dimensions) return { position: { diff --git a/packages/core/src/utils/storage.ts b/packages/core/src/utils/storage.ts index 83c4d3147..a4bae070b 100644 --- a/packages/core/src/utils/storage.ts +++ b/packages/core/src/utils/storage.ts @@ -48,12 +48,12 @@ export class Storage { const hooksOn = {} for (const [n, h] of Object.entries(reactiveState.hooks)) { const name = `on${n.charAt(0).toUpperCase() + n.slice(1)}` - hooksOn[name] = h.on + hooksOn[name] = (h as any).on } const emits = {} for (const [n, h] of Object.entries(reactiveState.hooks)) { - emits[n] = h.trigger + emits[n] = (h as any).trigger } // for lookup purposes diff --git a/packages/minimap/src/MiniMap.vue b/packages/minimap/src/MiniMap.vue index 8cd662027..9bbbc88d0 100644 --- a/packages/minimap/src/MiniMap.vue +++ b/packages/minimap/src/MiniMap.vue @@ -38,7 +38,7 @@ const attrs: Record = useAttrs() const defaultWidth = 200 const defaultHeight = 150 -const { id, edges, viewport, translateExtent, dimensions, emits, panZoom, getNodesInitialized } = useVueFlow() +const { id, edges, nodes, viewport, translateExtent, dimensions, emits, panZoom } = useVueFlow() const el = ref() @@ -62,7 +62,7 @@ const nodeClassNameFunc = computed(() => typeof nodeClassName === 'string' ? () => nodeClassName : typeof nodeClassName === 'function' ? nodeClassName : () => '', ) -const bb = computed(() => getRectOfNodes(getNodesInitialized.value)) +const bb = computed(() => getRectOfNodes(nodes.value)) const viewBB = computed(() => ({ x: -viewport.value.x / viewport.value.zoom, @@ -71,9 +71,7 @@ const viewBB = computed(() => ({ height: dimensions.value.height / viewport.value.zoom, })) -const boundingRect = computed(() => - getNodesInitialized.value && getNodesInitialized.value.length ? getBoundsOfRects(bb.value, viewBB.value) : viewBB.value, -) +const boundingRect = computed(() => (nodes.value && nodes.value.length ? getBoundsOfRects(bb.value, viewBB.value) : viewBB.value)) const viewScale = computed(() => { const scaledWidth = boundingRect.value.width / elementWidth.value @@ -220,7 +218,7 @@ export default { {{ ariaLabel }} Date: Sun, 23 Nov 2025 19:39:35 +0100 Subject: [PATCH 10/10] chore: cleanup Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- examples/vite/src/Basic/Basic.vue | 5 +---- packages/core/src/composables/useVueFlow.ts | 2 +- packages/core/src/types/store.ts | 4 ++-- packages/core/src/utils/graph.ts | 8 ++++---- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/vite/src/Basic/Basic.vue b/examples/vite/src/Basic/Basic.vue index 04f7684a4..e0ae58d34 100644 --- a/examples/vite/src/Basic/Basic.vue +++ b/examples/vite/src/Basic/Basic.vue @@ -18,10 +18,7 @@ const edges = ref([ { id: 'e1-3', source: '1', target: '3' }, ]) -const { onConnect, addEdges, setViewport, toObject } = useVueFlow({ - minZoom: 0.2, - maxZoom: 4, -}) +const { onConnect, addEdges, setViewport, toObject } = useVueFlow() onConnect(addEdges) diff --git a/packages/core/src/composables/useVueFlow.ts b/packages/core/src/composables/useVueFlow.ts index ada9f4b20..78a04ebc5 100644 --- a/packages/core/src/composables/useVueFlow.ts +++ b/packages/core/src/composables/useVueFlow.ts @@ -28,7 +28,7 @@ export function useVueFlow(id?: string): VueFlowSt */ if (scope) { const injectedState = inject(VueFlow, null) as VueFlowStore | null - if (typeof injectedState !== 'undefined' && injectedState !== null && (!vueFlowId || injectedState.id === vueFlowId)) { + if (typeof injectedState !== 'undefined' && injectedState !== null && (!id || injectedState.id === id)) { vueFlow = injectedState } } diff --git a/packages/core/src/types/store.ts b/packages/core/src/types/store.ts index 4e27dfcc6..ec5ad9dba 100644 --- a/packages/core/src/types/store.ts +++ b/packages/core/src/types/store.ts @@ -41,7 +41,7 @@ export interface UpdateNodeDimensionsParams { forceUpdate?: boolean } -export interface State extends Omit { +export interface State extends Omit, 'id' | 'nodes' | 'edges'> { /** Vue flow element ref */ vueFlowRef: HTMLDivElement | null /** Vue flow viewport element */ @@ -80,7 +80,7 @@ export interface State extends Omit