From 58783fd4a17f031092b383f99e6490d1264fbabb Mon Sep 17 00:00:00 2001 From: bcakmakoglu <78412429+bcakmakoglu@users.noreply.github.com> Date: Fri, 27 May 2022 16:11:41 +0200 Subject: [PATCH] refactor(vue-flow,viewpane): remove `instance` property and merge into `useVueFlow` # What's changed? * Remove the `instance` property from store and merge viewport functions into `useVueFlow` * Add `getElements` getter * Rename types * Deprecate `FlowInstance` type --- .../Controls/Controls.vue | 8 +-- .../src/composables/useZoomPanHelper.ts | 10 ++-- .../src/container/Viewport/Transform.vue | 50 +++---------------- .../src/container/Viewport/Viewport.vue | 6 +-- packages/vue-flow/src/store/actions.ts | 20 ++++++++ packages/vue-flow/src/store/getters.ts | 6 +++ packages/vue-flow/src/store/state.ts | 1 - packages/vue-flow/src/types/flow.ts | 13 ++--- packages/vue-flow/src/types/hooks.ts | 6 +-- packages/vue-flow/src/types/store.ts | 9 ++-- packages/vue-flow/src/types/zoom.ts | 41 ++++++++++----- packages/vue-flow/src/utils/changes.ts | 1 + 12 files changed, 86 insertions(+), 85 deletions(-) diff --git a/packages/vue-flow/src/additional-components/Controls/Controls.vue b/packages/vue-flow/src/additional-components/Controls/Controls.vue index 21fddd1c..a5ede6b3 100644 --- a/packages/vue-flow/src/additional-components/Controls/Controls.vue +++ b/packages/vue-flow/src/additional-components/Controls/Controls.vue @@ -17,22 +17,22 @@ const emit = defineEmits<{ (event: 'interactionChange', active: boolean): void }>() -const { instance, nodesDraggable, nodesConnectable, elementsSelectable, setInteractive } = $(useVueFlow()) +const { nodesDraggable, nodesConnectable, elementsSelectable, setInteractive, zoomIn, zoomOut, fitView } = $(useVueFlow()) const isInteractive = computed(() => nodesDraggable && nodesConnectable && elementsSelectable) const onZoomInHandler = () => { - instance?.zoomIn() + zoomIn() emit('zoomIn') } const onZoomOutHandler = () => { - instance?.zoomOut() + zoomOut() emit('zoomOut') } const onFitViewHandler = () => { - instance?.fitView(fitViewParams) + fitView(fitViewParams) emit('fitView') } diff --git a/packages/vue-flow/src/composables/useZoomPanHelper.ts b/packages/vue-flow/src/composables/useZoomPanHelper.ts index 51461219..9fd79b8a 100644 --- a/packages/vue-flow/src/composables/useZoomPanHelper.ts +++ b/packages/vue-flow/src/composables/useZoomPanHelper.ts @@ -2,7 +2,7 @@ import { zoomIdentity } from 'd3-zoom' import useVueFlow from './useVueFlow' import useWindow from './useWindow' import { clampPosition, getRectOfNodes, getTransformForBounds, pointToRendererPoint } from '~/utils' -import type { D3Selection, Dimensions, Getters, GraphNode, ViewportFuncs } from '~/types' +import type { D3Selection, Dimensions, Getters, GraphNode, ViewportFunctions } from '~/types' const DEFAULT_PADDING = 0.1 @@ -28,7 +28,7 @@ const untilDimensions = async (dimensions: Dimensions, getNodes: Getters['getNod return true } -export default (): ViewportFuncs => { +export default (): ViewportFunctions => { const { onPaneReady, nodes, @@ -48,7 +48,7 @@ export default (): ViewportFuncs => { onPaneReady(() => (hasDimensions = true)) - const zoomTo: ViewportFuncs['zoomTo'] = async (zoomLevel, options) => { + const zoomTo: ViewportFunctions['zoomTo'] = async (zoomLevel, options) => { if (!hasDimensions) await untilDimensions(dimensions, getNodes) if (d3Selection && d3Zoom) { @@ -64,11 +64,11 @@ export default (): ViewportFuncs => { } } - const zoomIn: ViewportFuncs['zoomIn'] = async (options) => { + const zoomIn: ViewportFunctions['zoomIn'] = async (options) => { await zoom(1.2, options?.duration) } - const zoomOut: ViewportFuncs['zoomOut'] = async (options) => { + const zoomOut: ViewportFunctions['zoomOut'] = async (options) => { await zoom(1 / 1.2, options?.duration) } diff --git a/packages/vue-flow/src/container/Viewport/Transform.vue b/packages/vue-flow/src/container/Viewport/Transform.vue index 17c99e36..005a82f6 100644 --- a/packages/vue-flow/src/container/Viewport/Transform.vue +++ b/packages/vue-flow/src/container/Viewport/Transform.vue @@ -1,11 +1,10 @@ diff --git a/packages/vue-flow/src/container/Viewport/Viewport.vue b/packages/vue-flow/src/container/Viewport/Viewport.vue index 99312d47..aadf3598 100644 --- a/packages/vue-flow/src/container/Viewport/Viewport.vue +++ b/packages/vue-flow/src/container/Viewport/Viewport.vue @@ -2,7 +2,7 @@ import type { D3ZoomEvent, ZoomTransform } from 'd3-zoom' import { zoom, zoomIdentity } from 'd3-zoom' import { pointer, select } from 'd3-selection' -import type { FlowTransform } from '../../types' +import type { ViewpaneTransform } from '../../types' import { PanOnScrollMode } from '../../types' import { useKeyPress, useVueFlow } from '../../composables' import { clamp, clampPosition } from '../../utils' @@ -35,12 +35,12 @@ const { const viewportEl = templateRef('viewport', null) -const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean => +const viewChanged = (prevTransform: ViewpaneTransform, eventTransform: ZoomTransform): boolean => (prevTransform.x !== eventTransform.x && !isNaN(eventTransform.x)) || (prevTransform.y !== eventTransform.y && !isNaN(eventTransform.y)) || (prevTransform.zoom !== eventTransform.k && !isNaN(eventTransform.k)) -const eventToFlowTransform = (eventTransform: ZoomTransform): FlowTransform => ({ +const eventToFlowTransform = (eventTransform: ZoomTransform): ViewpaneTransform => ({ x: eventTransform.x, y: eventTransform.y, zoom: eventTransform.k, diff --git a/packages/vue-flow/src/store/actions.ts b/packages/vue-flow/src/store/actions.ts index cb789960..5d9f93f1 100644 --- a/packages/vue-flow/src/store/actions.ts +++ b/packages/vue-flow/src/store/actions.ts @@ -5,6 +5,8 @@ import type { CoordinateExtent, EdgeChange, EdgeRemoveChange, + FlowExportObject, + Getters, GraphEdge, GraphNode, NodeChange, @@ -32,6 +34,7 @@ import { parseEdge, updateEdgeAction, } from '~/utils' +import { useZoomPanHelper } from '~/composables' export default (state: State, getters: ComputedGetters): Actions => { const updateNodePositions: Actions['updateNodePositions'] = (dragItems, changed, dragging) => { @@ -324,6 +327,20 @@ export default (state: State, getters: ComputedGetters): Actions => { if (!state.initialized) state.initialized = true } + const toObject = () => { + // we have to stringify/parse so objects containing refs (like nodes and edges) can potentially be saved in a storage + return JSON.parse( + JSON.stringify({ + nodes: state.nodes, + edges: state.edges, + position: [state.viewport.x, state.viewport.y], + zoom: state.viewport.zoom, + } as FlowExportObject), + ) + } + + const { fitView, ...rest } = useZoomPanHelper() + return { updateNodePositions, updateNodeDimensions, @@ -348,6 +365,9 @@ export default (state: State, getters: ComputedGetters): Actions => { removeSelectedEdges, setInteractive, setState, + fitView: (params = { padding: 0.1 }) => fitView(params), + toObject, + ...rest, $reset: () => { setState(useState()) }, diff --git a/packages/vue-flow/src/store/getters.ts b/packages/vue-flow/src/store/getters.ts index 79caf392..5b56225d 100644 --- a/packages/vue-flow/src/store/getters.ts +++ b/packages/vue-flow/src/store/getters.ts @@ -74,6 +74,7 @@ export default (state: State): ComputedGetters => { target.dimensions.height ) } + const getEdges = computed(() => { if (!state.onlyRenderVisibleElements) return state.edges.filter((edge) => edgeHidden(edge)) @@ -98,8 +99,12 @@ export default (state: State): ComputedGetters => { }) }) + const getElements: ComputedGetters['getElements'] = computed(() => [...getNodes.value, ...getEdges.value]) + const getSelectedNodes: ComputedGetters['getSelectedNodes'] = computed(() => state.nodes.filter((n) => n.selected)) + const getSelectedEdges: ComputedGetters['getSelectedEdges'] = computed(() => state.edges.filter((e) => e.selected)) + const getSelectedElements: ComputedGetters['getSelectedElements'] = computed(() => [ ...(getSelectedNodes.value ?? []), ...(getSelectedEdges.value ?? []), @@ -108,6 +113,7 @@ export default (state: State): ComputedGetters => { return { getNode, getEdge, + getElements, getEdgeTypes, getNodeTypes, getEdges, diff --git a/packages/vue-flow/src/store/state.ts b/packages/vue-flow/src/store/state.ts index e1f532c6..b6f78996 100644 --- a/packages/vue-flow/src/store/state.ts +++ b/packages/vue-flow/src/store/state.ts @@ -35,7 +35,6 @@ const defaultState = (): State => ({ edgeTypes: {}, initialized: false, - instance: null, dimensions: { width: 0, diff --git a/packages/vue-flow/src/types/flow.ts b/packages/vue-flow/src/types/flow.ts index f1d8d9aa..05e56fc7 100644 --- a/packages/vue-flow/src/types/flow.ts +++ b/packages/vue-flow/src/types/flow.ts @@ -2,8 +2,9 @@ import type { CSSProperties, Component, VNode } from 'vue' import type { DefaultEdgeOptions, Edge, GraphEdge } from './edge' import type { CoordinateExtent, GraphNode, Node } from './node' import type { ConnectionLineType, ConnectionMode } from './connection' -import type { KeyCode, PanOnScrollMode, ViewportFuncs } from './zoom' +import type { KeyCode, PanOnScrollMode } from './zoom' import type { DefaultEdgeTypes, DefaultNodeTypes, EdgeComponent, NodeComponent } from './components' +import type { VueFlowStore } from './store' export type ElementData = any @@ -84,14 +85,8 @@ export interface FlowExportObject { zoom: number } -interface Exports { - getElements: () => FlowElements - getNodes: () => GraphNode[] - getEdges: () => GraphEdge[] - toObject: () => FlowExportObject -} - -export type FlowInstance = Exports & ViewportFuncs +/** @deprecated will be removed soon, only remains for backwards compatability */ +export type FlowInstance = VueFlowStore export interface FlowProps { id?: string diff --git a/packages/vue-flow/src/types/hooks.ts b/packages/vue-flow/src/types/hooks.ts index edecabdd..2f7090a5 100644 --- a/packages/vue-flow/src/types/hooks.ts +++ b/packages/vue-flow/src/types/hooks.ts @@ -1,11 +1,11 @@ import type { EventHook, EventHookOn, EventHookTrigger } from '@vueuse/core' import type { D3ZoomEvent } from 'd3-zoom' -import type { FlowInstance } from './flow' import type { GraphEdge } from './edge' import type { GraphNode } from './node' import type { Connection, OnConnectStartParams } from './connection' -import type { FlowTransform } from './zoom' +import type { ViewpaneTransform } from './zoom' import type { EdgeChange, NodeChange } from './changes' +import type { VueFlowStore } from './store' export type MouseTouchEvent = MouseEvent | TouchEvent | PointerEvent @@ -54,7 +54,7 @@ export interface FlowEvents { } & OnConnectStartParams connectStop: MouseEvent connectEnd: MouseEvent - paneReady: FlowInstance + paneReady: VueFlowStore move: { event: D3ZoomEvent; flowTransform: FlowTransform } moveStart: { event: D3ZoomEvent; flowTransform: FlowTransform } moveEnd: { event: D3ZoomEvent; flowTransform: FlowTransform } diff --git a/packages/vue-flow/src/types/store.ts b/packages/vue-flow/src/types/store.ts index 65b58d3f..88e12603 100644 --- a/packages/vue-flow/src/types/store.ts +++ b/packages/vue-flow/src/types/store.ts @@ -4,7 +4,7 @@ import type { DefaultEdgeTypes, DefaultNodeTypes, EdgeComponent, NodeComponent } import type { Connection, ConnectionLineType, ConnectionMode } from './connection' import type { DefaultEdgeOptions, Edge, GraphEdge } from './edge' import type { CoordinateExtent, GraphNode, Node } from './node' -import type { D3Selection, D3Zoom, D3ZoomHandler, KeyCode, PanOnScrollMode, Viewport } from './zoom' +import type { D3Selection, D3Zoom, D3ZoomHandler, KeyCode, PanOnScrollMode, Viewport, ViewportFunctions } from './zoom' import type { FlowHooks, FlowHooksEmit, FlowHooksOn } from './hooks' import type { EdgeChange, NodeChange, NodeDragItem } from './changes' import type { HandleType, StartHandle } from './handle' @@ -18,7 +18,6 @@ export interface UpdateNodeDimensionsParams { export interface State extends Omit { /** Event hooks, you can manipulate the triggers at your own peril */ readonly hooks: FlowHooks - readonly instance: FlowInstance | null /** all stored nodes */ nodes: GraphNode[] @@ -121,7 +120,7 @@ export type SetState = ( export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void -export interface Actions { +export interface Actions extends ViewportFunctions { /** parses elements (nodes + edges) and re-sets the state */ setElements: SetElements /** parses nodes and re-sets the state */ @@ -164,6 +163,8 @@ export interface Actions { setInteractive: (isInteractive: boolean) => void /** set new state */ setState: SetState + /** return an object of graph values (elements, viewpane transform) for storage and re-loading a graph */ + toObject: () => FlowExportObject /** internal position updater, you probably don't want to use this */ updateNodePositions: UpdateNodePosition @@ -179,6 +180,8 @@ export interface Getters { getEdgeTypes: Record /** returns object containing current node types */ getNodeTypes: Record + /** get all elements (filters hidden elements) */ + getElements: FlowElements /** filters hidden nodes */ getNodes: GraphNode[] /** filters hidden edges */ diff --git a/packages/vue-flow/src/types/zoom.ts b/packages/vue-flow/src/types/zoom.ts index 180bba90..8f00c3e4 100644 --- a/packages/vue-flow/src/types/zoom.ts +++ b/packages/vue-flow/src/types/zoom.ts @@ -20,7 +20,7 @@ export enum PanOnScrollMode { Horizontal = 'horizontal', } -export interface ViewportFuncsOptions { +export interface TransitionOptions { duration?: number } @@ -34,32 +34,47 @@ export type FitViewParams = { y?: number } nodes?: string[] -} & ViewportFuncsOptions +} & TransitionOptions -export interface FlowTransform { +export interface ViewpaneTransform { x: number y: number zoom: number } -export type SetCenterOptions = ViewportFuncsOptions & { +export type SetCenterOptions = TransitionOptions & { zoom?: number } -export type FitBoundsOptions = ViewportFuncsOptions & { +export type FitBoundsOptions = TransitionOptions & { padding?: number } +/** Fit the viewpane around visible nodes */ export type FitView = (fitViewOptions?: FitViewParams) => void -export type Project = (position: XYPosition) => XYPosition -export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => void -export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => void -export type ZoomInOut = (options?: ViewportFuncsOptions) => void -export type ZoomTo = (zoomLevel: number, options?: ViewportFuncsOptions) => void -export type GetTransform = () => FlowTransform -export type SetTransform = (transform: FlowTransform, options?: ViewportFuncsOptions) => void -export interface ViewportFuncs { +/** project a position onto the viewpane, i.e. a mouse event clientX/clientY onto graph coordinates */ +export type Project = (position: XYPosition) => XYPosition + +/** set center of viewpane */ +export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => void + +/** fit the viewpane around bounds */ +export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => void + +/** zoom in/out */ +export type ZoomInOut = (options?: TransitionOptions) => void + +/** zoom to a specific level */ +export type ZoomTo = (zoomLevel: number, options?: TransitionOptions) => void + +/** get current viewpane transform */ +export type GetTransform = () => ViewpaneTransform + +/** set current viewpane transform */ +export type SetTransform = (transform: ViewpaneTransform, options?: TransitionOptions) => void + +export interface ViewportFunctions { zoomIn: ZoomInOut zoomOut: ZoomInOut zoomTo: ZoomTo diff --git a/packages/vue-flow/src/utils/changes.ts b/packages/vue-flow/src/utils/changes.ts index 9b06eb16..2bf9314b 100644 --- a/packages/vue-flow/src/utils/changes.ts +++ b/packages/vue-flow/src/utils/changes.ts @@ -15,6 +15,7 @@ import type { NodeChange, NodeRemoveChange, NodeSelectionChange, + XYPosition, } from '~/types' function handleParentExpand(updateItem: GraphNode, parent: GraphNode) {