diff --git a/src/store/stateStore.ts b/src/store/stateStore.ts
index 6bfd5b26..7b3ac9f3 100644
--- a/src/store/stateStore.ts
+++ b/src/store/stateStore.ts
@@ -1,16 +1,18 @@
import microDiff from 'microdiff'
import { setActivePinia, createPinia, defineStore, StoreDefinition, acceptHMRUpdate } from 'pinia'
-import { FlowState, FlowActions, Elements, FlowGetters, GraphNode, GraphEdge, Edge } from '~/types'
+import { FlowState, FlowActions, Elements, FlowGetters, GraphNode, GraphEdge, Edge, NextElements } from '~/types'
import {
getConnectedEdges,
getNodesInside,
getRectOfNodes,
- parseElements,
defaultNodeTypes,
defaultEdgeTypes,
isGraphNode,
getSourceTargetNodes,
isEdge,
+ processElements,
+ parseElement,
+ parseElements,
} from '~/utils'
const pinia = createPinia()
@@ -84,8 +86,19 @@ export default (id: string, preloadedState: FlowState) => {
},
},
actions: {
- setElements(elements) {
- const { nodes, edges } = parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)
+ async setElements(elements) {
+ const { nodes, edges } = parseElements(elements, this.elements, this.nodeExtent)
+ edges.forEach((edge: Edge, i, arr) => {
+ const { sourceNode, targetNode } = getSourceTargetNodes(edge, nodes)
+ if (!sourceNode) console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
+ if (!targetNode) console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
+
+ arr[i] = {
+ ...edge,
+ sourceNode,
+ targetNode,
+ }
+ })
this.elements = [...nodes, ...edges]
},
setUserSelection(mousePos) {
@@ -175,34 +188,52 @@ export default (id: string, preloadedState: FlowState) => {
this.elementsSelectable = isInteractive
},
addElements(elements: Elements) {
- const { nodes, edges } = parseElements(elements, this.getNodes, this.getEdges, this.nodeExtent)
+ const { nodes, edges } = parseElements(elements, this.elements, this.nodeExtent)
+ edges.forEach((edge: Edge, i, arr) => {
+ const { sourceNode, targetNode } = getSourceTargetNodes(edge, nodes)
+ if (!sourceNode) console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
+ if (!targetNode) console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
+
+ arr[i] = {
+ ...edge,
+ sourceNode,
+ targetNode,
+ }
+ })
this.elements = [...this.elements, ...nodes, ...edges]
},
async setState(state) {
- // set state variables
- const skip = [
- 'modelValue',
- 'd3Zoom',
- 'd3Selection',
- 'd3ZoomHandler',
- 'minZoom',
- 'maxZoom',
- 'translateExtent',
- 'nodeExtent',
- ]
- for (const opt of Object.keys(state)) {
- const val = state[opt as keyof FlowState]
- if (typeof val !== 'undefined' && !skip.includes(opt)) {
- if (typeof val === 'object' && !Array.isArray(val)) {
- ;(store as any)[opt] = { ...(store as any)[opt], ...val }
- } else (store as any)[opt] = val
- }
- }
+ if (state.panOnScroll) this.panOnScroll = state.panOnScroll
+ if (state.panOnScrollMode) this.panOnScrollMode = state.panOnScrollMode
+ if (state.panOnScrollSpeed) this.panOnScrollSpeed = state.panOnScrollSpeed
+ if (state.paneMoveable) this.paneMoveable = state.paneMoveable
+ if (state.zoomOnScroll) this.zoomOnScroll = state.zoomOnScroll
+ if (state.preventScrolling) this.preventScrolling = state.preventScrolling
+ if (state.zoomOnDoubleClick) this.zoomOnDoubleClick = state.zoomOnDoubleClick
+ if (state.zoomOnPinch) this.zoomOnPinch = state.zoomOnPinch
+ if (state.defaultZoom) this.defaultZoom = state.defaultZoom
+ if (state.defaultPosition) this.defaultPosition = state.defaultPosition
+ if (state.edgeTypes) this.edgeTypes = state.edgeTypes
+ if (state.nodeTypes) this.nodeTypes = state.nodeTypes
+ if (state.storageKey) this.storageKey = state.storageKey
+ if (state.edgeUpdaterRadius) this.edgeUpdaterRadius = state.edgeUpdaterRadius
+ if (state.elementsSelectable) this.elementsSelectable = state.elementsSelectable
+ if (state.onlyRenderVisibleElements) this.onlyRenderVisibleElements = state.onlyRenderVisibleElements
+ if (state.nodesConnectable) this.nodesConnectable = state.nodesConnectable
+ if (state.nodesDraggable) this.nodesDraggable = state.nodesDraggable
+ if (state.arrowHeadColor) this.arrowHeadColor = state.arrowHeadColor
+ if (state.markerEndId) this.markerEndId = state.markerEndId
+ if (state.deleteKeyCode) this.deleteKeyCode = state.deleteKeyCode
+ if (state.selectionKeyCode) this.selectionKeyCode = state.selectionKeyCode
+ if (state.zoomActivationKeyCode) this.zoomActivationKeyCode = state.zoomActivationKeyCode
+ if (state.multiSelectionKeyCode) this.multiSelectionKeyCode = state.multiSelectionKeyCode
+ if (state.snapToGrid) this.snapToGrid = state.snapToGrid
+ if (state.snapGrid) this.snapGrid = state.snapGrid
if (!this.isReady) await until(() => this.d3Zoom).not.toBeUndefined()
- this.setMinZoom(state.minZoom)
- this.setMaxZoom(state.maxZoom)
- this.setTranslateExtent(state.translateExtent)
- this.setNodeExtent(state.nodeExtent)
+ if (state.maxZoom) this.setMaxZoom(state.maxZoom)
+ if (state.minZoom) this.setMinZoom(state.minZoom)
+ if (state.translateExtent) this.setTranslateExtent(state.translateExtent)
+ if (state.nodeExtent) this.setNodeExtent(state.nodeExtent)
},
},
})
diff --git a/src/types/flow.ts b/src/types/flow.ts
index 81b951e1..463c0bb4 100644
--- a/src/types/flow.ts
+++ b/src/types/flow.ts
@@ -9,6 +9,11 @@ export type FlowElement = GraphNode | GraphEdge
export type FlowElements = FlowElement[]
export type Elements = (Node | Edge)[]
+export type NextElements = {
+ nodes: GraphNode[]
+ edges: GraphEdge[]
+}
+
export type Transform = [number, number, number]
export enum Position {
diff --git a/src/types/store.ts b/src/types/store.ts
index e231c5b6..255d2d74 100644
--- a/src/types/store.ts
+++ b/src/types/store.ts
@@ -73,7 +73,7 @@ export interface FlowState extends Omit {
}
export interface FlowActions {
- setElements: (elements: Elements) => void
+ setElements: (elements: Elements) => Promise
setUserSelection: (mousePos: XYPosition) => void
updateUserSelection: (mousePos: XYPosition) => void
unsetUserSelection: () => void
@@ -89,6 +89,7 @@ export interface FlowActions {
setConnectionNodeId: (payload: SetConnectionId) => void
setInteractive: (isInteractive: boolean) => void
addElements: (elements: Elements) => void
+ setState: (state: FlowOptions) => Promise
}
export interface FlowGetters {
diff --git a/src/utils/graph.ts b/src/utils/graph.ts
index 8dcd33fc..c7e92e49 100644
--- a/src/utils/graph.ts
+++ b/src/utils/graph.ts
@@ -15,6 +15,7 @@ import {
GraphNode,
FlowElements,
GraphEdge,
+ NextElements,
} from '~/types'
import { useWindow } from '~/composables'
import { getSourceTargetNodes } from '~/utils/edge'
@@ -312,46 +313,74 @@ export const getTransformForBounds = (
return [x, y, clampedZoom]
}
-type NextElements = {
- nodes: GraphNode[]
- edges: GraphEdge[]
+export const processElements = (elements: Elements, fn: (element: Node | Edge) => void) => {
+ return new Promise((resolve) => {
+ const chunk = 50
+ let index = 0
+ function doChunk() {
+ let cnt = chunk
+ while (cnt-- && index < elements.length) {
+ fn(elements[index])
+ ++index
+ }
+ if (index < elements.length) {
+ // set Timeout for async iteration
+ nextTick(doChunk)
+ } else {
+ resolve(true)
+ }
+ }
+ doChunk()
+ })
}
-export const parseElements = (elements: Elements, nodes: GraphNode[], edges: GraphEdge[], nodeExtent: NodeExtent) => {
+
+export const parseElement = (element: Node | Edge, prevElements: FlowElements, nodeExtent: NodeExtent) => {
+ let parsed: GraphEdge | GraphNode = {} as any
+ const index = prevElements.map((x) => x.id).indexOf(element.id)
+ if (isNode(element)) {
+ const storeNode = prevElements[index]
+
+ if (storeNode) {
+ const updatedNode = {
+ ...storeNode,
+ ...element,
+ } as GraphNode
+
+ if (typeof element.type !== 'undefined' && element.type !== storeNode.type) {
+ // we reset the elements dimensions here in order to force a re-calculation of the bounds.
+ // When the type of a node changes it is possible that the number or positions of handles changes too.
+ updatedNode.__vf.width = 0
+ }
+
+ parsed = updatedNode
+ } else {
+ parsed = parseNode(element, nodeExtent)
+ }
+ } else if (isEdge(element)) {
+ const storeEdge = prevElements[index]
+
+ if (storeEdge) {
+ parsed = {
+ ...storeEdge,
+ ...element,
+ } as GraphEdge
+ } else {
+ parsed = parseEdge(element)
+ }
+ }
+ return { parsed, index }
+}
+
+export const parseElements = (elements: Elements, prevElements: FlowElements, nodeExtent: NodeExtent) => {
const next: NextElements = {
nodes: [],
edges: [],
}
for (const element of elements) {
- if (isNode(element)) {
- const storeNode = nodes[nodes.map((x) => x.id).indexOf(element.id)]
-
- if (storeNode) {
- const updatedNode = {
- ...storeNode,
- ...element,
- } as GraphNode
-
- if (typeof element.type !== 'undefined' && element.type !== storeNode.type) {
- // we reset the elements dimensions here in order to force a re-calculation of the bounds.
- // When the type of a node changes it is possible that the number or positions of handles changes too.
- updatedNode.__vf.width = 0
- }
-
- next.nodes.push(updatedNode)
- } else {
- next.nodes.push(parseNode(element, nodeExtent))
- }
- } else if (isEdge(element)) {
- const storeEdge = edges[edges.map((x) => x.id).indexOf(element.id)]
-
- if (storeEdge) {
- next.edges.push({
- ...storeEdge,
- ...element,
- } as GraphEdge)
- } else {
- next.edges.push(parseEdge(element))
- }
+ const { parsed } = parseElement(element, prevElements, nodeExtent)
+ if (parsed) {
+ if (isEdge(parsed)) next.edges.push(parsed)
+ else next.nodes.push(parsed)
}
}
next.edges.forEach((edge, i, arr) => {