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

View File

@@ -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",

View File

@@ -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<RevueFlowStore>('store');
provide('NodeIdContext', props.id);
const nodeElement = ref<HTMLDivElement | null>(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 () => (
<div
class={nodeClasses}
class={nodeClasses.value}
ref={nodeElement}
style={nodeStyle.value}
onMouseenter={onMouseEnterHandler}

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;

View File

@@ -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<RevueFlowStore>('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 () => (
<svg width={pinia.width} height={pinia.height} class="revue-flow__edges">
<MarkerDefinitions color={props.arrowHeadColor || ''} />
<svg width={store?.width} height={store?.height} class="revue-flow__edges">
<MarkerDefinitions color={props.arrowHeadColor as string} />
<g transform={transformStyle.value}>
{pinia.edges.map((edge: Edge) => (
{store?.edges.map((edge: Edge) => (
<EdgeCmp
key={edge.id}
edge={edge}
props={props as any}
nodes={pinia.nodes}
selectedElements={pinia.selectedElements}
elementsSelectable={pinia.elementsSelectable}
transform={pinia.transform}
width={pinia.width}
height={pinia.height}
nodes={store?.nodes}
selectedElements={store?.selectedElements}
elementsSelectable={store?.elementsSelectable}
transform={store?.transform}
width={store?.width}
height={store?.height}
onlyRenderVisibleElements={props.onlyRenderVisibleElements}
/>
))}
{renderConnectionLine.value && (
<ConnectionLine
nodes={pinia.nodes}
connectionNodeId={pinia.connectionNodeId!}
connectionHandleId={pinia.connectionHandleId}
connectionHandleType={pinia.connectionHandleType!}
connectionPositionX={pinia.connectionPosition.x}
connectionPositionY={pinia.connectionPosition.y}
transform={pinia.transform}
nodes={store?.nodes}
connectionNodeId={store?.connectionNodeId as string}
connectionHandleId={store?.connectionHandleId as string}
connectionHandleType={store?.connectionHandleType as HandleType}
connectionPositionX={store?.connectionPosition.x}
connectionPositionY={store?.connectionPosition.y}
transform={store?.transform}
connectionLineStyle={props.connectionLineStyle}
connectionLineType={props.connectionLineType}
isConnectable={pinia.nodesConnectable}
isConnectable={store?.nodesConnectable}
CustomConnectionLineComponent={props.connectionLineComponent}
/>
)}

View File

@@ -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<FlowRendererProps['onPaneClick']>,
@@ -153,12 +156,20 @@ const FlowRenderer = defineComponent({
}
},
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) => {
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() : ''}
<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} />
</ZoomPane>
);

View File

@@ -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<RevueFlowProps, 'onSelectionChange' | 'elements'> {
@@ -39,102 +38,102 @@ const GraphView = defineComponent({
default: undefined
},
onMove: {
type: Function() as PropType<GraphViewProps['onMove']>,
type: Function as unknown as PropType<GraphViewProps['onMove']>,
required: false,
default: undefined as any
},
onMoveStart: {
type: Function() as PropType<GraphViewProps['onMoveStart']>,
type: Function as unknown as PropType<GraphViewProps['onMoveStart']>,
required: false,
default: undefined as any
},
onMoveEnd: {
type: Function() as PropType<GraphViewProps['onMoveEnd']>,
type: Function as unknown as PropType<GraphViewProps['onMoveEnd']>,
required: false,
default: undefined as any
},
onLoad: {
type: Function() as PropType<GraphViewProps['onLoad']>,
type: Function as unknown as PropType<GraphViewProps['onLoad']>,
required: false,
default: undefined as any
},
onElementClick: {
type: Function() as PropType<GraphViewProps['onElementClick']>,
type: Function as unknown as PropType<GraphViewProps['onElementClick']>,
required: false,
default: undefined as any
},
onNodeDoubleClick: {
type: Function() as PropType<GraphViewProps['onNodeDoubleClick']>,
type: Function as unknown as PropType<GraphViewProps['onNodeDoubleClick']>,
required: false,
default: undefined as any
},
onEdgeDoubleClick: {
type: Function() as PropType<GraphViewProps['onEdgeDoubleClick']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeDoubleClick']>,
required: false,
default: undefined as any
},
onNodeMouseEnter: {
type: Function() as PropType<GraphViewProps['onNodeMouseEnter']>,
type: Function as unknown as PropType<GraphViewProps['onNodeMouseEnter']>,
required: false,
default: undefined as any
},
onNodeMouseMove: {
type: Function() as PropType<GraphViewProps['onNodeMouseMove']>,
type: Function as unknown as PropType<GraphViewProps['onNodeMouseMove']>,
required: false,
default: undefined as any
},
onNodeMouseLeave: {
type: Function() as PropType<GraphViewProps['onNodeMouseLeave']>,
type: Function as unknown as PropType<GraphViewProps['onNodeMouseLeave']>,
required: false,
default: undefined as any
},
onNodeContextMenu: {
type: Function() as PropType<GraphViewProps['onNodeContextMenu']>,
type: Function as unknown as PropType<GraphViewProps['onNodeContextMenu']>,
required: false,
default: undefined as any
},
onNodeDragStart: {
type: Function() as PropType<GraphViewProps['onNodeDragStart']>,
type: Function as unknown as PropType<GraphViewProps['onNodeDragStart']>,
required: false,
default: undefined as any
},
onNodeDrag: {
type: Function() as PropType<GraphViewProps['onNodeDrag']>,
type: Function as unknown as PropType<GraphViewProps['onNodeDrag']>,
required: false,
default: undefined as any
},
onNodeDragStop: {
type: Function() as PropType<GraphViewProps['onNodeDragStop']>,
type: Function as unknown as PropType<GraphViewProps['onNodeDragStop']>,
required: false,
default: undefined as any
},
onSelectionContextMenu: {
type: Function() as PropType<GraphViewProps['onSelectionContextMenu']>,
type: Function as unknown as PropType<GraphViewProps['onSelectionContextMenu']>,
required: false,
default: undefined as any
},
onElementsRemove: {
type: Function() as PropType<GraphViewProps['onElementsRemove']>,
type: Function as unknown as PropType<GraphViewProps['onElementsRemove']>,
required: false,
default: undefined as any
},
onConnect: {
type: Function() as PropType<GraphViewProps['onConnect']>,
type: Function as unknown as PropType<GraphViewProps['onConnect']>,
required: false,
default: undefined as any
},
onConnectStart: {
type: Function() as PropType<GraphViewProps['onConnectStart']>,
type: Function as unknown as PropType<GraphViewProps['onConnectStart']>,
required: false,
default: undefined as any
},
onConnectStop: {
type: Function() as PropType<GraphViewProps['onConnectStop']>,
type: Function as unknown as PropType<GraphViewProps['onConnectStop']>,
required: false,
default: undefined as any
},
onConnectEnd: {
type: Function() as PropType<GraphViewProps['onConnectEnd']>,
type: Function as unknown as PropType<GraphViewProps['onConnectEnd']>,
required: false,
default: undefined as any
},
@@ -289,52 +288,52 @@ const GraphView = defineComponent({
default: undefined
},
onPaneClick: {
type: Function() as PropType<GraphViewProps['onPaneClick']>,
type: Function as unknown as PropType<GraphViewProps['onPaneClick']>,
required: false,
default: undefined as any
},
onPaneScroll: {
type: Function() as PropType<GraphViewProps['onPaneScroll']>,
type: Function as unknown as PropType<GraphViewProps['onPaneScroll']>,
required: false,
default: undefined as any
},
onPaneContextMenu: {
type: Function() as PropType<GraphViewProps['onPaneContextMenu']>,
type: Function as unknown as PropType<GraphViewProps['onPaneContextMenu']>,
required: false,
default: undefined as any
},
onEdgeUpdate: {
type: Function() as PropType<GraphViewProps['onEdgeUpdate']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeUpdate']>,
required: false,
default: undefined as any
},
onEdgeMouseEnter: {
type: Function() as PropType<GraphViewProps['onEdgeMouseEnter']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeMouseEnter']>,
required: false,
default: undefined as any
},
onEdgeContextMenu: {
type: Function() as PropType<GraphViewProps['onEdgeContextMenu']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeContextMenu']>,
required: false,
default: undefined as any
},
onEdgeMouseMove: {
type: Function() as PropType<GraphViewProps['onEdgeMouseMove']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeMouseMove']>,
required: false,
default: undefined as any
},
onEdgeMouseLeave: {
type: Function() as PropType<GraphViewProps['onEdgeMouseLeave']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeMouseLeave']>,
required: false,
default: undefined as any
},
onEdgeUpdateEnd: {
type: Function() as PropType<GraphViewProps['onEdgeUpdateEnd']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeUpdateEnd']>,
required: false,
default: undefined as any
},
onEdgeUpdateStart: {
type: Function() as PropType<GraphViewProps['onEdgeUpdateStart']>,
type: Function as unknown as PropType<GraphViewProps['onEdgeUpdateStart']>,
required: false,
default: undefined as any
},
@@ -345,23 +344,23 @@ const GraphView = defineComponent({
}
},
setup(props) {
const pinia = store();
const store = inject<RevueFlowStore>('store');
const isInitialized = ref<boolean>(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}
>
<NodeRenderer

View File

@@ -1,7 +1,6 @@
import { getNodesInside } from '../../utils/graph';
import { Node, NodeTypesType, Edge } from '../../types';
import { computed, defineComponent, PropType } from 'vue';
import store from '../../store';
import { Node, NodeTypesType, Edge, RevueFlowStore } from '../../types';
import { computed, defineComponent, inject, PropType } from 'vue';
interface NodeRendererProps {
nodeTypes: NodeTypesType;
@@ -49,72 +48,73 @@ const NodeRenderer = defineComponent({
default: undefined
},
onElementClick: {
type: Function() as PropType<NodeRendererProps['onElementClick']>,
type: Function as unknown as PropType<NodeRendererProps['onElementClick']>,
required: false,
default: undefined
},
onNodeDoubleClick: {
type: Function() as PropType<NodeRendererProps['onNodeDoubleClick']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeDoubleClick']>,
required: false,
default: undefined
},
onNodeMouseEnter: {
type: Function() as PropType<NodeRendererProps['onNodeMouseEnter']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeMouseEnter']>,
required: false,
default: undefined
},
onNodeMouseMove: {
type: Function() as PropType<NodeRendererProps['onNodeMouseMove']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeMouseMove']>,
required: false,
default: undefined
},
onNodeMouseLeave: {
type: Function() as PropType<NodeRendererProps['onNodeMouseLeave']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeMouseLeave']>,
required: false,
default: undefined
},
onNodeContextMenu: {
type: Function() as PropType<NodeRendererProps['onNodeContextMenu']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeContextMenu']>,
required: false,
default: undefined
},
onNodeDrag: {
type: Function() as PropType<NodeRendererProps['onNodeDrag']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeDrag']>,
required: false,
default: undefined
},
onNodeDragStart: {
type: Function() as PropType<NodeRendererProps['onNodeDragStart']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeDragStart']>,
required: false,
default: undefined
},
onNodeDragStop: {
type: Function() as PropType<NodeRendererProps['onNodeDragStop']>,
type: Function as unknown as PropType<NodeRendererProps['onNodeDragStop']>,
required: false,
default: undefined
}
},
setup(props) {
const pinia = store();
const store = inject<RevueFlowStore>('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 () => (
<div class="revue-flow__nodes" style={transformStyle.value}>
{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 (
<NodeComponent
@@ -173,8 +173,8 @@ const NodeRenderer = defineComponent({
onNodeDragStart={props.onNodeDragStart}
onNodeDrag={props.onNodeDrag}
onNodeDragStop={props.onNodeDragStop}
scale={pinia.transform[2]}
selected={pinia.selectedElements?.some(({ id }) => id === node.id) || false}
scale={store?.transform[2]}
selected={store?.selectedElements?.some(({ id }) => id === node.id) || false}
isDraggable={isDraggable}
isSelectable={isSelectable}
isConnectable={isConnectable}

View File

@@ -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<RevueFlowProps['onEdgeMouseEnter']>,
type: Function as unknown as PropType<RevueFlowProps['onEdgeMouseEnter']>,
required: false,
default: undefined as any
},
onEdgeContextMenu: {
type: Function() as PropType<RevueFlowProps['onEdgeContextMenu']>,
type: Function as unknown as PropType<RevueFlowProps['onEdgeContextMenu']>,
required: false,
default: undefined as any
},
onEdgeMouseMove: {
type: Function() as PropType<RevueFlowProps['onEdgeMouseMove']>,
type: Function as unknown as PropType<RevueFlowProps['onEdgeMouseMove']>,
required: false,
default: undefined as any
},
onEdgeMouseLeave: {
type: Function() as PropType<RevueFlowProps['onEdgeMouseLeave']>,
type: Function as unknown as PropType<RevueFlowProps['onEdgeMouseLeave']>,
required: false,
default: undefined as any
},
onEdgeUpdateEnd: {
type: Function() as PropType<RevueFlowProps['onEdgeUpdateEnd']>,
type: Function as unknown as PropType<RevueFlowProps['onEdgeUpdateEnd']>,
required: false,
default: undefined as any
},
onEdgeUpdateStart: {
type: Function() as PropType<RevueFlowProps['onEdgeUpdateStart']>,
type: Function as unknown as PropType<RevueFlowProps['onEdgeUpdateStart']>,
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([]);

View File

@@ -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<any, RevueFlowState, any, RevueFlowActionsTree> {
const pinia = inject<Pinia>(Symbol('pinia')) ?? createPinia();
): StoreDefinition<string, RevueFlowState, any, RevueFlowActions> {
setActivePinia(pinia);
return defineStore({

View File

@@ -58,5 +58,4 @@ export const initialState: RevueFlowState = {
const store = configureStore(initialState);
export type StoreType = typeof store;
export default store;

View File

@@ -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<string, RevueFlowState, any, RevueFlowActions>;
export type UpdateNodeInternals = (nodeId: ElementId) => void;

View File

@@ -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 || []),

View File

@@ -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"