diff --git a/package.json b/package.json index d25c5df7..676c8e61 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "np": "^7.5.0", "pascalcase": "^1.0.0", "postcss": "^8.3.5", - "postcss-cli": "^8.3.1", "postcss-nested": "^5.0.5", "prettier": "^2.2.1", "rollup": "^2.52.2", diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index 084cdc6f..9311e9bf 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -1,12 +1,11 @@ -import { Node, WrapNodeProps } from '../../types'; -import { computed, CSSProperties, defineComponent, onMounted, provide, ref } from 'vue'; -import store from '../../store'; +import { Node, RevueFlowStore, WrapNodeProps } from '../../types'; +import { computed, CSSProperties, defineComponent, inject, onMounted, provide, ref } from 'vue'; export default (NodeComponent: any) => { return defineComponent({ props: WrapNodeProps, setup(props) { - const pinia = store(); + const store = inject('store'); provide('NodeIdContext', props.id); const nodeElement = ref(null); @@ -70,10 +69,10 @@ export default (NodeComponent: any) => { const onSelectNodeHandler = (event: MouseEvent) => { if (!props.isDraggable) { if (props.isSelectable) { - pinia.unsetNodesSelection(); + store?.unsetNodesSelection(); if (!props.selected) { - pinia.addSelectedElements([node.value as Node]); + store?.addSelectedElements([node.value as Node]); } } @@ -81,70 +80,62 @@ export default (NodeComponent: any) => { } }; - /** - const onDragStart = (event: DraggableEvent) => { - onNodeDragStart?.(event as MouseEvent, node); + const onDragStart = (event: DragEvent) => { + props.onNodeDragStart?.(event as MouseEvent, node.value as Node); - if (selectNodesOnDrag && isSelectable) { - unsetNodesSelection(); + if (props.selectNodesOnDrag && props.isSelectable) { + store?.unsetNodesSelection(); - if (!selected) { - addSelectedElements(node); + if (!props.selected) { + store?.addSelectedElements([node.value as Node]); } - } else if (!selectNodesOnDrag && !selected && isSelectable) { - unsetNodesSelection(); - addSelectedElements([]); + } else if (!props.selectNodesOnDrag && !props.selected && props.isSelectable) { + store?.unsetNodesSelection(); + store?.addSelectedElements([]); } }; - const onDrag = useCallback( - (event: DraggableEvent, draggableData: DraggableData) => { - if (onNodeDrag) { - node.position.x += draggableData.deltaX; - node.position.y += draggableData.deltaY; - onNodeDrag(event as MouseEvent, node); + const onDrag = (event: DragEvent, draggableData: any) => { + if (props.onNodeDrag) { + node.value.position.x += draggableData.deltaX; + node.value.position.y += draggableData.deltaY; + props.onNodeDrag(event as MouseEvent, node.value as Node); + } + + store?.updateNodePosDiff({ + id: props.id as string, + diff: { + x: draggableData.deltaX, + y: draggableData.deltaY + }, + isDragging: true + }); + }; + + const onDragStop = (event: DragEvent) => { + // onDragStop also gets called when user just clicks on a node. + // Because of that we set dragging to true inside the onDrag handler and handle the click here + if (!props.isDragging) { + if (props.isSelectable && !props.selectNodesOnDrag && !props.selected) { + store?.addSelectedElements([node.value as Node]); } - updateNodePosDiff({ - id, - diff: { - x: draggableData.deltaX, - y: draggableData.deltaY - }, - isDragging: true - }); - }, - [id, node, onNodeDrag] - ); + props.onClick?.(event as MouseEvent, node.value as Node); - const onDragStop = useCallback( - (event: DraggableEvent) => { - // onDragStop also gets called when user just clicks on a node. - // Because of that we set dragging to true inside the onDrag handler and handle the click here - if (!isDragging) { - if (isSelectable && !selectNodesOnDrag && !selected) { - addSelectedElements(node); - } + return; + } - onClick?.(event as MouseEvent, node); + store?.updateNodePosDiff({ + id: node.value.id as string, + isDragging: false + }); - return; - } - - updateNodePosDiff({ - id: node.id, - isDragging: false - }); - - onNodeDragStop?.(event as MouseEvent, node); - }, - [node, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop, isDragging, selected] - ); - */ + props.onNodeDragStop?.(event as MouseEvent, node.value as Node); + }; onMounted(() => { if (nodeElement.value && !props.isHidden) { - pinia.updateNodeDimensions([{ id: props.id || '', nodeElement: nodeElement.value, forceUpdate: true }]); + store?.updateNodeDimensions([{ id: props.id || '', nodeElement: nodeElement.value, forceUpdate: true }]); } }); @@ -161,18 +152,18 @@ export default (NodeComponent: any) => { return null; } - const nodeClasses = [ + const nodeClasses = computed(() => [ 'revue-flow__node', `revue-flow__node-${props.type}`, { selected: props.selected, selectable: props.isSelectable } - ]; + ]); return () => (
void; + onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void; + onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void; + onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; +} + +const NodesSelection = ({ + onSelectionDragStart, + onSelectionDrag, + onSelectionDragStop, + onSelectionContextMenu +}: NodesSelectionProps) => { + const store = inject('store'); + + const nodeRef = ref(null); + + const grid = computed(() => (store?.snapToGrid ? store?.snapGrid : [1, 1])! as [number, number]); + + const selectedNodes = computed(() => + store?.selectedElements + ? store?.selectedElements.filter(isNode).map((selectedNode) => { + const matchingNode = store?.nodes.find((node) => node.id === selectedNode.id); + + return { + ...matchingNode, + position: matchingNode?.__rf.position + } as Node; + }) + : [] + ); + + const style = computed(() => ({ + transform: `translate(${store?.transform[0]}px,${store?.transform[1]}px) scale(${store?.transform[2]})` + })); + + const innerStyle = computed(() => ({ + width: store?.selectedNodesBbox.width, + height: store?.selectedNodesBbox.height, + top: store?.selectedNodesBbox.y, + left: store?.selectedNodesBbox.x + })); + + const onStart = (event: MouseEvent) => { + onSelectionDragStart?.(event, selectedNodes.value); + }; + + const onDrag = (event: MouseEvent, data: MoveEvent) => { + if (onSelectionDrag) { + onSelectionDrag(event, selectedNodes.value); + } + console.log('dragging', data); + }; + + const onStop = (event: MouseEvent) => { + store?.updateNodePosDiff({ + isDragging: false + }); + + onSelectionDragStop?.(event, selectedNodes.value); + }; + + const onContextMenu = (event: MouseEvent) => { + const selectedNodes = store?.selectedElements + ? store?.selectedElements.filter(isNode).map((selectedNode) => store?.nodes.find((node) => node.id === selectedNode.id)) + : []; + + onSelectionContextMenu?.(event, selectedNodes as any); + }; + + if (!store?.selectedElements || store?.selectionActive) { + return null; + } + + return ( +
+ onStart(event)} + onDrag={(event: MouseEvent, data: any) => onDrag(event, data)} + onStop={(event: MouseEvent) => onStop(event)} + nodeRef={nodeRef} + enableUserSelectHack={false} + > +
+ +
+ ); +}; + +export default NodesSelection; diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index 38055ce0..77a5ea18 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -11,10 +11,11 @@ import { ConnectionLineComponent, ConnectionMode, Transform, - OnEdgeUpdateFunc + OnEdgeUpdateFunc, + RevueFlowStore, + HandleType } from '../../types'; -import { computed, CSSProperties, defineComponent, PropType } from 'vue'; -import store from '../../store'; +import { computed, CSSProperties, defineComponent, inject, PropType } from 'vue'; import MarkerDefinitions from './MarkerDefinitions'; interface EdgeRendererProps { @@ -313,43 +314,43 @@ const EdgeRenderer = defineComponent({ } }, setup(props) { - const pinia = store(); - const transform = computed(() => pinia.transform); + const store = inject('store'); + const transform = computed(() => store?.transform); const transformStyle = computed(() => { - return `translate(${transform.value[0]},${transform.value[1]}) scale(${transform.value[2]})`; + return `translate(${transform.value?.[0]},${transform.value?.[1]}) scale(${transform.value?.[2]})`; }); - const renderConnectionLine = computed(() => pinia.connectionNodeId && pinia.connectionHandleType); + const renderConnectionLine = computed(() => store?.connectionNodeId && store?.connectionHandleType); return () => ( - - + + - {pinia.edges.map((edge: Edge) => ( + {store?.edges.map((edge: Edge) => ( ))} {renderConnectionLine.value && ( )} diff --git a/src/container/FlowRenderer/index.tsx b/src/container/FlowRenderer/index.tsx index 23612f47..1ce91bb3 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -1,8 +1,11 @@ import { GraphViewProps } from '../GraphView'; import ZoomPane from '../ZoomPane'; import UserSelection from '../../components/UserSelection'; -import { defineComponent, PropType } from 'vue'; -import store from '../../store'; +import NodesSelection from '../../components/NodesSelection'; +import { defineComponent, inject, PropType } from 'vue'; +import { RevueFlowStore } from '../../types'; +import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler'; +import useKeyPress from '../../hooks/useKeyPress'; type FlowRendererProps = Omit< GraphViewProps, @@ -19,7 +22,7 @@ type FlowRendererProps = Omit< const FlowRenderer = defineComponent({ name: 'FlowRenderer', - components: { UserSelection, ZoomPane }, + components: { UserSelection, ZoomPane, NodesSelection }, props: { onPaneClick: { type: Function() as PropType, @@ -153,12 +156,20 @@ const FlowRenderer = defineComponent({ } }, setup(props, { slots }) { - const pinia = store(); + const store = inject('store'); + + const selectionKeyPressed = useKeyPress(props.selectionKeyCode); + + useGlobalKeyHandler({ + onElementsRemove: props.onElementsRemove, + deleteKeyCode: props.deleteKeyCode as string, + multiSelectionKeyCode: props.multiSelectionKeyCode as string + }); const onClick = (event: MouseEvent) => { props.onPaneClick?.(event); - pinia.unsetNodesSelection(); - pinia.resetSelectedElements(); + store?.unsetNodesSelection(); + store?.resetSelectedElements(); }; const onContextMenu = (event: MouseEvent) => { @@ -189,6 +200,15 @@ const FlowRenderer = defineComponent({ zoomActivationKeyCode={props.zoomActivationKeyCode} > {slots.default ? slots.default() : ''} + + {store?.nodesSelectionActive && ( + + )}
); diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 85a6bb21..27aa2daf 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -3,9 +3,8 @@ import NodeRenderer from '../NodeRenderer'; import EdgeRenderer from '../EdgeRenderer'; import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph'; import { RevueFlowProps } from '../RevueFlow'; -import { NodeTypesType, EdgeTypesType, ConnectionLineType, KeyCode } from '../../types'; -import { CSSProperties, defineComponent, onBeforeMount, onMounted, PropType, ref } from 'vue'; -import store from '../../store'; +import { NodeTypesType, EdgeTypesType, ConnectionLineType, KeyCode, RevueFlowStore } from '../../types'; +import { CSSProperties, defineComponent, inject, onBeforeMount, onMounted, PropType, ref } from 'vue'; import useZoomPanHelper from '../../hooks/useZoomPanHelper'; export interface GraphViewProps extends Omit { @@ -39,102 +38,102 @@ const GraphView = defineComponent({ default: undefined }, onMove: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onMoveStart: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onMoveEnd: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onLoad: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onElementClick: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeDoubleClick: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeDoubleClick: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeMouseEnter: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeMouseMove: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeMouseLeave: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeContextMenu: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeDragStart: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeDrag: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onNodeDragStop: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onSelectionContextMenu: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onElementsRemove: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onConnect: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onConnectStart: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onConnectStop: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onConnectEnd: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, @@ -289,52 +288,52 @@ const GraphView = defineComponent({ default: undefined }, onPaneClick: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onPaneScroll: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onPaneContextMenu: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeUpdate: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeMouseEnter: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeContextMenu: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeMouseMove: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeMouseLeave: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeUpdateEnd: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeUpdateStart: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, @@ -345,23 +344,23 @@ const GraphView = defineComponent({ } }, setup(props) { - const pinia = store(); + const store = inject('store'); const isInitialized = ref(false); onMounted(() => { const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper(); if (!isInitialized.value && initialized) { - if (props.onLoad) { + if (props.onLoad && store) { props.onLoad({ fitView: (params = { padding: 0.1 }) => fitView(params), zoomIn, zoomOut, zoomTo, setTransform: transform, - project: onLoadProject(pinia), - getElements: onLoadGetElements(pinia), - toObject: onLoadToObject(pinia) + project: onLoadProject(store), + getElements: onLoadGetElements(store), + toObject: onLoadToObject(store) }); } isInitialized.value = true; @@ -370,71 +369,71 @@ const GraphView = defineComponent({ onBeforeMount(() => { if (props.onConnect) { - pinia.setOnConnect(props.onConnect); + store?.setOnConnect(props.onConnect); } if (props.onConnectStart) { - pinia.setOnConnectStart(props.onConnectStart); + store?.setOnConnectStart(props.onConnectStart); } if (props.onConnectStop) { - pinia.setOnConnectStop(props.onConnectStop); + store?.setOnConnectStop(props.onConnectStop); } if (props.onConnectEnd) { - pinia.setOnConnectEnd(props.onConnectEnd); + store?.setOnConnectEnd(props.onConnectEnd); } if (typeof props.snapToGrid !== 'undefined') { - pinia.setSnapToGrid(props.snapToGrid); + store?.setSnapToGrid(props.snapToGrid); } if (typeof props.snapGrid !== 'undefined') { - pinia.setSnapGrid(props.snapGrid); + store?.setSnapGrid(props.snapGrid); } if (typeof props.nodesDraggable !== 'undefined') { - pinia.setNodesDraggable(!!props.nodesDraggable); + store?.setNodesDraggable(!!props.nodesDraggable); } if (typeof props.nodesConnectable !== 'undefined') { - pinia.setNodesConnectable(!!props.nodesConnectable); + store?.setNodesConnectable(!!props.nodesConnectable); } if (typeof props.elementsSelectable !== 'undefined') { - pinia.setElementsSelectable(!!props.elementsSelectable); + store?.setElementsSelectable(!!props.elementsSelectable); } if (typeof props.minZoom !== 'undefined') { - pinia.setMinZoom(props.minZoom as any); + store?.setMinZoom(props.minZoom as any); } if (typeof props.maxZoom !== 'undefined') { - pinia.setMaxZoom(props.maxZoom as any); + store?.setMaxZoom(props.maxZoom as any); } if (typeof props.translateExtent !== 'undefined') { - pinia.setTranslateExtent(props.translateExtent as any); + store?.setTranslateExtent(props.translateExtent as any); } if (typeof props.nodeExtent !== 'undefined') { - pinia.setNodeExtent(props.nodeExtent as any); + store?.setNodeExtent(props.nodeExtent as any); } }); onMounted(() => { if (typeof props.nodesConnectable !== 'undefined') { - pinia.setNodesConnectable(!!props.nodesConnectable); + store?.setNodesConnectable(!!props.nodesConnectable); } }); onMounted(() => { if (typeof props.elementsSelectable !== 'undefined') { - pinia.setElementsSelectable(!!props.elementsSelectable); + store?.setElementsSelectable(!!props.elementsSelectable); } if (typeof props.connectionMode !== 'undefined') { - pinia.setConnectionMode(props.connectionMode as any); + store?.setConnectionMode(props.connectionMode as any); } }); @@ -462,11 +461,9 @@ const GraphView = defineComponent({ defaultPosition={props.defaultPosition as any} defaultZoom={props.defaultZoom as any} translateExtent={props.translateExtent} - /* - onSelectionDragStart={props.onSelectionDragStart} - onSelectionDrag={props.onSelectionDrag} - onSelectionDragStop={props.onSelectionDragStop} - */ + //onSelectionDragStart={props.onSelectionDragStart} + //onSelectionDrag={props.onSelectionDrag} + //onSelectionDragStop={props.onSelectionDragStop} onSelectionContextMenu={props.onSelectionContextMenu} > , + type: Function as unknown as PropType, required: false, default: undefined }, onNodeDoubleClick: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined }, onNodeMouseEnter: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined }, onNodeMouseMove: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined }, onNodeMouseLeave: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined }, onNodeContextMenu: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined }, onNodeDrag: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined }, onNodeDragStart: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined }, onNodeDragStop: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined } }, setup(props) { - const pinia = store(); + const store = inject('store'); const visibleNodes = computed(() => { return props.onlyRenderVisibleElements - ? getNodesInside( - pinia.nodes, - { - x: 0, - y: 0, - width: pinia.width, - height: pinia.height - }, - pinia.transform, - true - ) - : pinia.nodes; + ? store?.nodes && + getNodesInside( + store?.nodes, + { + x: 0, + y: 0, + width: store?.width, + height: store?.height + }, + store?.transform, + true + ) + : store?.nodes; }); const transformStyle = computed(() => ({ - transform: `translate(${pinia.transform[0]}px,${pinia.transform[1]}px) scale(${pinia.transform[2]})` + transform: `translate(${store?.transform[0]}px,${store?.transform[1]}px) scale(${store?.transform[2]})` })); const resizeObserver = computed(() => { @@ -128,13 +128,13 @@ const NodeRenderer = defineComponent({ nodeElement: entry.target as HTMLDivElement })); - pinia.updateNodeDimensions(updates); + store?.updateNodeDimensions(updates); }); }); return () => (
- {visibleNodes.value.map((node) => { + {visibleNodes.value?.map((node) => { const nodeType = node.type || 'default'; if (props.nodeTypes) { const NodeComponent: any = props.nodeTypes[nodeType] || props.nodeTypes.default; @@ -142,9 +142,9 @@ const NodeRenderer = defineComponent({ console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`); } - const isDraggable = !!(node.draggable || (pinia.nodesDraggable && typeof node.draggable === 'undefined')); - const isSelectable = !!(node.selectable || (pinia.elementsSelectable && typeof node.selectable === 'undefined')); - const isConnectable = !!(node.connectable || (pinia.nodesConnectable && typeof node.connectable === 'undefined')); + const isDraggable = !!(node.draggable || (store?.nodesDraggable && typeof node.draggable === 'undefined')); + const isSelectable = !!(node.selectable || (store?.elementsSelectable && typeof node.selectable === 'undefined')); + const isConnectable = !!(node.connectable || (store?.nodesConnectable && typeof node.connectable === 'undefined')); return ( id === node.id) || false} + scale={store?.transform[2]} + selected={store?.selectedElements?.some(({ id }) => id === node.id) || false} isDraggable={isDraggable} isSelectable={isSelectable} isConnectable={isConnectable} diff --git a/src/container/RevueFlow/index.tsx b/src/container/RevueFlow/index.tsx index 5a191305..57146f85 100644 --- a/src/container/RevueFlow/index.tsx +++ b/src/container/RevueFlow/index.tsx @@ -1,14 +1,4 @@ -import { - computed, - CSSProperties, - defineComponent, - HTMLAttributes, - onBeforeUnmount, - onMounted, - onUpdated, - PropType, - watch -} from 'vue'; +import { computed, CSSProperties, defineComponent, HTMLAttributes, onBeforeUnmount, onUpdated, PropType, provide } from 'vue'; import GraphView from '../GraphView'; import DefaultNode from '../../components/Nodes/DefaultNode'; import InputNode from '../../components/Nodes/InputNode'; @@ -427,32 +417,32 @@ const RevueFlow = defineComponent({ default: undefined as any }, onEdgeMouseEnter: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeContextMenu: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeMouseMove: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeMouseLeave: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeUpdateEnd: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, onEdgeUpdateStart: { - type: Function() as PropType, + type: Function as unknown as PropType, required: false, default: undefined as any }, @@ -464,13 +454,11 @@ const RevueFlow = defineComponent({ }, setup(props, { slots }) { const store = configureStore(initialState)(); - watch(props.elements, () => store.setElements(props.elements)); + provide('store', store); + store.setElements(props.elements); onUpdated(() => { store.setElements(props.elements); }); - onMounted(() => { - store.setElements(props.elements); - }); onBeforeUnmount(() => { store.$reset(); store.setElements([]); diff --git a/src/store/configure-store.ts b/src/store/configure-store.ts index 4bbdf2c0..4f2f004b 100644 --- a/src/store/configure-store.ts +++ b/src/store/configure-store.ts @@ -1,7 +1,6 @@ -import { setActivePinia, createPinia, defineStore, StoreDefinition, Pinia } from 'pinia'; -import { inject } from 'vue'; +import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia'; import isEqual from 'fast-deep-equal'; -import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActionsTree, XYPosition } from '../types'; +import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '../types'; import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph'; import { clampPosition, getDimensions } from '../utils'; import { getHandleBounds } from '../components/Nodes/utils'; @@ -10,11 +9,11 @@ type NextElements = { nextNodes: Node[]; nextEdges: Edge[]; }; +const pinia = createPinia(); export default function configureStore( preloadedState: RevueFlowState -): StoreDefinition { - const pinia = inject(Symbol('pinia')) ?? createPinia(); +): StoreDefinition { setActivePinia(pinia); return defineStore({ diff --git a/src/store/index.ts b/src/store/index.ts index 27977381..6d6ba161 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -58,5 +58,4 @@ export const initialState: RevueFlowState = { const store = configureStore(initialState); -export type StoreType = typeof store; export default store; diff --git a/src/types/index.ts b/src/types/index.ts index 76fcca97..664132a3 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,6 @@ import { Selection as D3Selection, ZoomBehavior } from 'd3'; import { Component, HTMLAttributes, PropType, VNode } from 'vue'; +import { Store } from 'pinia'; export type ElementId = string; @@ -869,7 +870,7 @@ export interface RevueFlowState { onConnectEnd?: OnConnectEndFunc; } -export type RevueFlowActionsTree = { +export type RevueFlowActions = { setOnConnect: (onConnect: OnConnectFunc) => void; setOnConnectStart: (onConnectStart: OnConnectStartFunc) => void; setOnConnectStop: (onConnectStop: OnConnectStopFunc) => void; @@ -903,4 +904,6 @@ export type RevueFlowActionsTree = { setConnectionMode: (connectionMode: ConnectionMode) => void; }; +export type RevueFlowStore = Store; + export type UpdateNodeInternals = (nodeId: ElementId) => void; diff --git a/src/utils/graph.ts b/src/utils/graph.ts index 09906228..fc568235 100644 --- a/src/utils/graph.ts +++ b/src/utils/graph.ts @@ -1,5 +1,3 @@ -import { Store } from 'pinia'; - import { clampPosition, clamp } from './index'; import { @@ -13,8 +11,8 @@ import { Box, Connection, FlowExportObject, - RevueFlowState, - NodeExtent + NodeExtent, + RevueFlowStore } from '../types'; export const isEdge = (element: Node | Connection | Edge): element is Edge => @@ -138,7 +136,7 @@ export const pointToRendererPoint = ( return position; }; -export const onLoadProject = (currentStore: Store<'revue-flow', RevueFlowState>) => { +export const onLoadProject = (currentStore: RevueFlowStore) => { return (position: XYPosition): XYPosition => { return pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid); }; @@ -266,13 +264,13 @@ const parseElements = (nodes: Node[], edges: Edge[]): Elements => { ]; }; -export const onLoadGetElements = (currentStore: Store<'revue-flow', RevueFlowState>) => { +export const onLoadGetElements = (currentStore: RevueFlowStore) => { return (): Elements => { return parseElements(currentStore.nodes || [], currentStore.edges || []); }; }; -export const onLoadToObject = (currentStore: Store<'revue-flow', RevueFlowState>) => { +export const onLoadToObject = (currentStore: RevueFlowStore) => { return (): FlowExportObject => { return { elements: parseElements(currentStore.nodes || [], currentStore.edges || []), diff --git a/yarn.lock b/yarn.lock index 216843ec..ca62e5d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5195,6 +5195,11 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +sortablejs@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290" + integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A== + source-map-js@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" @@ -5844,6 +5849,13 @@ vue@^3.0.5: "@vue/runtime-dom" "3.1.4" "@vue/shared" "3.1.4" +vuedraggable@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.0.3.tgz#98832a3562d44c73bbe4e205cc2dafce07b6ca14" + integrity sha512-NkJYk+UyxgEoSQcgvVZtqY6dYpdXkBHS8aq6CqoJAfXVM9ZRYT0WPdlBbTttG4nCwllU2M5JGGgo9Drt/L0a7w== + dependencies: + sortablejs "1.10.2" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"