diff --git a/package/src/index.ts b/package/src/index.ts index 5c891486..a5c3c020 100644 --- a/package/src/index.ts +++ b/package/src/index.ts @@ -20,10 +20,10 @@ export { isNode, isEdge, addEdge, + updateEdge, getOutgoers, getIncomers, getConnectedEdges, - updateEdge, getTransformForBounds, getRectOfNodes, graphPosToZoomedPos, @@ -31,6 +31,12 @@ export { getMarkerId, } from './utils/graph' +/** + * Intended for options API + * In composition API you can access apply utilities from `useVueFlow` + */ +export { applyChanges, applyEdgeChanges, applyNodeChanges } from './utils/changes' + export { defaultEdgeTypes, defaultNodeTypes } from './store' export { VueFlow as VueFlowInjection, NodeId as NodeIdInjection } from './context' diff --git a/package/src/store/actions.ts b/package/src/store/actions.ts index 71a4eaf7..421363e6 100644 --- a/package/src/store/actions.ts +++ b/package/src/store/actions.ts @@ -16,7 +16,8 @@ import { State, } from '~/types' import { - applyChanges, + applyNodeChanges as applyNodes, + applyEdgeChanges as applyEdges, connectionExists, createPositionChange, createSelectionChange, @@ -83,11 +84,6 @@ const updateEdgeAction = (edge: GraphEdge, newConnection: Connection, edges: Gra return true } -const applyEdgeChangesAction = (changes: EdgeChange[], edges: GraphEdge[], getNode: Getters['getNode']) => - applyChanges(changes, edges, getNode) -const applyNodeChangesAction = (changes: NodeChange[], nodes: GraphNode[], getNode: Getters['getNode']) => - applyChanges(changes, nodes, getNode) - const createGraphNodes = (nodes: Node[], getNode: Getters['getNode'], currGraphNodes: GraphNode[], extent: CoordinateExtent) => { const parentNodes: Record = {} @@ -127,17 +123,18 @@ export default (state: State, getters: ComputedGetters): Actions => { state.nodes.forEach((node) => { if (node.selected) { if (!node.parentNode) { - changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, getters.getNode.value)) + changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, state.nodes)) } else if (!isParentSelected(node, getters.getNode.value)) { - changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, getters.getNode.value)) + changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, state.nodes)) } } else if (node.id === id) { - changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, getters.getNode.value)) + changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }, state.nodes)) } }) if (changes.length) state.hooks.nodesChange.trigger(changes) } + const updateNodeDimensions: Actions['updateNodeDimensions'] = (updates) => { const changes: NodeDimensionChange[] = updates.reduce((res, update) => { const node = getters.getNode.value(update.id) @@ -167,6 +164,7 @@ export default (state: State, getters: ComputedGetters): Actions => { if (changes.length) state.hooks.nodesChange.trigger(changes) } + const addSelectedNodes: Actions['addSelectedNodes'] = (nodes) => { const selectedNodesIds = nodes.map((n) => n.id) @@ -176,6 +174,7 @@ export default (state: State, getters: ComputedGetters): Actions => { if (changedNodes.length) state.hooks.nodesChange.trigger(changedNodes) } + const addSelectedEdges: Actions['addSelectedEdges'] = (edges) => { const selectedEdgesIds = edges.map((e) => e.id) @@ -185,26 +184,32 @@ export default (state: State, getters: ComputedGetters): Actions => { if (changedEdges.length) state.hooks.edgesChange.trigger(changedEdges) } + const addSelectedElements: Actions['addSelectedElements'] = (elements) => { addSelectedNodes(elements.filter(isGraphNode)) addSelectedEdges(elements.filter(isGraphEdge)) } + const setMinZoom: Actions['setMinZoom'] = (minZoom: any) => { state.d3Zoom?.scaleExtent([minZoom, state.maxZoom]) state.minZoom = minZoom } + const setMaxZoom: Actions['setMaxZoom'] = (maxZoom: any) => { state.d3Zoom?.scaleExtent([state.minZoom, maxZoom]) state.maxZoom = maxZoom } + const setTranslateExtent: Actions['setTranslateExtent'] = (translateExtent: any) => { state.d3Zoom?.translateExtent(translateExtent) state.translateExtent = translateExtent } + const resetSelectedElements: Actions['resetSelectedElements'] = () => { addSelectedNodes([]) addSelectedEdges([]) } + const setInteractive: Actions['setInteractive'] = (isInteractive) => { state.nodesDraggable = isInteractive state.nodesConnectable = isInteractive @@ -215,6 +220,7 @@ export default (state: State, getters: ComputedGetters): Actions => { if (!state.initialized && !nodes.length) return state.nodes = createGraphNodes(nodes, getters.getNode.value, state.nodes, extent ?? state.nodeExtent) } + const setEdges: Actions['setEdges'] = (edges) => { if (!state.initialized && !edges.length) return state.edges = edges.map((edge) => { @@ -269,10 +275,10 @@ export default (state: State, getters: ComputedGetters): Actions => { const updateEdge: Actions['updateEdge'] = (oldEdge, newConnection) => updateEdgeAction(oldEdge, newConnection, state.edges, addEdges) - const applyNodeChanges: Actions['applyNodeChanges'] = (changes) => - applyNodeChangesAction(changes, state.nodes, getters.getNode.value) - const applyEdgeChanges: Actions['applyEdgeChanges'] = (changes) => - applyEdgeChangesAction(changes, state.edges, getters.getNode.value) + + const applyNodeChanges: Actions['applyNodeChanges'] = (changes) => applyNodes(changes, state.nodes) + + const applyEdgeChanges: Actions['applyEdgeChanges'] = (changes) => applyEdges(changes, state.edges) const setState: Actions['setState'] = (opts) => { const skip = ['modelValue', 'nodes', 'edges', 'maxZoom', 'minZoom', 'translateExtent'] @@ -298,6 +304,7 @@ export default (state: State, getters: ComputedGetters): Actions => { } if (!state.initialized) state.initialized = true } + return { updateNodePosition, updateNodeDimensions, diff --git a/package/src/utils/changes.ts b/package/src/utils/changes.ts index 4159458b..8030ac4e 100644 --- a/package/src/utils/changes.ts +++ b/package/src/utils/changes.ts @@ -12,6 +12,7 @@ import { Getters, CoordinateExtent, XYPosition, + GraphEdge, } from '~/types' type CreatePositionChangeParams = { @@ -21,8 +22,8 @@ type CreatePositionChangeParams = { dragging?: boolean } -function handleParentExpand(updateItem: GraphNode, getNode: Getters['getNode']) { - const parent = updateItem.parentNode ? getNode(updateItem.parentNode) : undefined +function handleParentExpand(updateItem: GraphNode, curr: GraphNode[]) { + const parent = updateItem.parentNode ? curr.find((el) => el.id === updateItem.parentNode) : undefined if (parent) { const extendWidth = updateItem.position.x + updateItem.dimensions.width - parent.dimensions.width const extendHeight = updateItem.position.y + updateItem.dimensions.height - parent.dimensions.height @@ -94,7 +95,6 @@ export const applyChanges = < >( changes: C[], elements: T[], - getNode: Getters['getNode'], ): T[] => { let elementIds = elements.map((el) => el.id) changes.forEach((change) => { @@ -109,13 +109,13 @@ export const applyChanges = < if (isGraphNode(el)) { if (typeof change.position !== 'undefined') el.position = change.position if (typeof change.dragging !== 'undefined') el.dragging = change.dragging - if (el.expandParent && el.parentNode) handleParentExpand(el, getNode) + if (el.expandParent && el.parentNode) handleParentExpand(el, elements as GraphNode[]) } break case 'dimensions': if (isGraphNode(el)) { if (typeof change.dimensions !== 'undefined') el.dimensions = change.dimensions - if (el.expandParent && el.parentNode) handleParentExpand(el, getNode) + if (el.expandParent && el.parentNode) handleParentExpand(el, elements as GraphNode[]) } break case 'remove': @@ -128,6 +128,9 @@ export const applyChanges = < return elements } +export const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]) => applyChanges(changes, edges) +export const applyNodeChanges = (changes: NodeChange[], nodes: GraphNode[]) => applyChanges(changes, nodes) + export const createSelectionChange = (id: string, selected: boolean): NodeSelectionChange | EdgeSelectionChange => ({ id, type: 'select', @@ -136,9 +139,9 @@ export const createSelectionChange = (id: string, selected: boolean): NodeSelect export const createPositionChange = ( { node, diff, dragging, nodeExtent }: CreatePositionChangeParams, - getNode: Getters['getNode'], + curr: GraphNode[], ): NodePositionChange => { - const parent = node.parentNode ? getNode(node.parentNode) : undefined + const parent = node.parentNode ? curr.find((el) => el.id === node.parentNode) : undefined const change: NodePositionChange = { id: node.id, type: 'position', diff --git a/package/src/utils/graph.ts b/package/src/utils/graph.ts index 5c9fe4ec..62355cf1 100644 --- a/package/src/utils/graph.ts +++ b/package/src/utils/graph.ts @@ -18,6 +18,7 @@ import { XYPosition, XYZPosition, Getters, + DefaultEdgeOptions, } from '~/types' import { useWindow } from '~/composables' @@ -139,7 +140,11 @@ export const connectionExists = (edge: Edge, elements: Elements) => (el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle)), ) -export const addEdge = (edgeParams: Edge | Connection, elements: Elements) => { +/** + * Intended for options API + * In composition API you can access utilities from `useVueFlow` + */ +export const addEdge = (edgeParams: Edge | Connection, elements: Elements, defaults?: DefaultEdgeOptions) => { if (!edgeParams.source || !edgeParams.target) { console.warn("Can't create edge. An edge needs a source and a target.") return elements @@ -154,12 +159,16 @@ export const addEdge = (edgeParams: Edge | Connection, elements: Elements) => { id: getEdgeId(edgeParams), } as Edge } - edge = parseEdge(edge) + edge = parseEdge(edge, defaults) if (connectionExists(edge, elements)) return elements elements.push(edge) return [...elements, edge] } +/** + * Intended for options API + * In composition API you can access utilities from `useVueFlow` + */ export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: Elements) => { if (!newConnection.source || !newConnection.target) { console.warn("Can't create new edge. An edge needs a source and a target.")