refactor(store): use store.setState directly instead of actions
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { memo, useCallback, HTMLAttributes, FC, useEffect, useState } from 'react';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { useStore } from '../../store';
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
|
||||
import PlusIcon from '../../../assets/icons/plus.svg';
|
||||
import MinusIcon from '../../../assets/icons/minus.svg';
|
||||
@@ -31,7 +31,6 @@ export const ControlButton: FC<ControlButtonProps> = ({ children, className, ...
|
||||
</button>
|
||||
);
|
||||
|
||||
const setInteractiveSelector = (s: ReactFlowState) => s.setInteractive;
|
||||
const isInteractiveSelector = (s: ReactFlowState) => s.nodesDraggable && s.nodesConnectable && s.elementsSelectable;
|
||||
|
||||
const Controls: FC<ControlProps> = ({
|
||||
@@ -47,8 +46,8 @@ const Controls: FC<ControlProps> = ({
|
||||
className,
|
||||
children,
|
||||
}) => {
|
||||
const store = useStoreApi();
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||
const setInteractive = useStore(setInteractiveSelector);
|
||||
const isInteractive = useStore(isInteractiveSelector);
|
||||
const { zoomIn, zoomOut, fitView } = useZoomPanHelper();
|
||||
|
||||
@@ -70,9 +69,14 @@ const Controls: FC<ControlProps> = ({
|
||||
}, [fitView, fitViewParams, onFitView]);
|
||||
|
||||
const onInteractiveChangeHandler = useCallback(() => {
|
||||
setInteractive?.(!isInteractive);
|
||||
store.setState({
|
||||
nodesDraggable: !isInteractive,
|
||||
nodesConnectable: !isInteractive,
|
||||
elementsSelectable: !isInteractive,
|
||||
});
|
||||
|
||||
onInteractiveChange?.(!isInteractive);
|
||||
}, [isInteractive, setInteractive, onInteractiveChange]);
|
||||
}, [isInteractive, onInteractiveChange]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(true);
|
||||
|
||||
@@ -10,9 +10,6 @@ import { getMarkerId } from '../../utils/graph';
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
addSelectedEdges: s.addSelectedEdges,
|
||||
setConnectionNodeId: s.setConnectionNodeId,
|
||||
setNodesSelectionActive: s.setNodesSelectionActive,
|
||||
setPosition: s.setConnectionPosition,
|
||||
connectionMode: s.connectionMode,
|
||||
});
|
||||
|
||||
@@ -57,10 +54,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
markerStart,
|
||||
}: WrapEdgeProps): JSX.Element | null => {
|
||||
const store = useStoreApi();
|
||||
const { addSelectedEdges, setConnectionNodeId, setNodesSelectionActive, setPosition, connectionMode } = useStore(
|
||||
selector,
|
||||
shallow
|
||||
);
|
||||
const { addSelectedEdges, connectionMode } = useStore(selector, shallow);
|
||||
|
||||
const [updating, setUpdating] = useState<boolean>(false);
|
||||
|
||||
@@ -99,7 +93,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
const onEdgeClick = useCallback(
|
||||
(event: React.MouseEvent<SVGGElement, MouseEvent>): void => {
|
||||
if (elementsSelectable) {
|
||||
setNodesSelectionActive(false);
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
addSelectedEdges([edgeElement.id]);
|
||||
}
|
||||
|
||||
@@ -169,8 +163,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
event,
|
||||
handleId,
|
||||
nodeId,
|
||||
setConnectionNodeId,
|
||||
setPosition,
|
||||
store.setState,
|
||||
onConnectEdge,
|
||||
isTarget,
|
||||
isValidConnection,
|
||||
@@ -180,18 +173,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
store.getState
|
||||
);
|
||||
},
|
||||
[
|
||||
id,
|
||||
source,
|
||||
target,
|
||||
type,
|
||||
sourceHandleId,
|
||||
targetHandleId,
|
||||
setConnectionNodeId,
|
||||
setPosition,
|
||||
edgeElement,
|
||||
onEdgeUpdate,
|
||||
]
|
||||
[id, source, target, type, sourceHandleId, targetHandleId, edgeElement, onEdgeUpdate]
|
||||
);
|
||||
|
||||
const onEdgeUpdaterSourceMouseDown = useCallback(
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { SetState } from 'zustand';
|
||||
|
||||
import { getHostForElement } from '../../utils';
|
||||
|
||||
import {
|
||||
OnConnect,
|
||||
OnConnectStart,
|
||||
OnConnectStop,
|
||||
OnConnectEnd,
|
||||
ConnectionMode,
|
||||
SetConnectionId,
|
||||
Connection,
|
||||
HandleType,
|
||||
XYPosition,
|
||||
ReactFlowState,
|
||||
} from '../../types';
|
||||
|
||||
type ValidConnectionFunc = (connection: Connection) => boolean;
|
||||
export type SetSourceIdFunc = (params: SetConnectionId) => void;
|
||||
|
||||
export type SetPosition = (pos: XYPosition) => void;
|
||||
|
||||
type Result = {
|
||||
elementBelow: Element | null;
|
||||
@@ -90,8 +86,7 @@ export function onMouseDown(
|
||||
event: ReactMouseEvent,
|
||||
handleId: string | null,
|
||||
nodeId: string,
|
||||
setConnectionNodeId: SetSourceIdFunc,
|
||||
setPosition: SetPosition,
|
||||
setState: SetState<ReactFlowState>,
|
||||
onConnect: OnConnect,
|
||||
isTarget: boolean,
|
||||
isValidConnection: ValidConnectionFunc,
|
||||
@@ -122,18 +117,24 @@ export function onMouseDown(
|
||||
const containerBounds = reactFlowNode.getBoundingClientRect();
|
||||
let recentHoveredHandle: Element;
|
||||
|
||||
setPosition({
|
||||
x: event.clientX - containerBounds.left,
|
||||
y: event.clientY - containerBounds.top,
|
||||
setState({
|
||||
connectionPosition: {
|
||||
x: event.clientX - containerBounds.left,
|
||||
y: event.clientY - containerBounds.top,
|
||||
},
|
||||
connectionNodeId: nodeId,
|
||||
connectionHandleId: handleId,
|
||||
connectionHandleType: handleType,
|
||||
});
|
||||
|
||||
setConnectionNodeId({ connectionNodeId: nodeId, connectionHandleId: handleId, connectionHandleType: handleType });
|
||||
onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||
|
||||
function onMouseMove(event: MouseEvent) {
|
||||
setPosition({
|
||||
x: event.clientX - containerBounds.left,
|
||||
y: event.clientY - containerBounds.top,
|
||||
setState({
|
||||
connectionPosition: {
|
||||
x: event.clientX - containerBounds.left,
|
||||
y: event.clientY - containerBounds.top,
|
||||
},
|
||||
});
|
||||
|
||||
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
|
||||
@@ -183,7 +184,11 @@ export function onMouseDown(
|
||||
}
|
||||
|
||||
resetRecentHandle(recentHoveredHandle);
|
||||
setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null });
|
||||
setState({
|
||||
connectionNodeId: null,
|
||||
connectionHandleId: null,
|
||||
connectionHandleType: null,
|
||||
});
|
||||
|
||||
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
||||
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
||||
|
||||
@@ -2,18 +2,16 @@ import React, { memo, useContext, useCallback, HTMLAttributes, forwardRef } from
|
||||
import cc from 'classcat';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../store';
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import NodeIdContext from '../../contexts/NodeIdContext';
|
||||
import { HandleProps, Connection, ReactFlowState, Position } from '../../types';
|
||||
import { onMouseDown, SetSourceIdFunc, SetPosition } from './handler';
|
||||
import { onMouseDown } from './handler';
|
||||
|
||||
const alwaysValid = () => true;
|
||||
|
||||
export type HandleComponentProps = HandleProps & Omit<HTMLAttributes<HTMLDivElement>, 'id'>;
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setPosition: s.setConnectionPosition,
|
||||
setConnectionNodeId: s.setConnectionNodeId,
|
||||
onConnectAction: s.onConnect,
|
||||
onConnectStart: s.onConnectStart,
|
||||
onConnectStop: s.onConnectStop,
|
||||
@@ -36,16 +34,12 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const store = useStoreApi();
|
||||
const nodeId = useContext(NodeIdContext) as string;
|
||||
const {
|
||||
setPosition,
|
||||
setConnectionNodeId,
|
||||
onConnectAction,
|
||||
onConnectStart,
|
||||
onConnectStop,
|
||||
onConnectEnd,
|
||||
connectionMode,
|
||||
} = useStore(selector, shallow);
|
||||
const { onConnectAction, onConnectStart, onConnectStop, onConnectEnd, connectionMode } = useStore(
|
||||
selector,
|
||||
shallow
|
||||
);
|
||||
|
||||
const handleId = id || null;
|
||||
const isTarget = type === 'target';
|
||||
@@ -64,8 +58,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
event,
|
||||
handleId,
|
||||
nodeId,
|
||||
setConnectionNodeId as unknown as SetSourceIdFunc,
|
||||
setPosition as unknown as SetPosition,
|
||||
store.setState,
|
||||
onConnectExtended,
|
||||
isTarget,
|
||||
isValidConnection,
|
||||
@@ -80,8 +73,6 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
[
|
||||
handleId,
|
||||
nodeId,
|
||||
setConnectionNodeId,
|
||||
setPosition,
|
||||
onConnectExtended,
|
||||
isTarget,
|
||||
isValidConnection,
|
||||
|
||||
@@ -3,13 +3,12 @@ import { DraggableCore, DraggableData, DraggableEvent } from 'react-draggable';
|
||||
import cc from 'classcat';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../store';
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import { Provider } from '../../contexts/NodeIdContext';
|
||||
import { NodeProps, WrapNodeProps, ReactFlowState } from '../../types';
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
addSelectedNodes: s.addSelectedNodes,
|
||||
setNodesSelectionActive: s.setNodesSelectionActive,
|
||||
updateNodePosition: s.updateNodePosition,
|
||||
unselectNodesAndEdges: s.unselectNodesAndEdges,
|
||||
});
|
||||
@@ -52,10 +51,8 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
noPanClassName,
|
||||
noDragClassName,
|
||||
}: WrapNodeProps) => {
|
||||
const { addSelectedNodes, unselectNodesAndEdges, setNodesSelectionActive, updateNodePosition } = useStore(
|
||||
selector,
|
||||
shallow
|
||||
);
|
||||
const store = useStoreApi();
|
||||
const { addSelectedNodes, unselectNodesAndEdges, updateNodePosition } = useStore(selector, shallow);
|
||||
const nodeElement = useRef<HTMLDivElement>(null);
|
||||
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
||||
const nodeStyle: CSSProperties = useMemo(
|
||||
@@ -109,7 +106,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
(event: MouseEvent) => {
|
||||
if (!isDraggable) {
|
||||
if (isSelectable) {
|
||||
setNodesSelectionActive(false);
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
|
||||
if (!selected) {
|
||||
addSelectedNodes([node.id]);
|
||||
@@ -127,14 +124,14 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
onNodeDragStart?.(event as MouseEvent, node);
|
||||
|
||||
if (selectNodesOnDrag && isSelectable) {
|
||||
setNodesSelectionActive(false);
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
|
||||
if (!selected) {
|
||||
addSelectedNodes([node.id]);
|
||||
}
|
||||
} else if (!selectNodesOnDrag && !selected && isSelectable) {
|
||||
unselectNodesAndEdges();
|
||||
setNodesSelectionActive(false);
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
}
|
||||
},
|
||||
[node, selected, selectNodesOnDrag, isSelectable, onNodeDragStart]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import { SetState } from 'zustand';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../store';
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import {
|
||||
Node,
|
||||
Edge,
|
||||
@@ -42,24 +43,11 @@ interface StoreUpdaterProps {
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setNodes: s.setNodes,
|
||||
setEdges: s.setEdges,
|
||||
setOnConnect: s.setOnConnect,
|
||||
setOnConnectStart: s.setOnConnectStart,
|
||||
setOnConnectStop: s.setOnConnectStop,
|
||||
setOnConnectEnd: s.setOnConnectEnd,
|
||||
setSnapGrid: s.setSnapGrid,
|
||||
setSnapToGrid: s.setSnapToGrid,
|
||||
setNodesDraggable: s.setNodesDraggable,
|
||||
setNodesConnectable: s.setNodesConnectable,
|
||||
setElementsSelectable: s.setElementsSelectable,
|
||||
setMinZoom: s.setMinZoom,
|
||||
setMaxZoom: s.setMaxZoom,
|
||||
setTranslateExtent: s.setTranslateExtent,
|
||||
setNodeExtent: s.setNodeExtent,
|
||||
setConnectionMode: s.setConnectionMode,
|
||||
setOnNodesChange: s.setOnNodesChange,
|
||||
setOnEdgesChange: s.setOnEdgesChange,
|
||||
reset: s.reset,
|
||||
setFitViewOnInit: s.setFitViewOnInit,
|
||||
});
|
||||
|
||||
function useStoreUpdater<T>(value: T | undefined, setStoreState: (param: T) => void) {
|
||||
@@ -70,6 +58,15 @@ function useStoreUpdater<T>(value: T | undefined, setStoreState: (param: T) => v
|
||||
}, [value]);
|
||||
}
|
||||
|
||||
function useDirectStoreUpdater(key: keyof ReactFlowState, value: any, setState: SetState<ReactFlowState>) {
|
||||
useEffect(() => {
|
||||
if (typeof value !== 'undefined') {
|
||||
// @ts-ignore
|
||||
setState({ [key]: value });
|
||||
}
|
||||
}, [value]);
|
||||
}
|
||||
|
||||
const StoreUpdater = ({
|
||||
nodes,
|
||||
edges,
|
||||
@@ -91,28 +88,11 @@ const StoreUpdater = ({
|
||||
translateExtent,
|
||||
fitViewOnInit,
|
||||
}: StoreUpdaterProps) => {
|
||||
const {
|
||||
setNodes,
|
||||
setEdges,
|
||||
setOnConnect,
|
||||
setOnConnectStart,
|
||||
setOnConnectStop,
|
||||
setOnConnectEnd,
|
||||
setNodesDraggable,
|
||||
setSnapGrid,
|
||||
setSnapToGrid,
|
||||
setNodesConnectable,
|
||||
setElementsSelectable,
|
||||
setMinZoom,
|
||||
setMaxZoom,
|
||||
setTranslateExtent,
|
||||
setNodeExtent,
|
||||
setOnNodesChange,
|
||||
setOnEdgesChange,
|
||||
setConnectionMode,
|
||||
reset,
|
||||
setFitViewOnInit,
|
||||
} = useStore(selector, shallow);
|
||||
const { setNodes, setEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset } = useStore(
|
||||
selector,
|
||||
shallow
|
||||
);
|
||||
const store = useStoreApi();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -122,24 +102,24 @@ const StoreUpdater = ({
|
||||
|
||||
useStoreUpdater<Node[]>(nodes, setNodes);
|
||||
useStoreUpdater<Edge[]>(edges, setEdges);
|
||||
useStoreUpdater<OnConnect>(onConnect, setOnConnect);
|
||||
useStoreUpdater<OnConnect>(onConnect, setOnConnect);
|
||||
useStoreUpdater<OnConnectStart>(onConnectStart, setOnConnectStart);
|
||||
useStoreUpdater<OnConnectStop>(onConnectStop, setOnConnectStop);
|
||||
useStoreUpdater<OnConnectEnd>(onConnectEnd, setOnConnectEnd);
|
||||
useStoreUpdater<boolean>(snapToGrid, setSnapToGrid);
|
||||
useStoreUpdater<SnapGrid>(snapGrid, setSnapGrid);
|
||||
useStoreUpdater<boolean>(nodesDraggable, setNodesDraggable);
|
||||
useStoreUpdater<boolean>(nodesConnectable, setNodesConnectable);
|
||||
useStoreUpdater<boolean>(elementsSelectable, setElementsSelectable);
|
||||
useStoreUpdater<number>(minZoom, setMinZoom);
|
||||
useStoreUpdater<number>(maxZoom, setMaxZoom);
|
||||
useStoreUpdater<CoordinateExtent>(translateExtent, setTranslateExtent);
|
||||
useStoreUpdater<CoordinateExtent>(nodeExtent, setNodeExtent);
|
||||
useStoreUpdater<ConnectionMode>(connectionMode, setConnectionMode);
|
||||
useStoreUpdater<OnNodesChange>(onNodesChange, setOnNodesChange);
|
||||
useStoreUpdater<OnEdgesChange>(onEdgesChange, setOnEdgesChange);
|
||||
useStoreUpdater<boolean>(fitViewOnInit, setFitViewOnInit);
|
||||
|
||||
useDirectStoreUpdater('connectionMode', connectionMode, store.setState);
|
||||
useDirectStoreUpdater('onConnect', onConnect, store.setState);
|
||||
useDirectStoreUpdater('onConnectStart', onConnectStart, store.setState);
|
||||
useDirectStoreUpdater('onConnectStop', onConnectStop, store.setState);
|
||||
useDirectStoreUpdater('onConnectEnd', onConnectEnd, store.setState);
|
||||
useDirectStoreUpdater('nodesDraggable', nodesDraggable, store.setState);
|
||||
useDirectStoreUpdater('nodesConnectable', nodesConnectable, store.setState);
|
||||
useDirectStoreUpdater('elementsSelectable', elementsSelectable, store.setState);
|
||||
useDirectStoreUpdater('fitViewOnInit', fitViewOnInit, store.setState);
|
||||
useDirectStoreUpdater('snapToGrid', snapToGrid, store.setState);
|
||||
useDirectStoreUpdater('snapGrid', snapGrid, store.setState);
|
||||
useDirectStoreUpdater('onNodesChange', onNodesChange, store.setState);
|
||||
useDirectStoreUpdater('onEdgesChange', onEdgesChange, store.setState);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -30,8 +30,6 @@ function getMousePosition(event: React.MouseEvent, containerBounds: DOMRect): XY
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
userSelectionActive: s.userSelectionActive,
|
||||
elementsSelectable: s.elementsSelectable,
|
||||
setUserSelectionActive: s.setUserSelectionActive,
|
||||
setNodesSelectionActive: s.setNodesSelectionActive,
|
||||
});
|
||||
|
||||
const initialRect: SelectionRect = {
|
||||
@@ -50,16 +48,14 @@ export default memo(({ selectionKeyPressed }: UserSelectionProps) => {
|
||||
const prevSelectedEdgesCount = useRef<number>(0);
|
||||
const containerBounds = useRef<DOMRect>();
|
||||
const [userSelectionRect, setUserSelectionRect] = useState<SelectionRect>(initialRect);
|
||||
const { userSelectionActive, elementsSelectable, setUserSelectionActive, setNodesSelectionActive } = useStore(
|
||||
selector,
|
||||
shallow
|
||||
);
|
||||
const { userSelectionActive, elementsSelectable } = useStore(selector, shallow);
|
||||
|
||||
const renderUserSelectionPane = userSelectionActive || selectionKeyPressed;
|
||||
|
||||
const resetUserSelection = useCallback(() => {
|
||||
setUserSelectionRect(initialRect);
|
||||
setUserSelectionActive(false);
|
||||
|
||||
store.setState({ userSelectionActive: false });
|
||||
|
||||
prevSelectedNodesCount.current = 0;
|
||||
prevSelectedEdgesCount.current = 0;
|
||||
@@ -81,8 +77,7 @@ export default memo(({ selectionKeyPressed }: UserSelectionProps) => {
|
||||
draw: true,
|
||||
});
|
||||
|
||||
setUserSelectionActive(true);
|
||||
setNodesSelectionActive(false);
|
||||
store.setState({ userSelectionActive: true, nodesSelectionActive: false });
|
||||
}, []);
|
||||
|
||||
const onMouseMove = (event: React.MouseEvent): void => {
|
||||
@@ -128,12 +123,14 @@ export default memo(({ selectionKeyPressed }: UserSelectionProps) => {
|
||||
};
|
||||
|
||||
const onMouseUp = useCallback(() => {
|
||||
setNodesSelectionActive(prevSelectedNodesCount.current > 0);
|
||||
store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
|
||||
|
||||
resetUserSelection();
|
||||
}, []);
|
||||
|
||||
const onMouseLeave = useCallback(() => {
|
||||
setNodesSelectionActive(false);
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
|
||||
resetUserSelection();
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, memo, ReactNode, WheelEvent, MouseEvent } from 'react';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../store';
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import { GraphViewProps } from '../GraphView';
|
||||
@@ -27,7 +27,6 @@ interface FlowRendererProps
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setNodesSelectionActive: s.setNodesSelectionActive,
|
||||
resetSelectedElements: s.resetSelectedElements,
|
||||
nodesSelectionActive: s.nodesSelectionActive,
|
||||
});
|
||||
@@ -62,7 +61,8 @@ const FlowRenderer = ({
|
||||
noWheelClassName,
|
||||
noPanClassName,
|
||||
}: FlowRendererProps) => {
|
||||
const { setNodesSelectionActive, resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow);
|
||||
const store = useStoreApi();
|
||||
const { resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow);
|
||||
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
||||
|
||||
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
||||
@@ -70,8 +70,9 @@ const FlowRenderer = ({
|
||||
const onClick = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
onPaneClick?.(event);
|
||||
setNodesSelectionActive(false);
|
||||
resetSelectedElements();
|
||||
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
},
|
||||
[onPaneClick]
|
||||
);
|
||||
|
||||
@@ -48,9 +48,6 @@ const selector = (s: ReactFlowState) => ({
|
||||
d3Zoom: s.d3Zoom,
|
||||
d3Selection: s.d3Selection,
|
||||
d3ZoomHandler: s.d3ZoomHandler,
|
||||
|
||||
initD3Zoom: s.initD3Zoom,
|
||||
updateTransform: s.updateTransform,
|
||||
});
|
||||
|
||||
const ZoomPane = ({
|
||||
@@ -77,7 +74,7 @@ const ZoomPane = ({
|
||||
const store = useStoreApi();
|
||||
const zoomPane = useRef<HTMLDivElement>(null);
|
||||
const prevTransform = useRef<FlowTransform>({ x: 0, y: 0, zoom: 0 });
|
||||
const { d3Zoom, d3Selection, d3ZoomHandler, initD3Zoom, updateTransform } = useStore(selector, shallow);
|
||||
const { d3Zoom, d3Selection, d3ZoomHandler } = useStore(selector, shallow);
|
||||
const zoomActivationKeyPressed = useKeyPress(zoomActivationKeyCode);
|
||||
|
||||
useResizeHandler(zoomPane);
|
||||
@@ -95,7 +92,7 @@ const ZoomPane = ({
|
||||
|
||||
d3ZoomInstance.transform(selection, updatedTransform);
|
||||
|
||||
initD3Zoom({
|
||||
store.setState({
|
||||
d3Zoom: d3ZoomInstance,
|
||||
d3Selection: selection,
|
||||
d3ZoomHandler: selection.on('wheel.zoom'),
|
||||
@@ -171,7 +168,7 @@ const ZoomPane = ({
|
||||
d3Zoom.on('zoom', null);
|
||||
} else {
|
||||
d3Zoom.on('zoom', (event: any) => {
|
||||
updateTransform([event.transform.x, event.transform.y, event.transform.k]);
|
||||
store.setState({ transform: [event.transform.x, event.transform.y, event.transform.k] });
|
||||
|
||||
if (onMove) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
@@ -180,7 +177,7 @@ const ZoomPane = ({
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [selectionKeyPressed, d3Zoom, updateTransform, onMove]);
|
||||
}, [selectionKeyPressed, d3Zoom, onMove]);
|
||||
|
||||
useEffect(() => {
|
||||
if (d3Zoom) {
|
||||
|
||||
@@ -12,8 +12,6 @@ interface HookParams {
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setNodesSelectionActive: s.setNodesSelectionActive,
|
||||
setMultiSelectionActive: s.setMultiSelectionActive,
|
||||
resetSelectedElements: s.resetSelectedElements,
|
||||
onNodesChange: s.onNodesChange,
|
||||
onEdgesChange: s.onEdgesChange,
|
||||
@@ -21,8 +19,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
|
||||
export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => {
|
||||
const store = useStoreApi();
|
||||
const { setNodesSelectionActive, setMultiSelectionActive, resetSelectedElements, onNodesChange, onEdgesChange } =
|
||||
useStore(selector, shallow);
|
||||
const { resetSelectedElements, onNodesChange, onEdgesChange } = useStore(selector, shallow);
|
||||
|
||||
const deleteKeyPressed = useKeyPress(deleteKeyCode);
|
||||
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
|
||||
@@ -46,12 +43,13 @@ export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => {
|
||||
onNodesChange?.(nodeChanges);
|
||||
onEdgesChange?.(edgeChanges);
|
||||
|
||||
setNodesSelectionActive(false);
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
|
||||
resetSelectedElements();
|
||||
}
|
||||
}, [deleteKeyPressed, onNodesChange, onEdgesChange]);
|
||||
|
||||
useEffect(() => {
|
||||
setMultiSelectionActive(multiSelectionKeyPressed);
|
||||
store.setState({ multiSelectionActive: multiSelectionKeyPressed });
|
||||
}, [multiSelectionKeyPressed]);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { useEffect, MutableRefObject } from 'react';
|
||||
|
||||
import { useStore } from '../store';
|
||||
import { useStoreApi } from '../store';
|
||||
import { getDimensions } from '../utils';
|
||||
import { ReactFlowState } from '../types';
|
||||
|
||||
const updateSizeSelector = (state: ReactFlowState) => state.updateSize;
|
||||
|
||||
function useResizeHandler(rendererNode: MutableRefObject<HTMLDivElement | null>): void {
|
||||
const updateSize = useStore(updateSizeSelector);
|
||||
const store = useStoreApi();
|
||||
|
||||
useEffect(() => {
|
||||
let resizeObserver: ResizeObserver;
|
||||
@@ -23,7 +20,7 @@ function useResizeHandler(rendererNode: MutableRefObject<HTMLDivElement | null>)
|
||||
console.warn('The React Flow parent container needs a width and a height to render the graph.');
|
||||
}
|
||||
|
||||
updateSize(size);
|
||||
store.setState({ width: size.width || 500, height: size.height || 500 });
|
||||
};
|
||||
|
||||
updateDimensions();
|
||||
|
||||
@@ -3,10 +3,10 @@ import { useCallback } from 'react';
|
||||
import { useStore } from '../store';
|
||||
import { UpdateNodeInternals, ReactFlowState } from '../types';
|
||||
|
||||
const updateNodeDimsSelector = (state: ReactFlowState) => state.updateNodeDimensions;
|
||||
const selector = (state: ReactFlowState) => state.updateNodeDimensions;
|
||||
|
||||
function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
const updateNodeDimensions = useStore(updateNodeDimsSelector);
|
||||
const updateNodeDimensions = useStore(selector);
|
||||
|
||||
return useCallback<UpdateNodeInternals>((id: string) => {
|
||||
const nodeElement = document.querySelector(`.react-flow__node[data-id="${id}"]`) as HTMLDivElement;
|
||||
|
||||
@@ -4,27 +4,14 @@ import createContext from 'zustand/context';
|
||||
import { clampPosition, getDimensions } from '../utils';
|
||||
import {
|
||||
ReactFlowState,
|
||||
ConnectionMode,
|
||||
Node,
|
||||
Edge,
|
||||
NodeDimensionUpdate,
|
||||
NodeDiffUpdate,
|
||||
InitD3ZoomPayload,
|
||||
CoordinateExtent,
|
||||
OnConnect,
|
||||
OnConnectStart,
|
||||
OnConnectStop,
|
||||
OnConnectEnd,
|
||||
SetConnectionId,
|
||||
SnapGrid,
|
||||
NodeChange,
|
||||
OnNodesChange,
|
||||
OnEdgesChange,
|
||||
EdgeChange,
|
||||
NodeDimensionChange,
|
||||
Transform,
|
||||
Dimensions,
|
||||
XYPosition,
|
||||
} from '../types';
|
||||
import { getHandleBounds } from '../components/Nodes/utils';
|
||||
import { createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||
@@ -36,7 +23,6 @@ const { Provider, useStore, useStoreApi } = createContext<ReactFlowState>();
|
||||
const createStore = () =>
|
||||
create<ReactFlowState>((set, get) => ({
|
||||
...initialState,
|
||||
|
||||
setNodes: (nodes: Node[]) => {
|
||||
const nodeInternals = createNodeInternals(nodes, get().nodeInternals);
|
||||
|
||||
@@ -158,13 +144,6 @@ const createStore = () =>
|
||||
onEdgesChange?.(edgesToUnselect);
|
||||
}
|
||||
},
|
||||
initD3Zoom: ({ d3Zoom, d3Selection, d3ZoomHandler, transform }: InitD3ZoomPayload) =>
|
||||
set({
|
||||
d3Zoom,
|
||||
d3Selection,
|
||||
d3ZoomHandler,
|
||||
transform,
|
||||
}),
|
||||
setMinZoom: (minZoom: number) => {
|
||||
const { d3Zoom, maxZoom } = get();
|
||||
d3Zoom?.scaleExtent([minZoom, maxZoom]);
|
||||
@@ -183,7 +162,6 @@ const createStore = () =>
|
||||
|
||||
set({ translateExtent });
|
||||
},
|
||||
|
||||
resetSelectedElements: () => {
|
||||
const { nodeInternals, edges, onNodesChange, onEdgesChange } = get();
|
||||
// @TODO: work with nodeInternals instead of converting it to an array
|
||||
@@ -210,33 +188,7 @@ const createStore = () =>
|
||||
nodeInternals: new Map(nodeInternals),
|
||||
});
|
||||
},
|
||||
setUserSelectionActive: (userSelectionActive: boolean) => set({ userSelectionActive }),
|
||||
setNodesSelectionActive: (nodesSelectionActive: boolean) => set({ nodesSelectionActive }),
|
||||
updateTransform: (transform: Transform) => set({ transform }),
|
||||
updateSize: (size: Dimensions) => set({ width: size.width || 500, height: size.height || 500 }),
|
||||
setOnConnect: (onConnect: OnConnect) => set({ onConnect }),
|
||||
setOnConnectStart: (onConnectStart: OnConnectStart) => set({ onConnectStart }),
|
||||
setOnConnectStop: (onConnectStop: OnConnectStop) => set({ onConnectStop }),
|
||||
setOnConnectEnd: (onConnectEnd: OnConnectEnd) => set({ onConnectEnd }),
|
||||
setConnectionPosition: (connectionPosition: XYPosition) => set({ connectionPosition }),
|
||||
setConnectionNodeId: (params: SetConnectionId) => set({ ...params }),
|
||||
setSnapToGrid: (snapToGrid: boolean) => set({ snapToGrid }),
|
||||
setSnapGrid: (snapGrid: SnapGrid) => set({ snapGrid }),
|
||||
setInteractive: (isInteractive: boolean) =>
|
||||
set({
|
||||
nodesDraggable: isInteractive,
|
||||
nodesConnectable: isInteractive,
|
||||
elementsSelectable: isInteractive,
|
||||
}),
|
||||
setNodesDraggable: (nodesDraggable: boolean) => set({ nodesDraggable }),
|
||||
setNodesConnectable: (nodesConnectable: boolean) => set({ nodesConnectable }),
|
||||
setElementsSelectable: (elementsSelectable: boolean) => set({ elementsSelectable }),
|
||||
setMultiSelectionActive: (multiSelectionActive: boolean) => set({ multiSelectionActive }),
|
||||
setConnectionMode: (connectionMode: ConnectionMode) => set({ connectionMode }),
|
||||
setOnNodesChange: (onNodesChange: OnNodesChange) => set({ onNodesChange }),
|
||||
setOnEdgesChange: (onEdgesChange: OnEdgesChange) => set({ onEdgesChange }),
|
||||
reset: () => set({ ...initialState }),
|
||||
setFitViewOnInit: (fitViewOnInit: boolean) => set({ fitViewOnInit }),
|
||||
}));
|
||||
|
||||
export { Provider, useStore, createStore, useStoreApi };
|
||||
|
||||
+6
-42
@@ -1,7 +1,7 @@
|
||||
import { MouseEvent as ReactMouseEvent, ReactNode } from 'react';
|
||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||
|
||||
import { XYPosition, Rect, Transform, CoordinateExtent, Dimensions } from './utils';
|
||||
import { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
|
||||
import { NodeChange, EdgeChange } from './changes';
|
||||
import { Node, NodeInternals, NodeDimensionUpdate, NodeDiffUpdate } from './nodes';
|
||||
import { Edge } from './edges';
|
||||
@@ -90,12 +90,6 @@ export type OnConnectStop = (event: MouseEvent) => void;
|
||||
|
||||
export type OnConnectEnd = (event: MouseEvent) => void;
|
||||
|
||||
export type SetConnectionId = {
|
||||
connectionNodeId: string | null;
|
||||
connectionHandleId: string | null;
|
||||
connectionHandleType: HandleType | null;
|
||||
};
|
||||
|
||||
export enum BackgroundVariant {
|
||||
Lines = 'lines',
|
||||
Dots = 'dots',
|
||||
@@ -143,13 +137,6 @@ export interface ZoomPanHelperFunctions {
|
||||
initialized: boolean;
|
||||
}
|
||||
|
||||
export type InitD3ZoomPayload = {
|
||||
d3Zoom: ZoomBehavior<Element, unknown>;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined>;
|
||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
|
||||
transform: Transform;
|
||||
};
|
||||
|
||||
export type ReactFlowStore = {
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -190,6 +177,11 @@ export type ReactFlowStore = {
|
||||
|
||||
fitViewOnInit: boolean;
|
||||
fitViewOnInitDone: boolean;
|
||||
|
||||
onConnect?: OnConnect;
|
||||
onConnectStart?: OnConnectStart;
|
||||
onConnectStop?: OnConnectStop;
|
||||
onConnectEnd?: OnConnectEnd;
|
||||
};
|
||||
|
||||
export type ReactFlowActions = {
|
||||
@@ -197,43 +189,15 @@ export type ReactFlowActions = {
|
||||
setEdges: (edges: Edge[]) => void;
|
||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
||||
updateNodePosition: (update: NodeDiffUpdate) => void;
|
||||
setNodesSelectionActive: (isActive: boolean) => void;
|
||||
setUserSelectionActive: (isActive: boolean) => void;
|
||||
resetSelectedElements: () => void;
|
||||
unselectNodesAndEdges: () => void;
|
||||
addSelectedNodes: (nodeIds: string[]) => void;
|
||||
addSelectedEdges: (edgeIds: string[]) => void;
|
||||
updateTransform: (transform: Transform) => void;
|
||||
updateSize: (size: Dimensions) => void;
|
||||
initD3Zoom: (payload: InitD3ZoomPayload) => void;
|
||||
setMinZoom: (minZoom: number) => void;
|
||||
setMaxZoom: (maxZoom: number) => void;
|
||||
setTranslateExtent: (translateExtent: CoordinateExtent) => void;
|
||||
setNodeExtent: (nodeExtent: CoordinateExtent) => void;
|
||||
setOnConnect: (onConnectFunction: OnConnect) => void;
|
||||
setOnConnectStart: (onConnectFunction: OnConnectStart) => void;
|
||||
setOnConnectStop: (onConnectFunction: OnConnectStop) => void;
|
||||
setOnConnectEnd: (onConnectFunction: OnConnectEnd) => void;
|
||||
setConnectionPosition: (connectionPosition: XYPosition) => void;
|
||||
setConnectionNodeId: (payload: SetConnectionId) => void;
|
||||
setSnapToGrid: (snapToGrid: boolean) => void;
|
||||
setSnapGrid: (snapGrid: SnapGrid) => void;
|
||||
setInteractive: (isInteractive: boolean) => void;
|
||||
setNodesDraggable: (nodesDraggable: boolean) => void;
|
||||
setNodesConnectable: (nodesConnectable: boolean) => void;
|
||||
setElementsSelectable: (elementsSelectable: boolean) => void;
|
||||
setMultiSelectionActive: (multiSelectionActive: boolean) => void;
|
||||
setConnectionMode: (connectionMode: ConnectionMode) => void;
|
||||
setOnNodesChange: (onNodesChange: OnNodesChange) => void;
|
||||
setOnEdgesChange: (onEdgesChange: OnEdgesChange) => void;
|
||||
|
||||
onConnect?: OnConnect;
|
||||
onConnectStart?: OnConnectStart;
|
||||
onConnectStop?: OnConnectStop;
|
||||
onConnectEnd?: OnConnectEnd;
|
||||
|
||||
reset: () => void;
|
||||
setFitViewOnInit: (fitViewOnInit: boolean) => void;
|
||||
};
|
||||
|
||||
export type ReactFlowState = ReactFlowStore & ReactFlowActions;
|
||||
|
||||
Reference in New Issue
Block a user