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
This commit is contained in:
bcakmakoglu
2022-05-27 16:11:41 +02:00
committed by Braks
parent 382a3b67fc
commit 58783fd4a1
12 changed files with 86 additions and 85 deletions

View File

@@ -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')
}

View File

@@ -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)
}

View File

@@ -1,11 +1,10 @@
<script lang="ts" setup>
import NodeRenderer from '../NodeRenderer/NodeRenderer.vue'
import EdgeRenderer from '../EdgeRenderer/EdgeRenderer.vue'
import { useVueFlow, useWindow, useZoomPanHelper } from '../../composables'
import type { Dimensions, FlowExportObject, FlowInstance, XYPosition } from '../../types'
import { pointToRendererPoint } from '../../utils'
import { useVueFlow, useWindow } from '../../composables'
import type { Dimensions } from '../../types'
const { id, nodes, edges, viewport, snapToGrid, snapGrid, dimensions, setState, fitViewOnInit, emits } = $(useVueFlow())
const { id, nodes, edges, viewport, snapToGrid, snapGrid, dimensions, setState, fitViewOnInit, emits, fitView } = $(useVueFlow())
const untilDimensions = async (dim: Dimensions) => {
// if ssr we can't wait for dimensions, they'll never really exist
@@ -19,53 +18,16 @@ const untilDimensions = async (dim: Dimensions) => {
}
let ready = $ref(false)
onMounted(async () => {
// create new instance and set to state
const { fitView, ...rest } = useZoomPanHelper()
let instance: FlowInstance | null = {
fitView: (params = { padding: 0.1 }) => fitView(params),
...rest,
project(position: XYPosition) {
return pointToRendererPoint(position, viewport, snapToGrid, snapGrid)
},
getElements() {
return [...nodes, ...edges]
},
getNodes() {
return nodes
},
getEdges() {
return edges
},
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,
edges,
position: [viewport.x, viewport.y],
zoom: viewport.zoom,
} as FlowExportObject),
)
},
}
onScopeDispose(() => (instance = null))
// wait until proper dimensions have been established, otherwise fitView will have wrong bounds when called at paneReady
await untilDimensions(dimensions)
// hide graph until dimensions are ready, so we don't have jumping graphs (ssr for example)
ready = true
setState({
instance,
})
fitViewOnInit && instance.fitView()
emits.paneReady(instance)
fitViewOnInit && fitView()
emits.paneReady(useVueFlow())
})
</script>

View File

@@ -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<HTMLDivElement>('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,

View File

@@ -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())
},

View File

@@ -74,6 +74,7 @@ export default (state: State): ComputedGetters => {
target.dimensions.height
)
}
const getEdges = computed<GraphEdge[]>(() => {
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,

View File

@@ -35,7 +35,6 @@ const defaultState = (): State => ({
edgeTypes: {},
initialized: false,
instance: null,
dimensions: {
width: 0,

View File

@@ -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

View File

@@ -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<HTMLDivElement, any>; flowTransform: FlowTransform }
moveStart: { event: D3ZoomEvent<HTMLDivElement, any>; flowTransform: FlowTransform }
moveEnd: { event: D3ZoomEvent<HTMLDivElement, any>; flowTransform: FlowTransform }

View File

@@ -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<FlowOptions, 'id' | 'modelValue'> {
/** 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<keyof DefaultEdgeTypes | string, EdgeComponent>
/** returns object containing current node types */
getNodeTypes: Record<keyof DefaultNodeTypes | string, NodeComponent>
/** get all elements (filters hidden elements) */
getElements: FlowElements
/** filters hidden nodes */
getNodes: GraphNode[]
/** filters hidden edges */

View File

@@ -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

View File

@@ -15,6 +15,7 @@ import type {
NodeChange,
NodeRemoveChange,
NodeSelectionChange,
XYPosition,
} from '~/types'
function handleParentExpand(updateItem: GraphNode, parent: GraphNode) {