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:
@@ -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')
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -35,7 +35,6 @@ const defaultState = (): State => ({
|
||||
edgeTypes: {},
|
||||
|
||||
initialized: false,
|
||||
instance: null,
|
||||
|
||||
dimensions: {
|
||||
width: 0,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
NodeChange,
|
||||
NodeRemoveChange,
|
||||
NodeSelectionChange,
|
||||
XYPosition,
|
||||
} from '~/types'
|
||||
|
||||
function handleParentExpand(updateItem: GraphNode, parent: GraphNode) {
|
||||
|
||||
Reference in New Issue
Block a user