refactor: use injected store so we can use a distinct instance not a global one

This commit is contained in:
Braks
2021-07-13 16:13:15 +02:00
parent 8f727a4d93
commit 6c9d88b09d
13 changed files with 327 additions and 217 deletions
-1
View File
@@ -58,7 +58,6 @@
"np": "^7.5.0", "np": "^7.5.0",
"pascalcase": "^1.0.0", "pascalcase": "^1.0.0",
"postcss": "^8.3.5", "postcss": "^8.3.5",
"postcss-cli": "^8.3.1",
"postcss-nested": "^5.0.5", "postcss-nested": "^5.0.5",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"rollup": "^2.52.2", "rollup": "^2.52.2",
+50 -59
View File
@@ -1,12 +1,11 @@
import { Node, WrapNodeProps } from '../../types'; import { Node, RevueFlowStore, WrapNodeProps } from '../../types';
import { computed, CSSProperties, defineComponent, onMounted, provide, ref } from 'vue'; import { computed, CSSProperties, defineComponent, inject, onMounted, provide, ref } from 'vue';
import store from '../../store';
export default (NodeComponent: any) => { export default (NodeComponent: any) => {
return defineComponent({ return defineComponent({
props: WrapNodeProps, props: WrapNodeProps,
setup(props) { setup(props) {
const pinia = store(); const store = inject<RevueFlowStore>('store');
provide('NodeIdContext', props.id); provide('NodeIdContext', props.id);
const nodeElement = ref<HTMLDivElement | null>(null); const nodeElement = ref<HTMLDivElement | null>(null);
@@ -70,10 +69,10 @@ export default (NodeComponent: any) => {
const onSelectNodeHandler = (event: MouseEvent) => { const onSelectNodeHandler = (event: MouseEvent) => {
if (!props.isDraggable) { if (!props.isDraggable) {
if (props.isSelectable) { if (props.isSelectable) {
pinia.unsetNodesSelection(); store?.unsetNodesSelection();
if (!props.selected) { 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: DragEvent) => {
const onDragStart = (event: DraggableEvent) => { props.onNodeDragStart?.(event as MouseEvent, node.value as Node);
onNodeDragStart?.(event as MouseEvent, node);
if (selectNodesOnDrag && isSelectable) { if (props.selectNodesOnDrag && props.isSelectable) {
unsetNodesSelection(); store?.unsetNodesSelection();
if (!selected) { if (!props.selected) {
addSelectedElements(node); store?.addSelectedElements([node.value as Node]);
} }
} else if (!selectNodesOnDrag && !selected && isSelectable) { } else if (!props.selectNodesOnDrag && !props.selected && props.isSelectable) {
unsetNodesSelection(); store?.unsetNodesSelection();
addSelectedElements([]); store?.addSelectedElements([]);
} }
}; };
const onDrag = useCallback( const onDrag = (event: DragEvent, draggableData: any) => {
(event: DraggableEvent, draggableData: DraggableData) => { if (props.onNodeDrag) {
if (onNodeDrag) { node.value.position.x += draggableData.deltaX;
node.position.x += draggableData.deltaX; node.value.position.y += draggableData.deltaY;
node.position.y += draggableData.deltaY; props.onNodeDrag(event as MouseEvent, node.value as Node);
onNodeDrag(event as MouseEvent, 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({ props.onClick?.(event as MouseEvent, node.value as Node);
id,
diff: {
x: draggableData.deltaX,
y: draggableData.deltaY
},
isDragging: true
});
},
[id, node, onNodeDrag]
);
const onDragStop = useCallback( return;
(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);
}
onClick?.(event as MouseEvent, node); store?.updateNodePosDiff({
id: node.value.id as string,
isDragging: false
});
return; props.onNodeDragStop?.(event as MouseEvent, node.value as Node);
} };
updateNodePosDiff({
id: node.id,
isDragging: false
});
onNodeDragStop?.(event as MouseEvent, node);
},
[node, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop, isDragging, selected]
);
*/
onMounted(() => { onMounted(() => {
if (nodeElement.value && !props.isHidden) { 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; return null;
} }
const nodeClasses = [ const nodeClasses = computed(() => [
'revue-flow__node', 'revue-flow__node',
`revue-flow__node-${props.type}`, `revue-flow__node-${props.type}`,
{ {
selected: props.selected, selected: props.selected,
selectable: props.isSelectable selectable: props.isSelectable
} }
]; ]);
return () => ( return () => (
<div <div
class={nodeClasses} class={nodeClasses.value}
ref={nodeElement} ref={nodeElement}
style={nodeStyle.value} style={nodeStyle.value}
onMouseenter={onMouseEnterHandler} onMouseenter={onMouseEnterHandler}
+103
View File
@@ -0,0 +1,103 @@
/**
* The nodes selection rectangle gets displayed when a user
* made a selectio with on or several nodes
*/
import VueDraggable, { MoveEvent } from 'vuedraggable';
import { isNode } from '../../utils/graph';
import { Node, RevueFlowStore } from '../../types';
import { computed, inject, ref } from 'vue';
export interface NodesSelectionProps {
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => 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<RevueFlowStore>('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<any>) => {
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 (
<div class="revue-flow__nodesselection" style={style.value}>
<VueDraggable
scale={store?.transform[2]}
grid={grid}
onStart={(event: MouseEvent) => onStart(event)}
onDrag={(event: MouseEvent, data: any) => onDrag(event, data)}
onStop={(event: MouseEvent) => onStop(event)}
nodeRef={nodeRef}
enableUserSelectHack={false}
>
<div ref={nodeRef} class="revue-flow__nodesselection-rect" onContextmenu={onContextMenu} style={innerStyle.value} />
</VueDraggable>
</div>
);
};
export default NodesSelection;
+25 -24
View File
@@ -11,10 +11,11 @@ import {
ConnectionLineComponent, ConnectionLineComponent,
ConnectionMode, ConnectionMode,
Transform, Transform,
OnEdgeUpdateFunc OnEdgeUpdateFunc,
RevueFlowStore,
HandleType
} from '../../types'; } from '../../types';
import { computed, CSSProperties, defineComponent, PropType } from 'vue'; import { computed, CSSProperties, defineComponent, inject, PropType } from 'vue';
import store from '../../store';
import MarkerDefinitions from './MarkerDefinitions'; import MarkerDefinitions from './MarkerDefinitions';
interface EdgeRendererProps { interface EdgeRendererProps {
@@ -313,43 +314,43 @@ const EdgeRenderer = defineComponent({
} }
}, },
setup(props) { setup(props) {
const pinia = store(); const store = inject<RevueFlowStore>('store');
const transform = computed(() => pinia.transform); const transform = computed(() => store?.transform);
const transformStyle = computed(() => { 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 () => ( return () => (
<svg width={pinia.width} height={pinia.height} class="revue-flow__edges"> <svg width={store?.width} height={store?.height} class="revue-flow__edges">
<MarkerDefinitions color={props.arrowHeadColor || ''} /> <MarkerDefinitions color={props.arrowHeadColor as string} />
<g transform={transformStyle.value}> <g transform={transformStyle.value}>
{pinia.edges.map((edge: Edge) => ( {store?.edges.map((edge: Edge) => (
<EdgeCmp <EdgeCmp
key={edge.id} key={edge.id}
edge={edge} edge={edge}
props={props as any} props={props as any}
nodes={pinia.nodes} nodes={store?.nodes}
selectedElements={pinia.selectedElements} selectedElements={store?.selectedElements}
elementsSelectable={pinia.elementsSelectable} elementsSelectable={store?.elementsSelectable}
transform={pinia.transform} transform={store?.transform}
width={pinia.width} width={store?.width}
height={pinia.height} height={store?.height}
onlyRenderVisibleElements={props.onlyRenderVisibleElements} onlyRenderVisibleElements={props.onlyRenderVisibleElements}
/> />
))} ))}
{renderConnectionLine.value && ( {renderConnectionLine.value && (
<ConnectionLine <ConnectionLine
nodes={pinia.nodes} nodes={store?.nodes}
connectionNodeId={pinia.connectionNodeId!} connectionNodeId={store?.connectionNodeId as string}
connectionHandleId={pinia.connectionHandleId} connectionHandleId={store?.connectionHandleId as string}
connectionHandleType={pinia.connectionHandleType!} connectionHandleType={store?.connectionHandleType as HandleType}
connectionPositionX={pinia.connectionPosition.x} connectionPositionX={store?.connectionPosition.x}
connectionPositionY={pinia.connectionPosition.y} connectionPositionY={store?.connectionPosition.y}
transform={pinia.transform} transform={store?.transform}
connectionLineStyle={props.connectionLineStyle} connectionLineStyle={props.connectionLineStyle}
connectionLineType={props.connectionLineType} connectionLineType={props.connectionLineType}
isConnectable={pinia.nodesConnectable} isConnectable={store?.nodesConnectable}
CustomConnectionLineComponent={props.connectionLineComponent} CustomConnectionLineComponent={props.connectionLineComponent}
/> />
)} )}
+26 -6
View File
@@ -1,8 +1,11 @@
import { GraphViewProps } from '../GraphView'; import { GraphViewProps } from '../GraphView';
import ZoomPane from '../ZoomPane'; import ZoomPane from '../ZoomPane';
import UserSelection from '../../components/UserSelection'; import UserSelection from '../../components/UserSelection';
import { defineComponent, PropType } from 'vue'; import NodesSelection from '../../components/NodesSelection';
import store from '../../store'; import { defineComponent, inject, PropType } from 'vue';
import { RevueFlowStore } from '../../types';
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
import useKeyPress from '../../hooks/useKeyPress';
type FlowRendererProps = Omit< type FlowRendererProps = Omit<
GraphViewProps, GraphViewProps,
@@ -19,7 +22,7 @@ type FlowRendererProps = Omit<
const FlowRenderer = defineComponent({ const FlowRenderer = defineComponent({
name: 'FlowRenderer', name: 'FlowRenderer',
components: { UserSelection, ZoomPane }, components: { UserSelection, ZoomPane, NodesSelection },
props: { props: {
onPaneClick: { onPaneClick: {
type: Function() as PropType<FlowRendererProps['onPaneClick']>, type: Function() as PropType<FlowRendererProps['onPaneClick']>,
@@ -153,12 +156,20 @@ const FlowRenderer = defineComponent({
} }
}, },
setup(props, { slots }) { setup(props, { slots }) {
const pinia = store(); const store = inject<RevueFlowStore>('store');
const selectionKeyPressed = useKeyPress(props.selectionKeyCode);
useGlobalKeyHandler({
onElementsRemove: props.onElementsRemove,
deleteKeyCode: props.deleteKeyCode as string,
multiSelectionKeyCode: props.multiSelectionKeyCode as string
});
const onClick = (event: MouseEvent) => { const onClick = (event: MouseEvent) => {
props.onPaneClick?.(event); props.onPaneClick?.(event);
pinia.unsetNodesSelection(); store?.unsetNodesSelection();
pinia.resetSelectedElements(); store?.resetSelectedElements();
}; };
const onContextMenu = (event: MouseEvent) => { const onContextMenu = (event: MouseEvent) => {
@@ -189,6 +200,15 @@ const FlowRenderer = defineComponent({
zoomActivationKeyCode={props.zoomActivationKeyCode} zoomActivationKeyCode={props.zoomActivationKeyCode}
> >
{slots.default ? slots.default() : ''} {slots.default ? slots.default() : ''}
<UserSelection selectionKeyPressed={selectionKeyPressed} />
{store?.nodesSelectionActive && (
<NodesSelection
onSelectionDragStart={props.onSelectionDragStart}
onSelectionDrag={props.onSelectionDrag}
onSelectionDragStop={props.onSelectionDragStop}
onSelectionContextMenu={props.onSelectionContextMenu}
/>
)}
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} /> <div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
</ZoomPane> </ZoomPane>
); );
+56 -59
View File
@@ -3,9 +3,8 @@ import NodeRenderer from '../NodeRenderer';
import EdgeRenderer from '../EdgeRenderer'; import EdgeRenderer from '../EdgeRenderer';
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph'; import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph';
import { RevueFlowProps } from '../RevueFlow'; import { RevueFlowProps } from '../RevueFlow';
import { NodeTypesType, EdgeTypesType, ConnectionLineType, KeyCode } from '../../types'; import { NodeTypesType, EdgeTypesType, ConnectionLineType, KeyCode, RevueFlowStore } from '../../types';
import { CSSProperties, defineComponent, onBeforeMount, onMounted, PropType, ref } from 'vue'; import { CSSProperties, defineComponent, inject, onBeforeMount, onMounted, PropType, ref } from 'vue';
import store from '../../store';
import useZoomPanHelper from '../../hooks/useZoomPanHelper'; import useZoomPanHelper from '../../hooks/useZoomPanHelper';
export interface GraphViewProps extends Omit<RevueFlowProps, 'onSelectionChange' | 'elements'> { export interface GraphViewProps extends Omit<RevueFlowProps, 'onSelectionChange' | 'elements'> {
@@ -39,102 +38,102 @@ const GraphView = defineComponent({
default: undefined default: undefined
}, },
onMove: { onMove: {
type: Function() as PropType<GraphViewProps['onMove']>, type: Function as unknown as PropType<GraphViewProps['onMove']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onMoveStart: { onMoveStart: {
type: Function() as PropType<GraphViewProps['onMoveStart']>, type: Function as unknown as PropType<GraphViewProps['onMoveStart']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onMoveEnd: { onMoveEnd: {
type: Function() as PropType<GraphViewProps['onMoveEnd']>, type: Function as unknown as PropType<GraphViewProps['onMoveEnd']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onLoad: { onLoad: {
type: Function() as PropType<GraphViewProps['onLoad']>, type: Function as unknown as PropType<GraphViewProps['onLoad']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onElementClick: { onElementClick: {
type: Function() as PropType<GraphViewProps['onElementClick']>, type: Function as unknown as PropType<GraphViewProps['onElementClick']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeDoubleClick: { onNodeDoubleClick: {
type: Function() as PropType<GraphViewProps['onNodeDoubleClick']>, type: Function as unknown as PropType<GraphViewProps['onNodeDoubleClick']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeDoubleClick: { onEdgeDoubleClick: {
type: Function() as PropType<GraphViewProps['onEdgeDoubleClick']>, type: Function as unknown as PropType<GraphViewProps['onEdgeDoubleClick']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeMouseEnter: { onNodeMouseEnter: {
type: Function() as PropType<GraphViewProps['onNodeMouseEnter']>, type: Function as unknown as PropType<GraphViewProps['onNodeMouseEnter']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeMouseMove: { onNodeMouseMove: {
type: Function() as PropType<GraphViewProps['onNodeMouseMove']>, type: Function as unknown as PropType<GraphViewProps['onNodeMouseMove']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeMouseLeave: { onNodeMouseLeave: {
type: Function() as PropType<GraphViewProps['onNodeMouseLeave']>, type: Function as unknown as PropType<GraphViewProps['onNodeMouseLeave']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeContextMenu: { onNodeContextMenu: {
type: Function() as PropType<GraphViewProps['onNodeContextMenu']>, type: Function as unknown as PropType<GraphViewProps['onNodeContextMenu']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeDragStart: { onNodeDragStart: {
type: Function() as PropType<GraphViewProps['onNodeDragStart']>, type: Function as unknown as PropType<GraphViewProps['onNodeDragStart']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeDrag: { onNodeDrag: {
type: Function() as PropType<GraphViewProps['onNodeDrag']>, type: Function as unknown as PropType<GraphViewProps['onNodeDrag']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onNodeDragStop: { onNodeDragStop: {
type: Function() as PropType<GraphViewProps['onNodeDragStop']>, type: Function as unknown as PropType<GraphViewProps['onNodeDragStop']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onSelectionContextMenu: { onSelectionContextMenu: {
type: Function() as PropType<GraphViewProps['onSelectionContextMenu']>, type: Function as unknown as PropType<GraphViewProps['onSelectionContextMenu']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onElementsRemove: { onElementsRemove: {
type: Function() as PropType<GraphViewProps['onElementsRemove']>, type: Function as unknown as PropType<GraphViewProps['onElementsRemove']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onConnect: { onConnect: {
type: Function() as PropType<GraphViewProps['onConnect']>, type: Function as unknown as PropType<GraphViewProps['onConnect']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onConnectStart: { onConnectStart: {
type: Function() as PropType<GraphViewProps['onConnectStart']>, type: Function as unknown as PropType<GraphViewProps['onConnectStart']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onConnectStop: { onConnectStop: {
type: Function() as PropType<GraphViewProps['onConnectStop']>, type: Function as unknown as PropType<GraphViewProps['onConnectStop']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onConnectEnd: { onConnectEnd: {
type: Function() as PropType<GraphViewProps['onConnectEnd']>, type: Function as unknown as PropType<GraphViewProps['onConnectEnd']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
@@ -289,52 +288,52 @@ const GraphView = defineComponent({
default: undefined default: undefined
}, },
onPaneClick: { onPaneClick: {
type: Function() as PropType<GraphViewProps['onPaneClick']>, type: Function as unknown as PropType<GraphViewProps['onPaneClick']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onPaneScroll: { onPaneScroll: {
type: Function() as PropType<GraphViewProps['onPaneScroll']>, type: Function as unknown as PropType<GraphViewProps['onPaneScroll']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onPaneContextMenu: { onPaneContextMenu: {
type: Function() as PropType<GraphViewProps['onPaneContextMenu']>, type: Function as unknown as PropType<GraphViewProps['onPaneContextMenu']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeUpdate: { onEdgeUpdate: {
type: Function() as PropType<GraphViewProps['onEdgeUpdate']>, type: Function as unknown as PropType<GraphViewProps['onEdgeUpdate']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeMouseEnter: { onEdgeMouseEnter: {
type: Function() as PropType<GraphViewProps['onEdgeMouseEnter']>, type: Function as unknown as PropType<GraphViewProps['onEdgeMouseEnter']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeContextMenu: { onEdgeContextMenu: {
type: Function() as PropType<GraphViewProps['onEdgeContextMenu']>, type: Function as unknown as PropType<GraphViewProps['onEdgeContextMenu']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeMouseMove: { onEdgeMouseMove: {
type: Function() as PropType<GraphViewProps['onEdgeMouseMove']>, type: Function as unknown as PropType<GraphViewProps['onEdgeMouseMove']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeMouseLeave: { onEdgeMouseLeave: {
type: Function() as PropType<GraphViewProps['onEdgeMouseLeave']>, type: Function as unknown as PropType<GraphViewProps['onEdgeMouseLeave']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeUpdateEnd: { onEdgeUpdateEnd: {
type: Function() as PropType<GraphViewProps['onEdgeUpdateEnd']>, type: Function as unknown as PropType<GraphViewProps['onEdgeUpdateEnd']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeUpdateStart: { onEdgeUpdateStart: {
type: Function() as PropType<GraphViewProps['onEdgeUpdateStart']>, type: Function as unknown as PropType<GraphViewProps['onEdgeUpdateStart']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
@@ -345,23 +344,23 @@ const GraphView = defineComponent({
} }
}, },
setup(props) { setup(props) {
const pinia = store(); const store = inject<RevueFlowStore>('store');
const isInitialized = ref<boolean>(false); const isInitialized = ref<boolean>(false);
onMounted(() => { onMounted(() => {
const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper(); const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper();
if (!isInitialized.value && initialized) { if (!isInitialized.value && initialized) {
if (props.onLoad) { if (props.onLoad && store) {
props.onLoad({ props.onLoad({
fitView: (params = { padding: 0.1 }) => fitView(params), fitView: (params = { padding: 0.1 }) => fitView(params),
zoomIn, zoomIn,
zoomOut, zoomOut,
zoomTo, zoomTo,
setTransform: transform, setTransform: transform,
project: onLoadProject(pinia), project: onLoadProject(store),
getElements: onLoadGetElements(pinia), getElements: onLoadGetElements(store),
toObject: onLoadToObject(pinia) toObject: onLoadToObject(store)
}); });
} }
isInitialized.value = true; isInitialized.value = true;
@@ -370,71 +369,71 @@ const GraphView = defineComponent({
onBeforeMount(() => { onBeforeMount(() => {
if (props.onConnect) { if (props.onConnect) {
pinia.setOnConnect(props.onConnect); store?.setOnConnect(props.onConnect);
} }
if (props.onConnectStart) { if (props.onConnectStart) {
pinia.setOnConnectStart(props.onConnectStart); store?.setOnConnectStart(props.onConnectStart);
} }
if (props.onConnectStop) { if (props.onConnectStop) {
pinia.setOnConnectStop(props.onConnectStop); store?.setOnConnectStop(props.onConnectStop);
} }
if (props.onConnectEnd) { if (props.onConnectEnd) {
pinia.setOnConnectEnd(props.onConnectEnd); store?.setOnConnectEnd(props.onConnectEnd);
} }
if (typeof props.snapToGrid !== 'undefined') { if (typeof props.snapToGrid !== 'undefined') {
pinia.setSnapToGrid(props.snapToGrid); store?.setSnapToGrid(props.snapToGrid);
} }
if (typeof props.snapGrid !== 'undefined') { if (typeof props.snapGrid !== 'undefined') {
pinia.setSnapGrid(props.snapGrid); store?.setSnapGrid(props.snapGrid);
} }
if (typeof props.nodesDraggable !== 'undefined') { if (typeof props.nodesDraggable !== 'undefined') {
pinia.setNodesDraggable(!!props.nodesDraggable); store?.setNodesDraggable(!!props.nodesDraggable);
} }
if (typeof props.nodesConnectable !== 'undefined') { if (typeof props.nodesConnectable !== 'undefined') {
pinia.setNodesConnectable(!!props.nodesConnectable); store?.setNodesConnectable(!!props.nodesConnectable);
} }
if (typeof props.elementsSelectable !== 'undefined') { if (typeof props.elementsSelectable !== 'undefined') {
pinia.setElementsSelectable(!!props.elementsSelectable); store?.setElementsSelectable(!!props.elementsSelectable);
} }
if (typeof props.minZoom !== 'undefined') { if (typeof props.minZoom !== 'undefined') {
pinia.setMinZoom(props.minZoom as any); store?.setMinZoom(props.minZoom as any);
} }
if (typeof props.maxZoom !== 'undefined') { if (typeof props.maxZoom !== 'undefined') {
pinia.setMaxZoom(props.maxZoom as any); store?.setMaxZoom(props.maxZoom as any);
} }
if (typeof props.translateExtent !== 'undefined') { if (typeof props.translateExtent !== 'undefined') {
pinia.setTranslateExtent(props.translateExtent as any); store?.setTranslateExtent(props.translateExtent as any);
} }
if (typeof props.nodeExtent !== 'undefined') { if (typeof props.nodeExtent !== 'undefined') {
pinia.setNodeExtent(props.nodeExtent as any); store?.setNodeExtent(props.nodeExtent as any);
} }
}); });
onMounted(() => { onMounted(() => {
if (typeof props.nodesConnectable !== 'undefined') { if (typeof props.nodesConnectable !== 'undefined') {
pinia.setNodesConnectable(!!props.nodesConnectable); store?.setNodesConnectable(!!props.nodesConnectable);
} }
}); });
onMounted(() => { onMounted(() => {
if (typeof props.elementsSelectable !== 'undefined') { if (typeof props.elementsSelectable !== 'undefined') {
pinia.setElementsSelectable(!!props.elementsSelectable); store?.setElementsSelectable(!!props.elementsSelectable);
} }
if (typeof props.connectionMode !== 'undefined') { 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} defaultPosition={props.defaultPosition as any}
defaultZoom={props.defaultZoom as any} defaultZoom={props.defaultZoom as any}
translateExtent={props.translateExtent} translateExtent={props.translateExtent}
/* //onSelectionDragStart={props.onSelectionDragStart}
onSelectionDragStart={props.onSelectionDragStart} //onSelectionDrag={props.onSelectionDrag}
onSelectionDrag={props.onSelectionDrag} //onSelectionDragStop={props.onSelectionDragStop}
onSelectionDragStop={props.onSelectionDragStop}
*/
onSelectionContextMenu={props.onSelectionContextMenu} onSelectionContextMenu={props.onSelectionContextMenu}
> >
<NodeRenderer <NodeRenderer
+33 -33
View File
@@ -1,7 +1,6 @@
import { getNodesInside } from '../../utils/graph'; import { getNodesInside } from '../../utils/graph';
import { Node, NodeTypesType, Edge } from '../../types'; import { Node, NodeTypesType, Edge, RevueFlowStore } from '../../types';
import { computed, defineComponent, PropType } from 'vue'; import { computed, defineComponent, inject, PropType } from 'vue';
import store from '../../store';
interface NodeRendererProps { interface NodeRendererProps {
nodeTypes: NodeTypesType; nodeTypes: NodeTypesType;
@@ -49,72 +48,73 @@ const NodeRenderer = defineComponent({
default: undefined default: undefined
}, },
onElementClick: { onElementClick: {
type: Function() as PropType<NodeRendererProps['onElementClick']>, type: Function as unknown as PropType<NodeRendererProps['onElementClick']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeDoubleClick: { onNodeDoubleClick: {
type: Function() as PropType<NodeRendererProps['onNodeDoubleClick']>, type: Function as unknown as PropType<NodeRendererProps['onNodeDoubleClick']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeMouseEnter: { onNodeMouseEnter: {
type: Function() as PropType<NodeRendererProps['onNodeMouseEnter']>, type: Function as unknown as PropType<NodeRendererProps['onNodeMouseEnter']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeMouseMove: { onNodeMouseMove: {
type: Function() as PropType<NodeRendererProps['onNodeMouseMove']>, type: Function as unknown as PropType<NodeRendererProps['onNodeMouseMove']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeMouseLeave: { onNodeMouseLeave: {
type: Function() as PropType<NodeRendererProps['onNodeMouseLeave']>, type: Function as unknown as PropType<NodeRendererProps['onNodeMouseLeave']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeContextMenu: { onNodeContextMenu: {
type: Function() as PropType<NodeRendererProps['onNodeContextMenu']>, type: Function as unknown as PropType<NodeRendererProps['onNodeContextMenu']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeDrag: { onNodeDrag: {
type: Function() as PropType<NodeRendererProps['onNodeDrag']>, type: Function as unknown as PropType<NodeRendererProps['onNodeDrag']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeDragStart: { onNodeDragStart: {
type: Function() as PropType<NodeRendererProps['onNodeDragStart']>, type: Function as unknown as PropType<NodeRendererProps['onNodeDragStart']>,
required: false, required: false,
default: undefined default: undefined
}, },
onNodeDragStop: { onNodeDragStop: {
type: Function() as PropType<NodeRendererProps['onNodeDragStop']>, type: Function as unknown as PropType<NodeRendererProps['onNodeDragStop']>,
required: false, required: false,
default: undefined default: undefined
} }
}, },
setup(props) { setup(props) {
const pinia = store(); const store = inject<RevueFlowStore>('store');
const visibleNodes = computed(() => { const visibleNodes = computed(() => {
return props.onlyRenderVisibleElements return props.onlyRenderVisibleElements
? getNodesInside( ? store?.nodes &&
pinia.nodes, getNodesInside(
{ store?.nodes,
x: 0, {
y: 0, x: 0,
width: pinia.width, y: 0,
height: pinia.height width: store?.width,
}, height: store?.height
pinia.transform, },
true store?.transform,
) true
: pinia.nodes; )
: store?.nodes;
}); });
const transformStyle = computed(() => ({ 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(() => { const resizeObserver = computed(() => {
@@ -128,13 +128,13 @@ const NodeRenderer = defineComponent({
nodeElement: entry.target as HTMLDivElement nodeElement: entry.target as HTMLDivElement
})); }));
pinia.updateNodeDimensions(updates); store?.updateNodeDimensions(updates);
}); });
}); });
return () => ( return () => (
<div class="revue-flow__nodes" style={transformStyle.value}> <div class="revue-flow__nodes" style={transformStyle.value}>
{visibleNodes.value.map((node) => { {visibleNodes.value?.map((node) => {
const nodeType = node.type || 'default'; const nodeType = node.type || 'default';
if (props.nodeTypes) { if (props.nodeTypes) {
const NodeComponent: any = props.nodeTypes[nodeType] || props.nodeTypes.default; 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".`); console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`);
} }
const isDraggable = !!(node.draggable || (pinia.nodesDraggable && typeof node.draggable === 'undefined')); const isDraggable = !!(node.draggable || (store?.nodesDraggable && typeof node.draggable === 'undefined'));
const isSelectable = !!(node.selectable || (pinia.elementsSelectable && typeof node.selectable === 'undefined')); const isSelectable = !!(node.selectable || (store?.elementsSelectable && typeof node.selectable === 'undefined'));
const isConnectable = !!(node.connectable || (pinia.nodesConnectable && typeof node.connectable === 'undefined')); const isConnectable = !!(node.connectable || (store?.nodesConnectable && typeof node.connectable === 'undefined'));
return ( return (
<NodeComponent <NodeComponent
@@ -173,8 +173,8 @@ const NodeRenderer = defineComponent({
onNodeDragStart={props.onNodeDragStart} onNodeDragStart={props.onNodeDragStart}
onNodeDrag={props.onNodeDrag} onNodeDrag={props.onNodeDrag}
onNodeDragStop={props.onNodeDragStop} onNodeDragStop={props.onNodeDragStop}
scale={pinia.transform[2]} scale={store?.transform[2]}
selected={pinia.selectedElements?.some(({ id }) => id === node.id) || false} selected={store?.selectedElements?.some(({ id }) => id === node.id) || false}
isDraggable={isDraggable} isDraggable={isDraggable}
isSelectable={isSelectable} isSelectable={isSelectable}
isConnectable={isConnectable} isConnectable={isConnectable}
+9 -21
View File
@@ -1,14 +1,4 @@
import { import { computed, CSSProperties, defineComponent, HTMLAttributes, onBeforeUnmount, onUpdated, PropType, provide } from 'vue';
computed,
CSSProperties,
defineComponent,
HTMLAttributes,
onBeforeUnmount,
onMounted,
onUpdated,
PropType,
watch
} from 'vue';
import GraphView from '../GraphView'; import GraphView from '../GraphView';
import DefaultNode from '../../components/Nodes/DefaultNode'; import DefaultNode from '../../components/Nodes/DefaultNode';
import InputNode from '../../components/Nodes/InputNode'; import InputNode from '../../components/Nodes/InputNode';
@@ -427,32 +417,32 @@ const RevueFlow = defineComponent({
default: undefined as any default: undefined as any
}, },
onEdgeMouseEnter: { onEdgeMouseEnter: {
type: Function() as PropType<RevueFlowProps['onEdgeMouseEnter']>, type: Function as unknown as PropType<RevueFlowProps['onEdgeMouseEnter']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeContextMenu: { onEdgeContextMenu: {
type: Function() as PropType<RevueFlowProps['onEdgeContextMenu']>, type: Function as unknown as PropType<RevueFlowProps['onEdgeContextMenu']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeMouseMove: { onEdgeMouseMove: {
type: Function() as PropType<RevueFlowProps['onEdgeMouseMove']>, type: Function as unknown as PropType<RevueFlowProps['onEdgeMouseMove']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeMouseLeave: { onEdgeMouseLeave: {
type: Function() as PropType<RevueFlowProps['onEdgeMouseLeave']>, type: Function as unknown as PropType<RevueFlowProps['onEdgeMouseLeave']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeUpdateEnd: { onEdgeUpdateEnd: {
type: Function() as PropType<RevueFlowProps['onEdgeUpdateEnd']>, type: Function as unknown as PropType<RevueFlowProps['onEdgeUpdateEnd']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
onEdgeUpdateStart: { onEdgeUpdateStart: {
type: Function() as PropType<RevueFlowProps['onEdgeUpdateStart']>, type: Function as unknown as PropType<RevueFlowProps['onEdgeUpdateStart']>,
required: false, required: false,
default: undefined as any default: undefined as any
}, },
@@ -464,13 +454,11 @@ const RevueFlow = defineComponent({
}, },
setup(props, { slots }) { setup(props, { slots }) {
const store = configureStore(initialState)(); const store = configureStore(initialState)();
watch(props.elements, () => store.setElements(props.elements)); provide('store', store);
store.setElements(props.elements);
onUpdated(() => { onUpdated(() => {
store.setElements(props.elements); store.setElements(props.elements);
}); });
onMounted(() => {
store.setElements(props.elements);
});
onBeforeUnmount(() => { onBeforeUnmount(() => {
store.$reset(); store.$reset();
store.setElements([]); store.setElements([]);
+4 -5
View File
@@ -1,7 +1,6 @@
import { setActivePinia, createPinia, defineStore, StoreDefinition, Pinia } from 'pinia'; import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia';
import { inject } from 'vue';
import isEqual from 'fast-deep-equal'; 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 { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph';
import { clampPosition, getDimensions } from '../utils'; import { clampPosition, getDimensions } from '../utils';
import { getHandleBounds } from '../components/Nodes/utils'; import { getHandleBounds } from '../components/Nodes/utils';
@@ -10,11 +9,11 @@ type NextElements = {
nextNodes: Node[]; nextNodes: Node[];
nextEdges: Edge[]; nextEdges: Edge[];
}; };
const pinia = createPinia();
export default function configureStore( export default function configureStore(
preloadedState: RevueFlowState preloadedState: RevueFlowState
): StoreDefinition<any, RevueFlowState, any, RevueFlowActionsTree> { ): StoreDefinition<string, RevueFlowState, any, RevueFlowActions> {
const pinia = inject<Pinia>(Symbol('pinia')) ?? createPinia();
setActivePinia(pinia); setActivePinia(pinia);
return defineStore({ return defineStore({
-1
View File
@@ -58,5 +58,4 @@ export const initialState: RevueFlowState = {
const store = configureStore(initialState); const store = configureStore(initialState);
export type StoreType = typeof store;
export default store; export default store;
+4 -1
View File
@@ -1,5 +1,6 @@
import { Selection as D3Selection, ZoomBehavior } from 'd3'; import { Selection as D3Selection, ZoomBehavior } from 'd3';
import { Component, HTMLAttributes, PropType, VNode } from 'vue'; import { Component, HTMLAttributes, PropType, VNode } from 'vue';
import { Store } from 'pinia';
export type ElementId = string; export type ElementId = string;
@@ -869,7 +870,7 @@ export interface RevueFlowState {
onConnectEnd?: OnConnectEndFunc; onConnectEnd?: OnConnectEndFunc;
} }
export type RevueFlowActionsTree = { export type RevueFlowActions = {
setOnConnect: (onConnect: OnConnectFunc) => void; setOnConnect: (onConnect: OnConnectFunc) => void;
setOnConnectStart: (onConnectStart: OnConnectStartFunc) => void; setOnConnectStart: (onConnectStart: OnConnectStartFunc) => void;
setOnConnectStop: (onConnectStop: OnConnectStopFunc) => void; setOnConnectStop: (onConnectStop: OnConnectStopFunc) => void;
@@ -903,4 +904,6 @@ export type RevueFlowActionsTree = {
setConnectionMode: (connectionMode: ConnectionMode) => void; setConnectionMode: (connectionMode: ConnectionMode) => void;
}; };
export type RevueFlowStore = Store<string, RevueFlowState, any, RevueFlowActions>;
export type UpdateNodeInternals = (nodeId: ElementId) => void; export type UpdateNodeInternals = (nodeId: ElementId) => void;
+5 -7
View File
@@ -1,5 +1,3 @@
import { Store } from 'pinia';
import { clampPosition, clamp } from './index'; import { clampPosition, clamp } from './index';
import { import {
@@ -13,8 +11,8 @@ import {
Box, Box,
Connection, Connection,
FlowExportObject, FlowExportObject,
RevueFlowState, NodeExtent,
NodeExtent RevueFlowStore
} from '../types'; } from '../types';
export const isEdge = (element: Node | Connection | Edge): element is Edge => export const isEdge = (element: Node | Connection | Edge): element is Edge =>
@@ -138,7 +136,7 @@ export const pointToRendererPoint = (
return position; return position;
}; };
export const onLoadProject = (currentStore: Store<'revue-flow', RevueFlowState>) => { export const onLoadProject = (currentStore: RevueFlowStore) => {
return (position: XYPosition): XYPosition => { return (position: XYPosition): XYPosition => {
return pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid); 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 (): Elements => {
return parseElements(currentStore.nodes || [], currentStore.edges || []); return parseElements(currentStore.nodes || [], currentStore.edges || []);
}; };
}; };
export const onLoadToObject = (currentStore: Store<'revue-flow', RevueFlowState>) => { export const onLoadToObject = (currentStore: RevueFlowStore) => {
return (): FlowExportObject => { return (): FlowExportObject => {
return { return {
elements: parseElements(currentStore.nodes || [], currentStore.edges || []), elements: parseElements(currentStore.nodes || [], currentStore.edges || []),
+12
View File
@@ -5195,6 +5195,11 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0" astral-regex "^2.0.0"
is-fullwidth-code-point "^3.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: source-map-js@^0.6.2:
version "0.6.2" version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" 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/runtime-dom" "3.1.4"
"@vue/shared" "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: which-boxed-primitive@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"