diff --git a/src/components/Handle/Handle.vue b/src/components/Handle/Handle.vue index a0fcb32f..e8669773 100644 --- a/src/components/Handle/Handle.vue +++ b/src/components/Handle/Handle.vue @@ -1,7 +1,7 @@ diff --git a/src/container/ZoomPane/ZoomPane.vue b/src/container/ZoomPane/ZoomPane.vue index 28a2a14d..7dbb6f46 100644 --- a/src/container/ZoomPane/ZoomPane.vue +++ b/src/container/ZoomPane/ZoomPane.vue @@ -2,7 +2,7 @@ import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom' import { get, invoke } from '@vueuse/core' import { pointer, select } from 'd3-selection' -import { FlowTransform, KeyCode, PanOnScrollMode } from '../../types' +import { FlowInstance, FlowTransform, KeyCode, PanOnScrollMode } from '../../types' import { useHooks, useKeyPress, useStore, useZoomPanHelper } from '../../composables' import { clamp, onLoadGetElements, onLoadProject, onLoadToObject } from '../../utils' @@ -186,7 +186,7 @@ store.dimensions = { invoke(async () => { await until(() => !isNaN(width.value) && width.value > 0 && !isNaN(height.value) && height.value > 0).toBeTruthy() - hooks.load.trigger({ + const instance: FlowInstance = { fitView: (params = { padding: 0.1 }) => fitView(params), zoomIn, zoomOut, @@ -195,7 +195,8 @@ invoke(async () => { project: onLoadProject(store), getElements: onLoadGetElements(store), toObject: onLoadToObject(store), - }) + } + hooks.load.trigger(instance) }) watch( diff --git a/src/context/index.ts b/src/context/index.ts index 405cdeb8..95df9a46 100644 --- a/src/context/index.ts +++ b/src/context/index.ts @@ -3,4 +3,4 @@ import { ElementId, FlowHooks, FlowStore } from '~/types' export const Store: InjectionKey = Symbol('store') export const Hooks: InjectionKey = Symbol('hooks') -export const NodeIdContextKey: InjectionKey = Symbol('NodeIdContext') +export const NodeId: InjectionKey = Symbol('nodeId') diff --git a/src/index.ts b/src/index.ts index f838935b..f825737d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,11 +19,9 @@ export { graphPosToZoomedPos, } from './utils/graph' export { default as useZoomPanHelper } from './composables/useZoomPanHelper' -export { default as useStore } from './composables/useStore' -export { default as useHooks } from './composables/useHooks' +export { default as useVueFlow } from './composables/useVueFlow' export { default as useHandle } from './composables/useHandle' export { default as useKeyPress } from './composables/useKeyPress' -export { default as useVueFlow } from './composables/useVueFlow' export * from './additional-components' export * from './types' diff --git a/src/store/useFlowStore.ts b/src/store/flowStore.ts similarity index 76% rename from src/store/useFlowStore.ts rename to src/store/flowStore.ts index e769d082..08e9d756 100644 --- a/src/store/useFlowStore.ts +++ b/src/store/flowStore.ts @@ -1,6 +1,6 @@ import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia' import diff from 'microdiff' -import { Edge, FlowState, Node, FlowActions } from '~/types' +import { Edge, FlowState, Node, FlowActions, Elements, NodeExtent } from '~/types' import { clampPosition, getDimensions, @@ -21,7 +21,54 @@ type NextElements = { const pinia = createPinia() let id = 0 -export default function useFlowStore(preloadedState: FlowState): StoreDefinition { +const parseElements = (elements: Elements, nodes: Node[], edges: Edge[], nodeExtent: NodeExtent) => { + const nextElements: NextElements = { + nextNodes: [], + nextEdges: [], + } + for (const element of elements) { + if (isNode(element)) { + const storeNode = nodes.find((node) => node.id === element.id) + + if (storeNode) { + const updatedNode: Node = { + ...storeNode, + ...element, + } + if (!updatedNode.__rf) updatedNode.__rf = {} + + if (storeNode.position.x !== element.position.x || storeNode.position.y !== element.position.y) { + updatedNode.__rf.position = element.position + } + + 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.__rf.width = undefined + } + + nextElements.nextNodes.push(updatedNode) + } else { + nextElements.nextNodes.push(parseNode(element, nodeExtent)) + } + } else if (isEdge(element)) { + const storeEdge = edges.find((se) => se.id === element.id) + + if (storeEdge) { + nextElements.nextEdges.push({ + ...storeEdge, + ...element, + }) + } else { + nextElements.nextEdges.push(parseEdge(element)) + } + } + } + + return nextElements +} + +export default function flowStore(preloadedState: FlowState): StoreDefinition { setActivePinia(pinia) return defineStore({ @@ -32,51 +79,7 @@ export default function useFlowStore(preloadedState: FlowState): StoreDefinition getters: {}, actions: { setElements(elements) { - const nextElements: NextElements = { - nextNodes: [], - nextEdges: [], - } - const { nextNodes, nextEdges } = elements.reduce((res, propElement): NextElements => { - if (isNode(propElement)) { - const storeNode = this.nodes.find((node) => node.id === propElement.id) - - if (storeNode) { - const updatedNode: Node = { - ...storeNode, - ...propElement, - } - if (!updatedNode.__rf) updatedNode.__rf = {} - - if (storeNode.position.x !== propElement.position.x || storeNode.position.y !== propElement.position.y) { - updatedNode.__rf.position = propElement.position - } - - if (typeof propElement.type !== 'undefined' && propElement.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.__rf.width = undefined - } - - res.nextNodes.push(updatedNode) - } else { - res.nextNodes.push(parseNode(propElement, this.nodeExtent)) - } - } else if (isEdge(propElement)) { - const storeEdge = this.edges.find((se) => se.id === propElement.id) - - if (storeEdge) { - res.nextEdges.push({ - ...storeEdge, - ...propElement, - }) - } else { - res.nextEdges.push(parseEdge(propElement)) - } - } - - return res - }, nextElements) - + const { nextNodes, nextEdges } = parseElements(elements, this.nodes, this.edges, this.nodeExtent) this.nodes = nextNodes this.edges = nextEdges }, @@ -257,6 +260,11 @@ export default function useFlowStore(preloadedState: FlowState): StoreDefinition this.nodesConnectable = isInteractive this.elementsSelectable = isInteractive }, + addElements(elements: Elements) { + const { nextNodes, nextEdges } = parseElements(elements, this.nodes, this.edges, this.nodeExtent) + this.nodes = [...this.nodes, ...nextNodes] + this.edges = [...this.edges, ...nextEdges] + }, }, }) } diff --git a/src/store/index.ts b/src/store/index.ts index d7f28009..c0591b49 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,4 +1,5 @@ import { ConnectionMode, FlowState } from '~/types' +import { createHooks } from '~/composables' export const initialState = (): FlowState => ({ dimensions: { @@ -6,6 +7,8 @@ export const initialState = (): FlowState => ({ height: 0, }, transform: [0, 0, 1], + nodeTypes: [], + edgeTypes: [], elements: [], nodes: [], edges: [], @@ -54,7 +57,10 @@ export const initialState = (): FlowState => ({ multiSelectionActive: false, + isReady: false, + hooks: createHooks(), + vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-', }) -export { default as useFlowStore } from './useFlowStore' +export { default as useFlowStore } from './flowStore' diff --git a/src/types/actions.ts b/src/types/actions.ts index be2c987b..98f45af1 100644 --- a/src/types/actions.ts +++ b/src/types/actions.ts @@ -22,4 +22,5 @@ export interface FlowActions { updateSize: (size: Dimensions) => void setConnectionNodeId: (payload: SetConnectionId) => void setInteractive: (isInteractive: boolean) => void + addElements: (elements: Elements) => void } diff --git a/src/types/store.ts b/src/types/store.ts index 27061129..734dc5ec 100644 --- a/src/types/store.ts +++ b/src/types/store.ts @@ -2,17 +2,20 @@ import { Store } from 'pinia' import { Dimensions, ElementId, Elements, Rect, SelectionRect, SnapGrid, Transform, XYPosition } from './types' import { HandleType } from './handle' import { ConnectionMode, OnConnectEndFunc, OnConnectFunc, OnConnectStartFunc, OnConnectStopFunc } from './connection' -import { Edge } from './edge' -import { Node, NodeExtent, TranslateExtent } from './node' +import { Edge, EdgeType } from './edge' +import { Node, NodeExtent, NodeType, TranslateExtent } from './node' import { FlowActions } from './actions' import { D3Selection, D3Zoom, D3ZoomHandler } from './panel' +import { FlowHooks } from '~/types/hooks' export interface FlowState { dimensions: Dimensions transform: Transform elements: Elements nodes: Node[] + nodeTypes: Record | string[] edges: Edge[] + edgeTypes: Record | string[] selectedElements?: Elements selectedNodesBbox: Rect @@ -50,6 +53,9 @@ export interface FlowState { onConnectStart?: OnConnectStartFunc onConnectStop?: OnConnectStopFunc onConnectEnd?: OnConnectEndFunc + + isReady: boolean + hooks: FlowHooks } export type FlowStore = Store