refactor(store): use redux instead of easy-peasy
This commit is contained in:
Generated
+9496
-11
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -35,7 +35,10 @@
|
||||
"d3-zoom": "^2.0.0",
|
||||
"easy-peasy": "^4.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"react-draggable": "^4.4.3"
|
||||
"react-draggable": "^4.4.3",
|
||||
"react-redux": "^7.2.2",
|
||||
"redux": "^4.0.5",
|
||||
"redux-thunk": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
@@ -52,6 +55,8 @@
|
||||
"@types/d3": "^6.2.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@types/react-redux": "^7.1.15",
|
||||
"@types/redux": "^3.6.0",
|
||||
"@types/resize-observer-browser": "^0.1.5",
|
||||
"autoprefixer": "^10.2.1",
|
||||
"babel-preset-react-app": "^10.0.0",
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { memo } from 'react';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { useStoreState } from '../../store/hooks';
|
||||
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph';
|
||||
import { getRectOfNodes, getBoundsofRects, isNode } from '../../utils/graph';
|
||||
import { Node, Rect } from '../../types';
|
||||
import MiniMapNode from './MiniMapNode';
|
||||
|
||||
@@ -33,7 +33,8 @@ const MiniMap = ({
|
||||
const containerWidth = useStoreState((s) => s.width);
|
||||
const containerHeight = useStoreState((s) => s.height);
|
||||
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
||||
const nodes = useStoreState((s) => s.nodes);
|
||||
const elements = useStoreState((s) => s.elements);
|
||||
const nodes = elements.filter(isNode);
|
||||
|
||||
const mapClasses = cc(['react-flow__minimap', className]);
|
||||
const elementWidth = (style?.width || defaultWidth)! as number;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import React, { useMemo, FC } from 'react';
|
||||
import { StoreProvider, createStore } from 'easy-peasy';
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { storeModel } from '../../store';
|
||||
import { initialState } from '../../store';
|
||||
import configureStore from '../../store/configure-store';
|
||||
|
||||
const ReactFlowProvider: FC = ({ children }) => {
|
||||
const store = useMemo(() => {
|
||||
return createStore(storeModel);
|
||||
return configureStore(initialState);
|
||||
}, []);
|
||||
|
||||
return <StoreProvider store={store}>{children}</StoreProvider>;
|
||||
return <Provider store={store}>{children}</Provider>;
|
||||
};
|
||||
|
||||
ReactFlowProvider.displayName = 'ReactFlowProvider';
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
type ValidConnectionFunc = (connection: Connection) => boolean;
|
||||
export type SetSourceIdFunc = (params: SetConnectionId) => void;
|
||||
|
||||
export type SetPosition = (pos: XYPosition) => void;
|
||||
|
||||
type Result = {
|
||||
elementBelow: Element | null;
|
||||
isValid: boolean;
|
||||
@@ -86,7 +88,7 @@ export function onMouseDown(
|
||||
handleId: ElementId | null,
|
||||
nodeId: ElementId,
|
||||
setConnectionNodeId: SetSourceIdFunc,
|
||||
setPosition: (pos: XYPosition) => void,
|
||||
setPosition: SetPosition,
|
||||
onConnect: OnConnectFunc,
|
||||
isTarget: boolean,
|
||||
isValidConnection: ValidConnectionFunc,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useStoreActions, useStoreState } from '../../store/hooks';
|
||||
import NodeIdContext from '../../contexts/NodeIdContext';
|
||||
import { HandleProps, Connection, ElementId, Position } from '../../types';
|
||||
|
||||
import { onMouseDown } from './handler';
|
||||
import { onMouseDown, SetSourceIdFunc, SetPosition } from './handler';
|
||||
|
||||
const alwaysValid = () => true;
|
||||
|
||||
@@ -45,8 +45,8 @@ const Handle: FunctionComponent<HandleProps & Omit<HTMLAttributes<HTMLDivElement
|
||||
event,
|
||||
handleId,
|
||||
nodeId,
|
||||
setConnectionNodeId,
|
||||
setPosition,
|
||||
(setConnectionNodeId as unknown) as SetSourceIdFunc,
|
||||
(setPosition as unknown) as SetPosition,
|
||||
onConnectExtended,
|
||||
isTarget,
|
||||
isValidConnection,
|
||||
|
||||
@@ -29,7 +29,8 @@ export default ({
|
||||
const selectedElements = useStoreState((state) => state.selectedElements);
|
||||
const snapToGrid = useStoreState((state) => state.snapToGrid);
|
||||
const snapGrid = useStoreState((state) => state.snapGrid);
|
||||
const nodes = useStoreState((state) => state.nodes);
|
||||
const elements = useStoreState((state) => state.elements);
|
||||
const nodes = elements.filter(isNode);
|
||||
|
||||
const updateNodePosDiff = useStoreActions((actions) => actions.updateNodePosDiff);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { memo, CSSProperties, useCallback } from 'react';
|
||||
|
||||
import { useStoreState } from '../../store/hooks';
|
||||
import ConnectionLine from '../../components/ConnectionLine/index';
|
||||
import { isEdge } from '../../utils/graph';
|
||||
import { isEdge, isNode } from '../../utils/graph';
|
||||
import MarkerDefinitions from './MarkerDefinitions';
|
||||
import { getEdgePositions, getHandle, isEdgeVisible, getSourceTargetNodes } from './utils';
|
||||
import {
|
||||
@@ -169,7 +169,7 @@ const Edge = ({
|
||||
|
||||
const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||
const transform = useStoreState((state) => state.transform);
|
||||
const edges = useStoreState((state) => state.edges);
|
||||
const elements = useStoreState((state) => state.elements);
|
||||
const connectionNodeId = useStoreState((state) => state.connectionNodeId);
|
||||
const connectionHandleId = useStoreState((state) => state.connectionHandleId);
|
||||
const connectionHandleType = useStoreState((state) => state.connectionHandleType);
|
||||
@@ -179,7 +179,9 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||
const elementsSelectable = useStoreState((state) => state.elementsSelectable);
|
||||
const width = useStoreState((state) => state.width);
|
||||
const height = useStoreState((state) => state.height);
|
||||
const nodes = useStoreState((state) => state.nodes);
|
||||
|
||||
const edges = elements.filter(isEdge);
|
||||
const nodes = elements.filter(isNode);
|
||||
|
||||
if (!width) {
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { memo, useMemo, ComponentType, MouseEvent } from 'react';
|
||||
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
import { getNodesInside, isNode } from '../../utils/graph';
|
||||
import { useStoreState, useStoreActions } from '../../store/hooks';
|
||||
import { Node, NodeTypesType, WrapNodeProps, Edge } from '../../types';
|
||||
|
||||
@@ -25,10 +25,13 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
const nodesDraggable = useStoreState((state) => state.nodesDraggable);
|
||||
const nodesConnectable = useStoreState((state) => state.nodesConnectable);
|
||||
const elementsSelectable = useStoreState((state) => state.elementsSelectable);
|
||||
const viewportBox = useStoreState((state) => state.viewportBox);
|
||||
const nodes = useStoreState((state) => state.nodes);
|
||||
const width = useStoreState((state) => state.width);
|
||||
const height = useStoreState((state) => state.height);
|
||||
const elements = useStoreState((state) => state.elements);
|
||||
const batchUpdateNodeDimensions = useStoreActions((actions) => actions.batchUpdateNodeDimensions);
|
||||
|
||||
const viewportBox = { x: 0, y: 0, width, height };
|
||||
const nodes = elements.filter(isNode);
|
||||
const visibleNodes = props.onlyRenderVisibleElements ? getNodesInside(nodes, viewportBox, transform, true) : nodes;
|
||||
|
||||
const transformStyle = useMemo(
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import React, { FC } from 'react';
|
||||
import { StoreProvider } from 'easy-peasy';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import store from '../../store';
|
||||
import { useStore } from '../../store/hooks';
|
||||
// import { useStore } from '../../store/hooks';
|
||||
|
||||
const Wrapper: FC = ({ children }) => {
|
||||
const easyPeasyStore = useStore();
|
||||
const isWrapepdWithReactFlowProvider = easyPeasyStore?.getState()?.reactFlowVersion;
|
||||
// const reactFlowStore = useStore();
|
||||
// const isWrapepdWithReactFlowProvider = reactFlowStore?.getState()?.reactFlowVersion;
|
||||
|
||||
if (isWrapepdWithReactFlowProvider) {
|
||||
// we need to wrap it with a fragment because t's not allowed for children to be a ReactNode
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
||||
return <>{children}</>;
|
||||
}
|
||||
// console.log(isWrapepdWithReactFlowProvider);
|
||||
|
||||
return <StoreProvider store={store}>{children}</StoreProvider>;
|
||||
// if (isWrapepdWithReactFlowProvider) {
|
||||
// // we need to wrap it with a fragment because t's not allowed for children to be a ReactNode
|
||||
// // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
||||
// return <>{children}</>;
|
||||
// }
|
||||
|
||||
return <Provider store={store}>{children}</Provider>;
|
||||
};
|
||||
|
||||
Wrapper.displayName = 'ReactFlowWrapper';
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
import {
|
||||
OnConnectFunc,
|
||||
OnConnectStartFunc,
|
||||
OnConnectStopFunc,
|
||||
OnConnectEndFunc,
|
||||
Dimensions,
|
||||
Elements,
|
||||
NodePosUpdate,
|
||||
NodeDiffUpdate,
|
||||
Transform,
|
||||
TranslateExtent,
|
||||
XYPosition,
|
||||
SnapGrid,
|
||||
ConnectionMode,
|
||||
SetConnectionId,
|
||||
NodeDimensionUpdate,
|
||||
InitD3ZoomPayload,
|
||||
} from '../types';
|
||||
|
||||
export const SET_ON_CONNECT = 'SET_ON_CONNECT';
|
||||
export const SET_ON_CONNECT_START = 'SET_ON_CONNECT_START';
|
||||
export const SET_ON_CONNECT_STOP = 'SET_ON_CONNECT_STOP';
|
||||
export const SET_ON_CONNECT_END = 'SET_ON_CONNECT_END';
|
||||
export const SET_ELEMENTS = 'SET_ELEMENTS';
|
||||
export const BATCH_UPDATE_NODE_DIMENSIONS = 'BATCH_UPDATE_NODE_DIMENSIONS';
|
||||
export const UPDATE_NODE_DIMENSIONS = 'UPDATE_NODE_DIMENSIONS';
|
||||
export const UPDATE_NODE_POS = 'UPDATE_NODE_POS';
|
||||
export const UPDATE_NODE_POS_DIFF = 'UPDATE_NODE_POS_DIFF';
|
||||
export const SET_USER_SELECTION = 'SET_USER_SELECTION';
|
||||
export const UPDATE_USER_SELECTION = 'UPDATE_USER_SELECTION';
|
||||
export const UNSET_USER_SELECTION = 'UNSET_USER_SELECTION';
|
||||
export const SET_SELECTION = 'SET_SELECTION';
|
||||
export const UNSET_NODES_SELECTION = 'UNSET_NODES_SELECTION';
|
||||
export const SET_SELECTED_ELEMENTS = 'SET_SELECTED_ELEMENTS';
|
||||
export const RESET_SELECTED_ELEMENTS = 'RESET_SELECTED_ELEMENTS';
|
||||
export const ADD_SELECTED_ELEMENTS = 'ADD_SELECTED_ELEMENTS';
|
||||
export const UPDATE_TRANSFORM = 'UPDATE_TRANSFORM';
|
||||
export const UPDATE_SIZE = 'UPDATE_SIZE';
|
||||
export const INIT_D3ZOOM = 'INIT_D3ZOOM';
|
||||
export const SET_MINZOOM = 'SET_MINZOOM';
|
||||
export const SET_MAXZOOM = 'SET_MAXZOOM';
|
||||
export const SET_TRANSLATEEXTENT = 'SET_TRANSLATEEXTENT';
|
||||
export const SET_CONNECTION_POSITION = 'SET_CONNECTION_POSITION';
|
||||
export const SET_CONNECTION_NODEID = 'SET_CONNECTION_NODEID';
|
||||
export const SET_SNAPTOGRID = 'SET_SNAPTOGRID';
|
||||
export const SET_SNAPGRID = 'SET_SNAPGRID';
|
||||
export const SET_INTERACTIVE = 'SET_INTERACTIVE';
|
||||
export const SET_NODES_DRAGGABLE = 'SET_NODES_DRAGGABLE';
|
||||
export const SET_NODES_CONNECTABLE = 'SET_NODES_CONNECTABLE';
|
||||
export const SET_ELEMENTS_SELECTABLE = 'SET_ELEMENTS_SELECTABLE';
|
||||
export const SET_MULTI_SELECTION_ACTIVE = 'SET_MULTI_SELECTION_ACTIVE';
|
||||
export const SET_CONNECTION_MODE = 'SET_CONNECTION_MODE';
|
||||
|
||||
interface OnConnectAction {
|
||||
type: typeof SET_ON_CONNECT;
|
||||
payload: {
|
||||
onConnect: OnConnectFunc;
|
||||
};
|
||||
}
|
||||
|
||||
interface OnConnectStartAction {
|
||||
type: typeof SET_ON_CONNECT_START;
|
||||
payload: {
|
||||
onConnectStart: OnConnectStartFunc;
|
||||
};
|
||||
}
|
||||
|
||||
interface OnConnectStopAction {
|
||||
type: typeof SET_ON_CONNECT_STOP;
|
||||
payload: {
|
||||
onConnectStop: OnConnectStopFunc;
|
||||
};
|
||||
}
|
||||
|
||||
interface OnConnectEndAction {
|
||||
type: typeof SET_ON_CONNECT_END;
|
||||
payload: {
|
||||
onConnectEnd: OnConnectEndFunc;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetElementsAction {
|
||||
type: typeof SET_ELEMENTS;
|
||||
elements: Elements;
|
||||
}
|
||||
|
||||
interface BatchUpdateNodeDimensions {
|
||||
type: typeof BATCH_UPDATE_NODE_DIMENSIONS;
|
||||
updates: NodeDimensionUpdate[];
|
||||
}
|
||||
|
||||
interface UpdateNodeDimensions {
|
||||
type: typeof UPDATE_NODE_DIMENSIONS;
|
||||
payload: NodeDimensionUpdate;
|
||||
}
|
||||
|
||||
interface UpdateNodePos {
|
||||
type: typeof UPDATE_NODE_POS;
|
||||
payload: NodePosUpdate;
|
||||
}
|
||||
|
||||
interface UpdateNodePosDiff {
|
||||
type: typeof UPDATE_NODE_POS_DIFF;
|
||||
payload: NodeDiffUpdate;
|
||||
}
|
||||
|
||||
interface SetUserSelection {
|
||||
type: typeof SET_USER_SELECTION;
|
||||
mousePos: XYPosition;
|
||||
}
|
||||
|
||||
interface UpdateUserSelection {
|
||||
type: typeof UPDATE_USER_SELECTION;
|
||||
mousePos: XYPosition;
|
||||
}
|
||||
|
||||
interface UnsetUserSelection {
|
||||
type: typeof UNSET_USER_SELECTION;
|
||||
}
|
||||
|
||||
interface SetSelection {
|
||||
type: typeof SET_SELECTION;
|
||||
payload: {
|
||||
selectionActive: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface UnsetNodesSelection {
|
||||
type: typeof UNSET_NODES_SELECTION;
|
||||
payload: {
|
||||
nodesSelectionActive: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface ResetSelectedElements {
|
||||
type: typeof RESET_SELECTED_ELEMENTS;
|
||||
payload: {
|
||||
selectedElements: Elements | null;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetSelectedElements {
|
||||
type: typeof SET_SELECTED_ELEMENTS;
|
||||
elements: Elements;
|
||||
}
|
||||
|
||||
interface AddSelectedElements {
|
||||
type: typeof ADD_SELECTED_ELEMENTS;
|
||||
elements: Elements;
|
||||
}
|
||||
|
||||
interface UpdateTransform {
|
||||
type: typeof UPDATE_TRANSFORM;
|
||||
payload: {
|
||||
transform: Transform;
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateSize {
|
||||
type: typeof UPDATE_SIZE;
|
||||
payload: {
|
||||
size: Dimensions;
|
||||
};
|
||||
}
|
||||
|
||||
interface InitD3Zoom {
|
||||
type: typeof INIT_D3ZOOM;
|
||||
payload: InitD3ZoomPayload;
|
||||
}
|
||||
|
||||
interface SetMinZoom {
|
||||
type: typeof SET_MINZOOM;
|
||||
payload: {
|
||||
minZoom: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetMaxZoom {
|
||||
type: typeof SET_MAXZOOM;
|
||||
payload: {
|
||||
maxZoom: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetTranslateExtent {
|
||||
type: typeof SET_TRANSLATEEXTENT;
|
||||
payload: {
|
||||
translateExtent: TranslateExtent;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetConnectionPosition {
|
||||
type: typeof SET_CONNECTION_POSITION;
|
||||
payload: {
|
||||
connectionPosition: XYPosition;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetConnectionNodeId {
|
||||
type: typeof SET_CONNECTION_NODEID;
|
||||
payload: SetConnectionId;
|
||||
}
|
||||
|
||||
interface SetSnapToGrid {
|
||||
type: typeof SET_SNAPTOGRID;
|
||||
payload: {
|
||||
snapToGrid: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetSnapGrid {
|
||||
type: typeof SET_SNAPGRID;
|
||||
payload: {
|
||||
snapGrid: SnapGrid;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetInteractive {
|
||||
type: typeof SET_INTERACTIVE;
|
||||
payload: {
|
||||
nodesDraggable: boolean;
|
||||
nodesConnectable: boolean;
|
||||
elementsSelectable: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetNodesDraggable {
|
||||
type: typeof SET_NODES_DRAGGABLE;
|
||||
payload: {
|
||||
nodesDraggable: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetNodesConnectable {
|
||||
type: typeof SET_NODES_CONNECTABLE;
|
||||
payload: {
|
||||
nodesConnectable: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetElementsSelectable {
|
||||
type: typeof SET_ELEMENTS_SELECTABLE;
|
||||
payload: {
|
||||
elementsSelectable: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetMultiSelectionActive {
|
||||
type: typeof SET_MULTI_SELECTION_ACTIVE;
|
||||
payload: {
|
||||
multiSelectionActive: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface SetConnectionMode {
|
||||
type: typeof SET_CONNECTION_MODE;
|
||||
payload: {
|
||||
connectionMode: ConnectionMode;
|
||||
};
|
||||
}
|
||||
|
||||
export type ActionTypes =
|
||||
| OnConnectAction
|
||||
| OnConnectStartAction
|
||||
| OnConnectStopAction
|
||||
| OnConnectEndAction
|
||||
| SetElementsAction
|
||||
| BatchUpdateNodeDimensions
|
||||
| UpdateNodeDimensions
|
||||
| UpdateNodePos
|
||||
| UpdateNodePosDiff
|
||||
| SetUserSelection
|
||||
| UpdateUserSelection
|
||||
| UnsetUserSelection
|
||||
| SetSelection
|
||||
| UnsetNodesSelection
|
||||
| ResetSelectedElements
|
||||
| SetSelectedElements
|
||||
| AddSelectedElements
|
||||
| UpdateTransform
|
||||
| UpdateSize
|
||||
| InitD3Zoom
|
||||
| SetMinZoom
|
||||
| SetMaxZoom
|
||||
| SetTranslateExtent
|
||||
| SetConnectionPosition
|
||||
| SetConnectionNodeId
|
||||
| SetSnapToGrid
|
||||
| SetSnapGrid
|
||||
| SetInteractive
|
||||
| SetNodesDraggable
|
||||
| SetNodesConnectable
|
||||
| SetElementsSelectable
|
||||
| SetMultiSelectionActive
|
||||
| SetConnectionMode;
|
||||
@@ -0,0 +1,243 @@
|
||||
import {
|
||||
Elements,
|
||||
OnConnectEndFunc,
|
||||
OnConnectFunc,
|
||||
OnConnectStartFunc,
|
||||
OnConnectStopFunc,
|
||||
NodeDimensionUpdate,
|
||||
NodePosUpdate,
|
||||
NodeDiffUpdate,
|
||||
XYPosition,
|
||||
Transform,
|
||||
Dimensions,
|
||||
InitD3ZoomPayload,
|
||||
TranslateExtent,
|
||||
SetConnectionId,
|
||||
SnapGrid,
|
||||
ConnectionMode,
|
||||
} from '../types';
|
||||
|
||||
import {
|
||||
ActionTypes,
|
||||
SET_ON_CONNECT,
|
||||
SET_ON_CONNECT_START,
|
||||
SET_ON_CONNECT_STOP,
|
||||
SET_ON_CONNECT_END,
|
||||
SET_ELEMENTS,
|
||||
BATCH_UPDATE_NODE_DIMENSIONS,
|
||||
UPDATE_NODE_DIMENSIONS,
|
||||
UPDATE_NODE_POS,
|
||||
UPDATE_NODE_POS_DIFF,
|
||||
SET_USER_SELECTION,
|
||||
UNSET_USER_SELECTION,
|
||||
SET_SELECTION,
|
||||
UNSET_NODES_SELECTION,
|
||||
RESET_SELECTED_ELEMENTS,
|
||||
SET_SELECTED_ELEMENTS,
|
||||
ADD_SELECTED_ELEMENTS,
|
||||
UPDATE_TRANSFORM,
|
||||
UPDATE_SIZE,
|
||||
INIT_D3ZOOM,
|
||||
SET_MINZOOM,
|
||||
SET_MAXZOOM,
|
||||
SET_TRANSLATEEXTENT,
|
||||
SET_CONNECTION_POSITION,
|
||||
SET_CONNECTION_NODEID,
|
||||
SET_SNAPTOGRID,
|
||||
SET_SNAPGRID,
|
||||
SET_INTERACTIVE,
|
||||
SET_NODES_DRAGGABLE,
|
||||
SET_NODES_CONNECTABLE,
|
||||
SET_ELEMENTS_SELECTABLE,
|
||||
SET_MULTI_SELECTION_ACTIVE,
|
||||
SET_CONNECTION_MODE,
|
||||
} from './action-types';
|
||||
|
||||
export const setOnConnect = (onConnect: OnConnectFunc): ActionTypes => ({
|
||||
type: SET_ON_CONNECT,
|
||||
payload: {
|
||||
onConnect,
|
||||
},
|
||||
});
|
||||
|
||||
export const setOnConnectStart = (onConnectStart: OnConnectStartFunc): ActionTypes => ({
|
||||
type: SET_ON_CONNECT_START,
|
||||
payload: {
|
||||
onConnectStart,
|
||||
},
|
||||
});
|
||||
|
||||
export const setOnConnectStop = (onConnectStop: OnConnectStopFunc): ActionTypes => ({
|
||||
type: SET_ON_CONNECT_STOP,
|
||||
payload: {
|
||||
onConnectStop,
|
||||
},
|
||||
});
|
||||
|
||||
export const setOnConnectEnd = (onConnectEnd: OnConnectEndFunc): ActionTypes => ({
|
||||
type: SET_ON_CONNECT_END,
|
||||
payload: {
|
||||
onConnectEnd,
|
||||
},
|
||||
});
|
||||
|
||||
export const setElements = (elements: Elements): ActionTypes => ({
|
||||
type: SET_ELEMENTS,
|
||||
elements,
|
||||
});
|
||||
|
||||
export const batchUpdateNodeDimensions = (updates: NodeDimensionUpdate[]): ActionTypes => ({
|
||||
type: BATCH_UPDATE_NODE_DIMENSIONS,
|
||||
updates,
|
||||
});
|
||||
|
||||
export const updateNodeDimensions = (payload: NodeDimensionUpdate): ActionTypes => ({
|
||||
type: UPDATE_NODE_DIMENSIONS,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const updateNodePos = (payload: NodePosUpdate): ActionTypes => ({
|
||||
type: UPDATE_NODE_POS,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const updateNodePosDiff = (payload: NodeDiffUpdate): ActionTypes => ({
|
||||
type: UPDATE_NODE_POS_DIFF,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const setUserSelection = (mousePos: XYPosition): ActionTypes => ({
|
||||
type: SET_USER_SELECTION,
|
||||
mousePos,
|
||||
});
|
||||
|
||||
export const updateUserSelection = (mousePos: XYPosition): ActionTypes => ({
|
||||
type: SET_USER_SELECTION,
|
||||
mousePos,
|
||||
});
|
||||
|
||||
export const unsetUserSelection = (): ActionTypes => ({
|
||||
type: UNSET_USER_SELECTION,
|
||||
});
|
||||
|
||||
export const setSelection = (selectionActive: boolean): ActionTypes => ({
|
||||
type: SET_SELECTION,
|
||||
payload: {
|
||||
selectionActive,
|
||||
},
|
||||
});
|
||||
|
||||
export const unsetNodesSelection = (): ActionTypes => ({
|
||||
type: UNSET_NODES_SELECTION,
|
||||
payload: {
|
||||
nodesSelectionActive: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const resetSelectedElements = (): ActionTypes => ({
|
||||
type: RESET_SELECTED_ELEMENTS,
|
||||
payload: {
|
||||
selectedElements: null,
|
||||
},
|
||||
});
|
||||
|
||||
export const setSelectedElements = (elements: Elements): ActionTypes => ({
|
||||
type: SET_SELECTED_ELEMENTS,
|
||||
elements,
|
||||
});
|
||||
|
||||
export const addSelectedElements = (elements: Elements): ActionTypes => ({
|
||||
type: ADD_SELECTED_ELEMENTS,
|
||||
elements,
|
||||
});
|
||||
|
||||
export const updateTransform = (transform: Transform): ActionTypes => ({
|
||||
type: UPDATE_TRANSFORM,
|
||||
payload: {
|
||||
transform,
|
||||
},
|
||||
});
|
||||
|
||||
export const updateSize = (size: Dimensions): ActionTypes => ({
|
||||
type: UPDATE_SIZE,
|
||||
payload: {
|
||||
size: {
|
||||
width: size.width || 500,
|
||||
height: size.height || 500,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const initD3Zoom = (payload: InitD3ZoomPayload): ActionTypes => ({
|
||||
type: INIT_D3ZOOM,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const setMinZoom = (minZoom: number): ActionTypes => ({
|
||||
type: SET_MINZOOM,
|
||||
payload: { minZoom },
|
||||
});
|
||||
|
||||
export const setMaxZoom = (maxZoom: number): ActionTypes => ({
|
||||
type: SET_MAXZOOM,
|
||||
payload: { maxZoom },
|
||||
});
|
||||
|
||||
export const setTranslateExtent = (translateExtent: TranslateExtent): ActionTypes => ({
|
||||
type: SET_TRANSLATEEXTENT,
|
||||
payload: { translateExtent },
|
||||
});
|
||||
|
||||
export const setConnectionPosition = (connectionPosition: XYPosition): ActionTypes => ({
|
||||
type: SET_CONNECTION_POSITION,
|
||||
payload: { connectionPosition },
|
||||
});
|
||||
|
||||
export const setConnectionNodeId = (payload: SetConnectionId): ActionTypes => ({
|
||||
type: SET_CONNECTION_NODEID,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const setSnapToGrid = (snapToGrid: boolean): ActionTypes => ({
|
||||
type: SET_SNAPTOGRID,
|
||||
payload: { snapToGrid },
|
||||
});
|
||||
|
||||
export const setSnapGrid = (snapGrid: SnapGrid): ActionTypes => ({
|
||||
type: SET_SNAPGRID,
|
||||
payload: { snapGrid },
|
||||
});
|
||||
|
||||
export const setInteractive = (isInteractive: boolean): ActionTypes => ({
|
||||
type: SET_INTERACTIVE,
|
||||
payload: {
|
||||
nodesDraggable: isInteractive,
|
||||
nodesConnectable: isInteractive,
|
||||
elementsSelectable: isInteractive,
|
||||
},
|
||||
});
|
||||
|
||||
export const setNodesDraggable = (nodesDraggable: boolean): ActionTypes => ({
|
||||
type: SET_NODES_DRAGGABLE,
|
||||
payload: { nodesDraggable },
|
||||
});
|
||||
|
||||
export const setNodesConnectable = (nodesConnectable: boolean): ActionTypes => ({
|
||||
type: SET_NODES_CONNECTABLE,
|
||||
payload: { nodesConnectable },
|
||||
});
|
||||
|
||||
export const setElementsSelectable = (elementsSelectable: boolean): ActionTypes => ({
|
||||
type: SET_ELEMENTS_SELECTABLE,
|
||||
payload: { elementsSelectable },
|
||||
});
|
||||
|
||||
export const setMultiSelectionActive = (multiSelectionActive: boolean): ActionTypes => ({
|
||||
type: SET_MULTI_SELECTION_ACTIVE,
|
||||
payload: { multiSelectionActive },
|
||||
});
|
||||
|
||||
export const setConnectionMode = (connectionMode: ConnectionMode): ActionTypes => ({
|
||||
type: SET_CONNECTION_MODE,
|
||||
payload: { connectionMode },
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { applyMiddleware, compose, createStore, Store } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
|
||||
import { ReactFlowState, reactFlowReducer } from './index';
|
||||
|
||||
export default function configureStore(preloadedState: ReactFlowState): Store<ReactFlowState> {
|
||||
const composedEnhancers = compose(applyMiddleware(thunkMiddleware));
|
||||
const store = createStore(reactFlowReducer, preloadedState, composedEnhancers);
|
||||
|
||||
return store;
|
||||
}
|
||||
+25
-7
@@ -1,10 +1,28 @@
|
||||
import { createTypedHooks } from 'easy-peasy';
|
||||
import { bindActionCreators, ActionCreatorsMapObject } from 'redux';
|
||||
import { useStore as useStoreRedux, useSelector, useDispatch, TypedUseSelectorHook } from 'react-redux';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { StoreModel } from './index';
|
||||
import { ReactFlowState, AppDispatch } from './index';
|
||||
import { ActionTypes } from './action-types';
|
||||
|
||||
const typedHooks = createTypedHooks<StoreModel>();
|
||||
export const useTypedSelector: TypedUseSelectorHook<ReactFlowState> = useSelector;
|
||||
|
||||
export const useStoreActions = typedHooks.useStoreActions;
|
||||
export const useStoreDispatch = typedHooks.useStoreDispatch;
|
||||
export const useStoreState = typedHooks.useStoreState;
|
||||
export const useStore = typedHooks.useStore;
|
||||
export function useActions(actions: ActionTypes, deps?: any): ActionTypes {
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
|
||||
const action = useMemo(
|
||||
() => {
|
||||
if (Array.isArray(actions)) {
|
||||
return actions.map((a) => bindActionCreators(a, dispatch));
|
||||
}
|
||||
return bindActionCreators<ActionCreatorsMapObject<ActionTypes>>(actions, dispatch);
|
||||
},
|
||||
deps ? [dispatch, ...deps] : [dispatch]
|
||||
);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
export const useStoreActions = useActions;
|
||||
export const useStoreState = useTypedSelector;
|
||||
export const useStore = useStoreRedux;
|
||||
|
||||
+375
-375
@@ -1,19 +1,17 @@
|
||||
import { createStore, Action, action, Thunk, thunk, computed, Computed } from 'easy-peasy';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||
|
||||
import { getDimensions } from '../utils';
|
||||
import { getNodesInside, getConnectedEdges, getRectOfNodes, isNode, isEdge, parseElement } from '../utils/graph';
|
||||
import { getHandleBounds } from '../components/Nodes/utils';
|
||||
import configureStore from './configure-store';
|
||||
|
||||
import {
|
||||
ElementId,
|
||||
Elements,
|
||||
Transform,
|
||||
Node,
|
||||
Edge,
|
||||
Rect,
|
||||
Dimensions,
|
||||
XYPosition,
|
||||
OnConnectFunc,
|
||||
OnConnectStartFunc,
|
||||
@@ -21,39 +19,57 @@ import {
|
||||
OnConnectEndFunc,
|
||||
SelectionRect,
|
||||
HandleType,
|
||||
SetConnectionId,
|
||||
NodePosUpdate,
|
||||
NodeDiffUpdate,
|
||||
TranslateExtent,
|
||||
SnapGrid,
|
||||
ConnectionMode,
|
||||
FlowElement,
|
||||
} from '../types';
|
||||
|
||||
type NodeDimensionUpdate = {
|
||||
id: ElementId;
|
||||
nodeElement: HTMLDivElement;
|
||||
};
|
||||
import {
|
||||
ActionTypes,
|
||||
ADD_SELECTED_ELEMENTS,
|
||||
BATCH_UPDATE_NODE_DIMENSIONS,
|
||||
INIT_D3ZOOM,
|
||||
RESET_SELECTED_ELEMENTS,
|
||||
SET_CONNECTION_MODE,
|
||||
SET_CONNECTION_NODEID,
|
||||
SET_CONNECTION_POSITION,
|
||||
SET_ELEMENTS,
|
||||
SET_ELEMENTS_SELECTABLE,
|
||||
SET_INTERACTIVE,
|
||||
SET_MAXZOOM,
|
||||
SET_MINZOOM,
|
||||
SET_MULTI_SELECTION_ACTIVE,
|
||||
SET_NODES_CONNECTABLE,
|
||||
SET_NODES_DRAGGABLE,
|
||||
SET_ON_CONNECT,
|
||||
SET_ON_CONNECT_END,
|
||||
SET_ON_CONNECT_START,
|
||||
SET_ON_CONNECT_STOP,
|
||||
SET_SELECTED_ELEMENTS,
|
||||
SET_SNAPGRID,
|
||||
SET_SNAPTOGRID,
|
||||
SET_TRANSLATEEXTENT,
|
||||
SET_USER_SELECTION,
|
||||
UNSET_USER_SELECTION,
|
||||
UPDATE_NODE_DIMENSIONS,
|
||||
UPDATE_NODE_POS,
|
||||
UPDATE_NODE_POS_DIFF,
|
||||
UPDATE_SIZE,
|
||||
UPDATE_TRANSFORM,
|
||||
UPDATE_USER_SELECTION,
|
||||
} from './action-types';
|
||||
|
||||
type NodeDimensionUpdates = {
|
||||
updates: NodeDimensionUpdate[];
|
||||
};
|
||||
|
||||
type InitD3Zoom = {
|
||||
d3Zoom: ZoomBehavior<Element, unknown>;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined>;
|
||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
|
||||
transform: Transform;
|
||||
};
|
||||
export interface StoreModel {
|
||||
export interface ReactFlowState {
|
||||
width: number;
|
||||
height: number;
|
||||
transform: Transform;
|
||||
elements: Elements;
|
||||
nodes: Computed<StoreModel, Node[]>;
|
||||
edges: Computed<StoreModel, Edge[]>;
|
||||
// nodes: Computed<StoreModel, Node[]>;
|
||||
// edges: Computed<StoreModel, Edge[]>;
|
||||
selectedElements: Elements | null;
|
||||
selectedNodesBbox: Rect;
|
||||
viewportBox: Computed<StoreModel, Rect>;
|
||||
// viewportBox: Computed<StoreModel, Rect>;
|
||||
|
||||
d3Zoom: ZoomBehavior<Element, unknown> | null;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
|
||||
@@ -88,70 +104,18 @@ export interface StoreModel {
|
||||
onConnectStart?: OnConnectStartFunc;
|
||||
onConnectStop?: OnConnectStopFunc;
|
||||
onConnectEnd?: OnConnectEndFunc;
|
||||
|
||||
setOnConnect: Action<StoreModel, OnConnectFunc>;
|
||||
setOnConnectStart: Action<StoreModel, OnConnectStartFunc>;
|
||||
setOnConnectStop: Action<StoreModel, OnConnectStopFunc>;
|
||||
setOnConnectEnd: Action<StoreModel, OnConnectEndFunc>;
|
||||
|
||||
setElements: Action<StoreModel, Elements>;
|
||||
|
||||
batchUpdateNodeDimensions: Action<StoreModel, NodeDimensionUpdates>;
|
||||
updateNodeDimensions: Action<StoreModel, NodeDimensionUpdate>;
|
||||
|
||||
updateNodePos: Action<StoreModel, NodePosUpdate>;
|
||||
updateNodePosDiff: Action<StoreModel, NodeDiffUpdate>;
|
||||
|
||||
setSelection: Action<StoreModel, boolean>;
|
||||
|
||||
unsetNodesSelection: Action<StoreModel>;
|
||||
resetSelectedElements: Action<StoreModel>;
|
||||
|
||||
setSelectedElements: Action<StoreModel, Elements | Node | Edge>;
|
||||
addSelectedElements: Thunk<StoreModel, Elements | Node | Edge>;
|
||||
|
||||
updateTransform: Action<StoreModel, Transform>;
|
||||
|
||||
updateSize: Action<StoreModel, Dimensions>;
|
||||
|
||||
initD3Zoom: Action<StoreModel, InitD3Zoom>;
|
||||
|
||||
setMinZoom: Action<StoreModel, number>;
|
||||
setMaxZoom: Action<StoreModel, number>;
|
||||
|
||||
setTranslateExtent: Action<StoreModel, TranslateExtent>;
|
||||
|
||||
setSnapToGrid: Action<StoreModel, boolean>;
|
||||
setSnapGrid: Action<StoreModel, SnapGrid>;
|
||||
|
||||
setConnectionPosition: Action<StoreModel, XYPosition>;
|
||||
|
||||
setConnectionNodeId: Action<StoreModel, SetConnectionId>;
|
||||
|
||||
setInteractive: Action<StoreModel, boolean>;
|
||||
setNodesDraggable: Action<StoreModel, boolean>;
|
||||
setNodesConnectable: Action<StoreModel, boolean>;
|
||||
setElementsSelectable: Action<StoreModel, boolean>;
|
||||
|
||||
setUserSelection: Action<StoreModel, XYPosition>;
|
||||
updateUserSelection: Action<StoreModel, XYPosition>;
|
||||
unsetUserSelection: Action<StoreModel>;
|
||||
|
||||
setMultiSelectionActive: Action<StoreModel, boolean>;
|
||||
|
||||
setConnectionMode: Action<StoreModel, ConnectionMode>;
|
||||
}
|
||||
|
||||
export const storeModel: StoreModel = {
|
||||
export const initialState: ReactFlowState = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
transform: [0, 0, 1],
|
||||
elements: [],
|
||||
nodes: computed((state) => state.elements.filter(isNode)),
|
||||
edges: computed((state) => state.elements.filter(isEdge)),
|
||||
// nodes: computed((state) => state.elements.filter(isNode)),
|
||||
// edges: computed((state) => state.elements.filter(isEdge)),
|
||||
selectedElements: null,
|
||||
selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 },
|
||||
viewportBox: computed((state) => ({ x: 0, y: 0, width: state.width, height: state.height })),
|
||||
// viewportBox: computed((state) => ({ x: 0, y: 0, width: state.width, height: state.height })),
|
||||
|
||||
d3Zoom: null,
|
||||
d3Selection: null,
|
||||
@@ -191,327 +155,363 @@ export const storeModel: StoreModel = {
|
||||
multiSelectionActive: false,
|
||||
|
||||
reactFlowVersion: typeof __REACT_FLOW_VERSION__ !== 'undefined' ? __REACT_FLOW_VERSION__ : '-',
|
||||
};
|
||||
|
||||
setOnConnect: action((state, onConnect) => {
|
||||
state.onConnect = onConnect;
|
||||
}),
|
||||
setOnConnectStart: action((state, onConnectStart) => {
|
||||
state.onConnectStart = onConnectStart;
|
||||
}),
|
||||
setOnConnectStop: action((state, onConnectStop) => {
|
||||
state.onConnectStop = onConnectStop;
|
||||
}),
|
||||
setOnConnectEnd: action((state, onConnectEnd) => {
|
||||
state.onConnectEnd = onConnectEnd;
|
||||
}),
|
||||
export function reactFlowReducer(state = initialState, action: ActionTypes): ReactFlowState {
|
||||
switch (action.type) {
|
||||
case SET_ELEMENTS: {
|
||||
const propElements = action.elements;
|
||||
|
||||
setElements: action((state, propElements) => {
|
||||
// remove deleted elements
|
||||
for (let i = 0; i < state.elements.length; i++) {
|
||||
const se = state.elements[i];
|
||||
const elementExistsInProps = propElements.find((pe) => pe.id === se.id);
|
||||
const nextElements = propElements.map((el: FlowElement) => {
|
||||
let storeElement = state.elements.find((se) => se.id === el.id);
|
||||
|
||||
if (!elementExistsInProps) {
|
||||
state.elements.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
// update existing element
|
||||
if (storeElement) {
|
||||
if (isNode(storeElement)) {
|
||||
const propNode = el as Node;
|
||||
const positionChanged =
|
||||
storeElement.position.x !== propNode.position.x || storeElement.position.y !== propNode.position.y;
|
||||
const typeChanged = typeof propNode.type !== 'undefined' && propNode.type !== storeElement.type;
|
||||
|
||||
propElements.forEach((el) => {
|
||||
const storeElementIndex = state.elements.findIndex((se) => se.id === el.id);
|
||||
storeElement = {
|
||||
...storeElement,
|
||||
...propNode,
|
||||
};
|
||||
|
||||
// update existing element
|
||||
if (storeElementIndex !== -1) {
|
||||
const storeElement = state.elements[storeElementIndex];
|
||||
if (positionChanged) {
|
||||
(storeElement as Node).__rf.position = propNode.position;
|
||||
}
|
||||
|
||||
if (isNode(storeElement)) {
|
||||
const propNode = el as Node;
|
||||
const positionChanged =
|
||||
storeElement.position.x !== propNode.position.x || storeElement.position.y !== propNode.position.y;
|
||||
const typeChanged = typeof propNode.type !== 'undefined' && propNode.type !== storeElement.type;
|
||||
|
||||
state.elements[storeElementIndex] = {
|
||||
...storeElement,
|
||||
...propNode,
|
||||
};
|
||||
|
||||
if (positionChanged) {
|
||||
(state.elements[storeElementIndex] as Node).__rf.position = propNode.position;
|
||||
if (typeChanged) {
|
||||
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
||||
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
||||
(storeElement as Node).__rf.width = null;
|
||||
}
|
||||
} else {
|
||||
storeElement = {
|
||||
...storeElement,
|
||||
...el,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeChanged) {
|
||||
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
||||
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
||||
(state.elements[storeElementIndex] as Node).__rf.width = null;
|
||||
}
|
||||
return storeElement;
|
||||
} else {
|
||||
state.elements[storeElementIndex] = {
|
||||
...storeElement,
|
||||
// add new element
|
||||
return parseElement(el);
|
||||
}
|
||||
});
|
||||
|
||||
return { ...state, elements: nextElements };
|
||||
}
|
||||
case BATCH_UPDATE_NODE_DIMENSIONS: {
|
||||
const updatedElements = state.elements.map((el) => {
|
||||
const update = action.updates.find((u) => u.id === el.id);
|
||||
if (update) {
|
||||
const dimensions = getDimensions(update.nodeElement);
|
||||
const nodeToUpdate = el as Node;
|
||||
|
||||
if (
|
||||
dimensions.width &&
|
||||
dimensions.height &&
|
||||
(nodeToUpdate.__rf.width !== dimensions.width || nodeToUpdate.__rf.height !== dimensions.height)
|
||||
) {
|
||||
const handleBounds = getHandleBounds(update.nodeElement, state.transform[2]);
|
||||
|
||||
return {
|
||||
...nodeToUpdate,
|
||||
__rf: {
|
||||
...nodeToUpdate.__rf,
|
||||
...dimensions,
|
||||
handleBounds,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
elements: updatedElements,
|
||||
};
|
||||
}
|
||||
case UPDATE_NODE_DIMENSIONS: {
|
||||
const { nodeElement, id } = action.payload;
|
||||
const dimensions = getDimensions(nodeElement);
|
||||
|
||||
if (!dimensions.width || !dimensions.height) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const nextElements = state.elements.map((el) => {
|
||||
if (el.id === id && isNode(el)) {
|
||||
const handleBounds = getHandleBounds(nodeElement, state.transform[2]);
|
||||
|
||||
return {
|
||||
...el,
|
||||
__rf: {
|
||||
...el.__rf,
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
handleBounds,
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// add new element
|
||||
state.elements.push(parseElement(el));
|
||||
}
|
||||
});
|
||||
}),
|
||||
|
||||
batchUpdateNodeDimensions: action((state, { updates }) => {
|
||||
updates.forEach((update) => {
|
||||
const dimensions = getDimensions(update.nodeElement);
|
||||
const matchingIndex = state.elements.findIndex((n) => n.id === update.id);
|
||||
const matchingNode = state.elements[matchingIndex] as Node;
|
||||
return el;
|
||||
});
|
||||
|
||||
if (
|
||||
matchingIndex !== -1 &&
|
||||
dimensions.width &&
|
||||
dimensions.height &&
|
||||
(matchingNode.__rf.width !== dimensions.width || matchingNode.__rf.height !== dimensions.height)
|
||||
) {
|
||||
const handleBounds = getHandleBounds(update.nodeElement, state.transform[2]);
|
||||
|
||||
(state.elements[matchingIndex] as Node).__rf.width = dimensions.width;
|
||||
(state.elements[matchingIndex] as Node).__rf.height = dimensions.height;
|
||||
(state.elements[matchingIndex] as Node).__rf.handleBounds = handleBounds;
|
||||
}
|
||||
});
|
||||
}),
|
||||
|
||||
updateNodeDimensions: action((state, { id, nodeElement }) => {
|
||||
const dimensions = getDimensions(nodeElement);
|
||||
const matchingIndex = state.elements.findIndex((n) => n.id === id);
|
||||
|
||||
if (matchingIndex !== -1 && dimensions.width && dimensions.height) {
|
||||
const handleBounds = getHandleBounds(nodeElement, state.transform[2]);
|
||||
|
||||
(state.elements[matchingIndex] as Node).__rf.width = dimensions.width;
|
||||
(state.elements[matchingIndex] as Node).__rf.height = dimensions.height;
|
||||
(state.elements[matchingIndex] as Node).__rf.handleBounds = handleBounds;
|
||||
return { ...state, elements: nextElements };
|
||||
}
|
||||
}),
|
||||
case UPDATE_NODE_POS: {
|
||||
const { id, pos } = action.payload;
|
||||
|
||||
updateNodePos: action((state, { id, pos }) => {
|
||||
let position: XYPosition = pos;
|
||||
let position: XYPosition = pos;
|
||||
|
||||
if (state.snapToGrid) {
|
||||
const [gridSizeX, gridSizeY] = state.snapGrid;
|
||||
position = {
|
||||
x: gridSizeX * Math.round(pos.x / gridSizeX),
|
||||
y: gridSizeY * Math.round(pos.y / gridSizeY),
|
||||
if (state.snapToGrid) {
|
||||
const [gridSizeX, gridSizeY] = state.snapGrid;
|
||||
position = {
|
||||
x: gridSizeX * Math.round(pos.x / gridSizeX),
|
||||
y: gridSizeY * Math.round(pos.y / gridSizeY),
|
||||
};
|
||||
}
|
||||
|
||||
const nextElements = state.elements.map((el) => {
|
||||
if (el.id === id && isNode(el)) {
|
||||
return {
|
||||
...el,
|
||||
__rf: {
|
||||
...el.__rf,
|
||||
position,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return el;
|
||||
});
|
||||
|
||||
return { ...state, elements: nextElements };
|
||||
}
|
||||
case UPDATE_NODE_POS_DIFF: {
|
||||
const { id, diff, isDragging } = action.payload;
|
||||
|
||||
const nextElements = state.elements.map((el) => {
|
||||
if (isNode(el) && (id === el.id || state.selectedElements?.find((sNode) => sNode.id === el.id))) {
|
||||
if (diff) {
|
||||
return {
|
||||
...el,
|
||||
__rf: {
|
||||
...el.__rf,
|
||||
isDragging,
|
||||
position: {
|
||||
x: el.__rf.position.x + diff.x,
|
||||
y: el.__rf.position.x + diff.y,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...el,
|
||||
__rf: {
|
||||
...el.__rf,
|
||||
isDragging,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return el;
|
||||
});
|
||||
|
||||
return { ...state, elements: nextElements };
|
||||
}
|
||||
case SET_USER_SELECTION: {
|
||||
const { mousePos } = action;
|
||||
|
||||
const userSelectionRect = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
startX: mousePos.x,
|
||||
startY: mousePos.y,
|
||||
x: mousePos.x,
|
||||
y: mousePos.y,
|
||||
draw: true,
|
||||
};
|
||||
|
||||
return {
|
||||
...state,
|
||||
userSelectionRect,
|
||||
selectionActive: true,
|
||||
};
|
||||
}
|
||||
case UPDATE_USER_SELECTION: {
|
||||
const { mousePos } = action;
|
||||
const startX = state.userSelectionRect.startX || 0;
|
||||
const startY = state.userSelectionRect.startY || 0;
|
||||
|
||||
const negativeX = mousePos.x < startX;
|
||||
const negativeY = mousePos.y < startY;
|
||||
|
||||
const nextUserSelectRect = {
|
||||
...state.userSelectionRect,
|
||||
x: negativeX ? mousePos.x : state.userSelectionRect.x,
|
||||
y: negativeY ? mousePos.y : state.userSelectionRect.y,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
height: Math.abs(mousePos.y - startY),
|
||||
};
|
||||
|
||||
const nodes = state.elements.filter(isNode);
|
||||
const edges = state.elements.filter(isEdge);
|
||||
|
||||
const selectedNodes = getNodesInside(nodes, nextUserSelectRect, state.transform);
|
||||
const selectedEdges = getConnectedEdges(selectedNodes, edges);
|
||||
|
||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges];
|
||||
const selectedElementsUpdated = !isEqual(nextSelectedElements, state.selectedElements);
|
||||
|
||||
if (selectedElementsUpdated) {
|
||||
return {
|
||||
...state,
|
||||
selectedElements: nextSelectedElements.length > 0 ? nextSelectedElements : null,
|
||||
userSelectionRect: nextUserSelectRect,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
userSelectionRect: nextUserSelectRect,
|
||||
};
|
||||
}
|
||||
case UNSET_USER_SELECTION: {
|
||||
const selectedNodes = state.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[];
|
||||
|
||||
const selectionActive = false;
|
||||
const userSelectionRect = {
|
||||
...state.userSelectionRect,
|
||||
draw: false,
|
||||
};
|
||||
|
||||
if (!selectedNodes || selectedNodes.length === 0) {
|
||||
return {
|
||||
...state,
|
||||
selectionActive,
|
||||
userSelectionRect,
|
||||
selectedElements: null,
|
||||
nodesSelectionActive: false,
|
||||
};
|
||||
}
|
||||
|
||||
const selectedNodesBbox = getRectOfNodes(selectedNodes);
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectionActive,
|
||||
userSelectionRect,
|
||||
selectedNodesBbox,
|
||||
nodesSelectionActive: true,
|
||||
};
|
||||
}
|
||||
case SET_SELECTED_ELEMENTS: {
|
||||
const { elements } = action;
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
|
||||
const selectedElementsUpdated = !isEqual(selectedElementsArr, state.selectedElements);
|
||||
const selectedElements = selectedElementsUpdated ? selectedElementsArr : state.selectedElements;
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectedElements,
|
||||
};
|
||||
}
|
||||
case ADD_SELECTED_ELEMENTS: {
|
||||
const { multiSelectionActive, selectedElements } = state;
|
||||
const { elements } = action;
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
|
||||
|
||||
let nextElements = selectedElementsArr;
|
||||
|
||||
if (multiSelectionActive) {
|
||||
nextElements = selectedElements ? [...selectedElements, ...selectedElementsArr] : selectedElementsArr;
|
||||
}
|
||||
|
||||
const selectedElementsUpdated = !isEqual(nextElements, state.selectedElements);
|
||||
const nextSelectedElements = selectedElementsUpdated ? nextElements : state.selectedElements;
|
||||
|
||||
return { ...state, selectedElements: nextSelectedElements };
|
||||
}
|
||||
case INIT_D3ZOOM: {
|
||||
const { d3Zoom, d3Selection, d3ZoomHandler, transform } = action.payload;
|
||||
|
||||
return {
|
||||
...state,
|
||||
d3Zoom,
|
||||
d3Selection,
|
||||
d3ZoomHandler,
|
||||
transform,
|
||||
};
|
||||
}
|
||||
case SET_MINZOOM: {
|
||||
const { minZoom } = action.payload;
|
||||
|
||||
if (state.d3Zoom) {
|
||||
state.d3Zoom.scaleExtent([minZoom, state.maxZoom]);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
minZoom,
|
||||
};
|
||||
}
|
||||
|
||||
state.elements.forEach((n) => {
|
||||
if (n.id === id && isNode(n)) {
|
||||
n.__rf.position = position;
|
||||
case SET_MAXZOOM: {
|
||||
const { maxZoom } = action.payload;
|
||||
|
||||
if (state.d3Zoom) {
|
||||
state.d3Zoom.scaleExtent([state.minZoom, maxZoom]);
|
||||
}
|
||||
});
|
||||
}),
|
||||
|
||||
updateNodePosDiff: action((state, { id = null, diff = null, isDragging = true }) => {
|
||||
state.elements.forEach((n) => {
|
||||
if (isNode(n) && (id === n.id || state.selectedElements?.find((sNode) => sNode.id === n.id))) {
|
||||
if (diff) {
|
||||
n.__rf.position = {
|
||||
x: n.__rf.position.x + diff.x,
|
||||
y: n.__rf.position.y + diff.y,
|
||||
};
|
||||
}
|
||||
n.__rf.isDragging = isDragging;
|
||||
return {
|
||||
...state,
|
||||
maxZoom,
|
||||
};
|
||||
}
|
||||
case SET_TRANSLATEEXTENT: {
|
||||
const { translateExtent } = action.payload;
|
||||
|
||||
if (state.d3Zoom) {
|
||||
state.d3Zoom.translateExtent(translateExtent);
|
||||
}
|
||||
});
|
||||
}),
|
||||
|
||||
setUserSelection: action((state, mousePos) => {
|
||||
state.userSelectionRect = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
startX: mousePos.x,
|
||||
startY: mousePos.y,
|
||||
x: mousePos.x,
|
||||
y: mousePos.y,
|
||||
draw: true,
|
||||
};
|
||||
state.selectionActive = true;
|
||||
}),
|
||||
|
||||
updateUserSelection: action((state, mousePos) => {
|
||||
const startX = state.userSelectionRect.startX || 0;
|
||||
const startY = state.userSelectionRect.startY || 0;
|
||||
|
||||
const negativeX = mousePos.x < startX;
|
||||
const negativeY = mousePos.y < startY;
|
||||
const nextRect = {
|
||||
...state.userSelectionRect,
|
||||
x: negativeX ? mousePos.x : state.userSelectionRect.x,
|
||||
y: negativeY ? mousePos.y : state.userSelectionRect.y,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
height: Math.abs(mousePos.y - startY),
|
||||
};
|
||||
|
||||
const selectedNodes = getNodesInside(state.nodes, nextRect, state.transform);
|
||||
const selectedEdges = getConnectedEdges(selectedNodes, state.edges);
|
||||
|
||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges];
|
||||
const selectedElementsUpdated = !isEqual(nextSelectedElements, state.selectedElements);
|
||||
|
||||
state.userSelectionRect = nextRect;
|
||||
|
||||
if (selectedElementsUpdated) {
|
||||
state.selectedElements = nextSelectedElements.length > 0 ? nextSelectedElements : null;
|
||||
return {
|
||||
...state,
|
||||
translateExtent,
|
||||
};
|
||||
}
|
||||
}),
|
||||
case SET_ON_CONNECT:
|
||||
case SET_ON_CONNECT_START:
|
||||
case SET_ON_CONNECT_STOP:
|
||||
case SET_ON_CONNECT_END:
|
||||
case RESET_SELECTED_ELEMENTS:
|
||||
case UPDATE_TRANSFORM:
|
||||
case UPDATE_SIZE:
|
||||
case SET_CONNECTION_POSITION:
|
||||
case SET_CONNECTION_NODEID:
|
||||
case SET_SNAPTOGRID:
|
||||
case SET_SNAPGRID:
|
||||
case SET_INTERACTIVE:
|
||||
case SET_NODES_DRAGGABLE:
|
||||
case SET_NODES_CONNECTABLE:
|
||||
case SET_ELEMENTS_SELECTABLE:
|
||||
case SET_MULTI_SELECTION_ACTIVE:
|
||||
case SET_CONNECTION_MODE:
|
||||
return { ...state, ...action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
unsetUserSelection: action((state) => {
|
||||
const selectedNodes = state.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[];
|
||||
const store = configureStore(initialState);
|
||||
|
||||
if (!selectedNodes || selectedNodes.length === 0) {
|
||||
state.selectionActive = false;
|
||||
state.userSelectionRect.draw = false;
|
||||
state.nodesSelectionActive = false;
|
||||
state.selectedElements = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedNodesBbox = getRectOfNodes(selectedNodes);
|
||||
|
||||
state.nodesSelectionActive = true;
|
||||
state.selectedNodesBbox = selectedNodesBbox;
|
||||
|
||||
state.userSelectionRect.draw = false;
|
||||
state.selectionActive = false;
|
||||
}),
|
||||
|
||||
setSelection: action((state, isActive) => {
|
||||
state.selectionActive = isActive;
|
||||
}),
|
||||
|
||||
unsetNodesSelection: action((state) => {
|
||||
state.nodesSelectionActive = false;
|
||||
}),
|
||||
|
||||
resetSelectedElements: action((state) => {
|
||||
state.selectedElements = null;
|
||||
}),
|
||||
|
||||
setSelectedElements: action((state, elements) => {
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
|
||||
const selectedElementsUpdated = !isEqual(selectedElementsArr, state.selectedElements);
|
||||
const selectedElements = selectedElementsUpdated ? selectedElementsArr : state.selectedElements;
|
||||
|
||||
state.selectedElements = selectedElements;
|
||||
}),
|
||||
|
||||
addSelectedElements: thunk((actions, elements, helpers) => {
|
||||
const { multiSelectionActive, selectedElements } = helpers.getState();
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
|
||||
|
||||
if (multiSelectionActive) {
|
||||
const nextElements = selectedElements ? [...selectedElements, ...selectedElementsArr] : selectedElementsArr;
|
||||
actions.setSelectedElements(nextElements);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
actions.setSelectedElements(elements);
|
||||
}),
|
||||
|
||||
updateTransform: action((state, transform) => {
|
||||
state.transform[0] = transform[0];
|
||||
state.transform[1] = transform[1];
|
||||
state.transform[2] = transform[2];
|
||||
}),
|
||||
|
||||
updateSize: action((state, size) => {
|
||||
// when parent has no size we use these default values
|
||||
// so that the calculations don't throw any errors
|
||||
state.width = size.width || 500;
|
||||
state.height = size.height || 500;
|
||||
}),
|
||||
|
||||
initD3Zoom: action((state, { d3Zoom, d3Selection, d3ZoomHandler, transform }) => {
|
||||
state.d3Zoom = d3Zoom;
|
||||
state.d3Selection = d3Selection;
|
||||
state.d3ZoomHandler = d3ZoomHandler;
|
||||
|
||||
state.transform[0] = transform[0];
|
||||
state.transform[1] = transform[1];
|
||||
state.transform[2] = transform[2];
|
||||
}),
|
||||
|
||||
setMinZoom: action((state, minZoom) => {
|
||||
state.minZoom = minZoom;
|
||||
|
||||
if (state.d3Zoom) {
|
||||
state.d3Zoom.scaleExtent([minZoom, state.maxZoom]);
|
||||
}
|
||||
}),
|
||||
|
||||
setMaxZoom: action((state, maxZoom) => {
|
||||
state.maxZoom = maxZoom;
|
||||
|
||||
if (state.d3Zoom) {
|
||||
state.d3Zoom.scaleExtent([state.minZoom, maxZoom]);
|
||||
}
|
||||
}),
|
||||
|
||||
setTranslateExtent: action((state, translateExtent) => {
|
||||
state.translateExtent = translateExtent;
|
||||
|
||||
if (state.d3Zoom) {
|
||||
state.d3Zoom.translateExtent(translateExtent);
|
||||
}
|
||||
}),
|
||||
|
||||
setConnectionPosition: action((state, position) => {
|
||||
state.connectionPosition = position;
|
||||
}),
|
||||
|
||||
setConnectionNodeId: action((state, { connectionNodeId, connectionHandleId, connectionHandleType }) => {
|
||||
state.connectionNodeId = connectionNodeId;
|
||||
state.connectionHandleId = connectionHandleId;
|
||||
state.connectionHandleType = connectionHandleType;
|
||||
}),
|
||||
|
||||
setSnapToGrid: action((state, snapToGrid) => {
|
||||
state.snapToGrid = snapToGrid;
|
||||
}),
|
||||
|
||||
setSnapGrid: action((state, snapGrid) => {
|
||||
state.snapGrid[0] = snapGrid[0];
|
||||
state.snapGrid[1] = snapGrid[1];
|
||||
}),
|
||||
|
||||
setInteractive: action((state, isInteractive) => {
|
||||
state.nodesDraggable = isInteractive;
|
||||
state.nodesConnectable = isInteractive;
|
||||
state.elementsSelectable = isInteractive;
|
||||
}),
|
||||
|
||||
setNodesDraggable: action((state, nodesDraggable) => {
|
||||
state.nodesDraggable = nodesDraggable;
|
||||
}),
|
||||
|
||||
setNodesConnectable: action((state, nodesConnectable) => {
|
||||
state.nodesConnectable = nodesConnectable;
|
||||
}),
|
||||
|
||||
setElementsSelectable: action((state, elementsSelectable) => {
|
||||
state.elementsSelectable = elementsSelectable;
|
||||
}),
|
||||
|
||||
setMultiSelectionActive: action((state, isActive) => {
|
||||
state.multiSelectionActive = isActive;
|
||||
}),
|
||||
|
||||
setConnectionMode: action((state, connectionMode) => {
|
||||
state.connectionMode = connectionMode;
|
||||
}),
|
||||
};
|
||||
|
||||
const nodeEnv: string = (typeof __ENV__ !== 'undefined' && __ENV__) as string;
|
||||
const store = createStore(storeModel, { devTools: nodeEnv === 'development' });
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
||||
export default store;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||
|
||||
export type ElementId = string;
|
||||
|
||||
@@ -360,3 +361,15 @@ export interface ZoomPanHelperFunctions {
|
||||
}
|
||||
|
||||
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
|
||||
|
||||
export type NodeDimensionUpdate = {
|
||||
id: ElementId;
|
||||
nodeElement: HTMLDivElement;
|
||||
};
|
||||
|
||||
export type InitD3ZoomPayload = {
|
||||
d3Zoom: ZoomBehavior<Element, unknown>;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined>;
|
||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
|
||||
transform: Transform;
|
||||
};
|
||||
|
||||
+11
-7
@@ -1,5 +1,5 @@
|
||||
import { Store } from 'easy-peasy';
|
||||
import { StoreModel } from '../store';
|
||||
import { Store } from 'redux';
|
||||
import { ReactFlowState } from '../store';
|
||||
import {
|
||||
ElementId,
|
||||
Node,
|
||||
@@ -134,7 +134,7 @@ export const pointToRendererPoint = (
|
||||
return position;
|
||||
};
|
||||
|
||||
export const onLoadProject = (currentStore: Store<StoreModel>) => {
|
||||
export const onLoadProject = (currentStore: Store<ReactFlowState>) => {
|
||||
return (position: XYPosition): XYPosition => {
|
||||
const { transform, snapToGrid, snapGrid } = currentStore.getState();
|
||||
|
||||
@@ -266,17 +266,21 @@ const parseElements = (nodes: Node[], edges: Edge[]): Elements => {
|
||||
];
|
||||
};
|
||||
|
||||
export const onLoadGetElements = (currentStore: Store<StoreModel>) => {
|
||||
export const onLoadGetElements = (currentStore: Store<ReactFlowState>) => {
|
||||
return (): Elements => {
|
||||
const { nodes = [], edges = [] } = currentStore.getState();
|
||||
const { elements = [] } = currentStore.getState();
|
||||
const nodes = elements.filter(isNode);
|
||||
const edges = elements.filter(isEdge);
|
||||
|
||||
return parseElements(nodes, edges);
|
||||
};
|
||||
};
|
||||
|
||||
export const onLoadToObject = (currentStore: Store<StoreModel>) => {
|
||||
export const onLoadToObject = (currentStore: Store<ReactFlowState>) => {
|
||||
return (): FlowExportObject => {
|
||||
const { nodes = [], edges = [], transform } = currentStore.getState();
|
||||
const { elements = [], transform } = currentStore.getState();
|
||||
const nodes = elements.filter(isNode);
|
||||
const edges = elements.filter(isEdge);
|
||||
|
||||
return {
|
||||
elements: parseElements(nodes, edges),
|
||||
|
||||
+12
-12
@@ -7,20 +7,20 @@
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"strict": false,
|
||||
"noImplicitAny": false,
|
||||
"strictNullChecks": false,
|
||||
"strictFunctionTypes": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"noImplicitThis": false,
|
||||
"alwaysStrict": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": false,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "build", "dist", "example", "rollup.config.js"]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user