chore(useStore): cleanup selectors
This commit is contained in:
@@ -7,7 +7,7 @@ import { useStore } from '../../store';
|
||||
import { getRectOfNodes } from '../../utils/graph';
|
||||
import { getBoundsofRects } from '../../utils';
|
||||
|
||||
import { MiniMapProps, GetMiniMapNodeAttribute, ReactFlowState, Rect } from '../../types';
|
||||
import { MiniMapProps, GetMiniMapNodeAttribute, ReactFlowState } from '../../types';
|
||||
|
||||
declare const window: any;
|
||||
|
||||
@@ -15,9 +15,12 @@ const defaultWidth = 200;
|
||||
const defaultHeight = 150;
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
width: s.width,
|
||||
height: s.height,
|
||||
transform: s.transform,
|
||||
viewBBox: {
|
||||
x: -s.transform[0] / s.transform[2],
|
||||
y: -s.transform[1] / s.transform[2],
|
||||
width: s.width / s.transform[2],
|
||||
height: s.height / s.transform[2],
|
||||
},
|
||||
nodes: Array.from(s.nodeInternals.values()),
|
||||
});
|
||||
|
||||
@@ -33,19 +36,13 @@ const MiniMap = ({
|
||||
nodeStrokeWidth = 2,
|
||||
maskColor = 'rgb(240, 242, 243, 0.7)',
|
||||
}: MiniMapProps) => {
|
||||
const { width: containerWidth, height: containerHeight, transform, nodes } = useStore(selector, shallow);
|
||||
const { viewBBox, nodes } = useStore(selector, shallow);
|
||||
const elementWidth = (style?.width as number) ?? defaultWidth;
|
||||
const elementHeight = (style?.height as number) ?? defaultHeight;
|
||||
const nodeColorFunc = getAttrFunction(nodeColor);
|
||||
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
||||
const nodeClassNameFunc = getAttrFunction(nodeClassName);
|
||||
const viewBB: Rect = {
|
||||
x: -transform[0] / transform[2],
|
||||
y: -transform[1] / transform[2],
|
||||
width: containerWidth / transform[2],
|
||||
height: containerHeight / transform[2],
|
||||
};
|
||||
const boundingRect = nodes.length > 0 ? getBoundsofRects(getRectOfNodes(nodes), viewBB) : viewBB;
|
||||
const boundingRect = nodes.length > 0 ? getBoundsofRects(getRectOfNodes(nodes), viewBBox) : viewBBox;
|
||||
const scaledWidth = boundingRect.width / elementWidth;
|
||||
const scaledHeight = boundingRect.height / elementHeight;
|
||||
const viewScale = Math.max(scaledWidth, scaledHeight);
|
||||
@@ -89,7 +86,7 @@ const MiniMap = ({
|
||||
<path
|
||||
className="react-flow__minimap-mask"
|
||||
d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
||||
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`}
|
||||
M${viewBBox.x},${viewBBox.y}h${viewBBox.width}v${viewBBox.height}h${-viewBBox.width}z`}
|
||||
fill={maskColor}
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
|
||||
@@ -1,58 +1,54 @@
|
||||
import React, { useRef, CSSProperties } from 'react';
|
||||
import React, { CSSProperties, useCallback } from 'react';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../store';
|
||||
import { getBezierPath } from '../Edges/BezierEdge';
|
||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||
import { ConnectionLineType, ConnectionLineComponent, HandleType, Node, ReactFlowState, Position } from '../../types';
|
||||
import { ConnectionLineType, ConnectionLineComponent, HandleType, Position } from '../../types';
|
||||
import { getSimpleBezierPath } from '../Edges/SimpleBezierEdge';
|
||||
import { internalsSymbol } from '../../utils';
|
||||
|
||||
interface ConnectionLineProps {
|
||||
connectionNodeId: string;
|
||||
connectionHandleId: string | null;
|
||||
connectionHandleType: HandleType;
|
||||
connectionPositionX: number;
|
||||
connectionPositionY: number;
|
||||
connectionLineType: ConnectionLineType;
|
||||
isConnectable: boolean;
|
||||
connectionLineStyle?: CSSProperties;
|
||||
CustomConnectionLineComponent?: ConnectionLineComponent;
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({ nodeInternals: s.nodeInternals, transform: s.transform });
|
||||
|
||||
export default ({
|
||||
connectionNodeId,
|
||||
connectionHandleId,
|
||||
connectionHandleType,
|
||||
connectionLineStyle,
|
||||
connectionPositionX,
|
||||
connectionPositionY,
|
||||
connectionLineType = ConnectionLineType.Bezier,
|
||||
isConnectable,
|
||||
CustomConnectionLineComponent,
|
||||
}: ConnectionLineProps) => {
|
||||
const nodeId = connectionNodeId;
|
||||
const handleId = connectionHandleId;
|
||||
const { fromNode, handleId, toX, toY } = useStore(
|
||||
useCallback(
|
||||
(s) => ({
|
||||
fromNode: s.nodeInternals.get(connectionNodeId),
|
||||
handleId: s.connectionHandleId,
|
||||
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
|
||||
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
|
||||
}),
|
||||
[connectionNodeId]
|
||||
),
|
||||
shallow
|
||||
);
|
||||
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
|
||||
|
||||
const { nodeInternals, transform } = useStore(selector, shallow);
|
||||
const fromNode = useRef<Node | undefined>(nodeInternals.get(nodeId));
|
||||
const fromHandleBounds = fromNode.current?.[internalsSymbol]?.handleBounds;
|
||||
|
||||
if (!fromNode.current || !isConnectable || !fromHandleBounds?.[connectionHandleType]) {
|
||||
if (!fromNode || !isConnectable || !fromHandleBounds?.[connectionHandleType]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleBound = fromHandleBounds[connectionHandleType];
|
||||
const fromHandle = handleId ? handleBound?.find((d) => d.id === handleId) : handleBound?.[0];
|
||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.current?.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.current?.height ?? 0;
|
||||
const fromX = (fromNode.current.positionAbsolute?.x || 0) + fromHandleX;
|
||||
const fromY = (fromNode.current.positionAbsolute?.y || 0) + fromHandleY;
|
||||
|
||||
const toX = (connectionPositionX - transform[0]) / transform[2];
|
||||
const toY = (connectionPositionY - transform[1]) / transform[2];
|
||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode?.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode?.height ?? 0;
|
||||
const fromX = (fromNode?.positionAbsolute?.x || 0) + fromHandleX;
|
||||
const fromY = (fromNode?.positionAbsolute?.y || 0) + fromHandleY;
|
||||
|
||||
const fromPosition = fromHandle?.position;
|
||||
|
||||
@@ -114,10 +110,10 @@ export default ({
|
||||
targetPosition={targetPosition}
|
||||
connectionLineType={connectionLineType}
|
||||
connectionLineStyle={connectionLineStyle}
|
||||
fromNode={fromNode.current}
|
||||
fromNode={fromNode}
|
||||
fromHandle={fromHandle}
|
||||
// backward compatibility, mark as deprecated?
|
||||
sourceNode={fromNode.current}
|
||||
sourceNode={fromNode}
|
||||
sourceHandle={fromHandle}
|
||||
/>
|
||||
</g>
|
||||
|
||||
@@ -84,14 +84,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
||||
? (evt: MouseEvent): void => onEdgeUpdateEnd(evt, edge, handleType)
|
||||
: undefined;
|
||||
|
||||
const onConnectEdge = (connection: Connection) => {
|
||||
const { edges } = store.getState();
|
||||
const edge = edges.find((e) => e.id === id);
|
||||
|
||||
if (edge && onEdgeUpdate) {
|
||||
onEdgeUpdate(edge, connection);
|
||||
}
|
||||
};
|
||||
const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection);
|
||||
|
||||
handleMouseDown({
|
||||
event,
|
||||
|
||||
@@ -16,7 +16,6 @@ export type HandleComponentProps = HandleProps & Omit<HTMLAttributes<HTMLDivElem
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
connectionStartHandle: s.connectionStartHandle,
|
||||
connectOnClick: s.connectOnClick,
|
||||
hasDefaultEdges: s.hasDefaultEdges,
|
||||
});
|
||||
|
||||
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
@@ -37,13 +36,13 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
) => {
|
||||
const store = useStoreApi();
|
||||
const nodeId = useContext(NodeIdContext) as string;
|
||||
const { connectionStartHandle, connectOnClick, hasDefaultEdges } = useStore(selector, shallow);
|
||||
const { connectionStartHandle, connectOnClick } = useStore(selector, shallow);
|
||||
|
||||
const handleId = id || null;
|
||||
const isTarget = type === 'target';
|
||||
|
||||
const onConnectExtended = (params: Connection) => {
|
||||
const { defaultEdgeOptions, onConnect: onConnectAction } = store.getState();
|
||||
const { defaultEdgeOptions, onConnect: onConnectAction, hasDefaultEdges } = store.getState();
|
||||
|
||||
const edgeParams = {
|
||||
...defaultEdgeOptions,
|
||||
@@ -104,28 +103,26 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
store.setState({ connectionStartHandle: null });
|
||||
};
|
||||
|
||||
const handleClasses = cc([
|
||||
'react-flow__handle',
|
||||
`react-flow__handle-${position}`,
|
||||
'nodrag',
|
||||
className,
|
||||
{
|
||||
source: !isTarget,
|
||||
target: isTarget,
|
||||
connectable: isConnectable,
|
||||
connecting:
|
||||
connectionStartHandle?.nodeId === nodeId &&
|
||||
connectionStartHandle?.handleId === handleId &&
|
||||
connectionStartHandle?.type === type,
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-handleid={handleId}
|
||||
data-nodeid={nodeId}
|
||||
data-handlepos={position}
|
||||
className={handleClasses}
|
||||
className={cc([
|
||||
'react-flow__handle',
|
||||
`react-flow__handle-${position}`,
|
||||
'nodrag',
|
||||
className,
|
||||
{
|
||||
source: !isTarget,
|
||||
target: isTarget,
|
||||
connectable: isConnectable,
|
||||
connecting:
|
||||
connectionStartHandle?.nodeId === nodeId &&
|
||||
connectionStartHandle?.handleId === handleId &&
|
||||
connectionStartHandle?.type === type,
|
||||
},
|
||||
])}
|
||||
onMouseDown={onMouseDownHandler}
|
||||
onClick={connectOnClick ? onClick : undefined}
|
||||
ref={ref}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import React, { useEffect, useRef, memo, ComponentType, MouseEvent } from 'react';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import { useStoreApi } from '../../store';
|
||||
import { Provider } from '../../contexts/NodeIdContext';
|
||||
import { NodeProps, WrapNodeProps, ReactFlowState } from '../../types';
|
||||
import { NodeProps, WrapNodeProps } from '../../types';
|
||||
import useDrag from '../../hooks/useDrag';
|
||||
import { getMouseHandler, handleNodeClick } from './utils';
|
||||
|
||||
const selector = (s: ReactFlowState) => s.updateNodeDimensions;
|
||||
|
||||
export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
const NodeWrapper = ({
|
||||
id,
|
||||
@@ -23,9 +21,6 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
onMouseLeave,
|
||||
onContextMenu,
|
||||
onDoubleClick,
|
||||
onDragStart,
|
||||
onDrag,
|
||||
onDragStop,
|
||||
style,
|
||||
className,
|
||||
isDraggable,
|
||||
@@ -44,7 +39,6 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
initialized,
|
||||
}: WrapNodeProps) => {
|
||||
const store = useStoreApi();
|
||||
const updateNodeDimensions = useStore(selector);
|
||||
const nodeRef = useRef<HTMLDivElement>(null);
|
||||
const prevSourcePosition = useRef(sourcePosition);
|
||||
const prevTargetPosition = useRef(targetPosition);
|
||||
@@ -96,14 +90,11 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
if (targetPosChanged) {
|
||||
prevTargetPosition.current = targetPosition;
|
||||
}
|
||||
updateNodeDimensions([{ id, nodeElement: nodeRef.current, forceUpdate: true }]);
|
||||
store.getState().updateNodeDimensions([{ id, nodeElement: nodeRef.current, forceUpdate: true }]);
|
||||
}
|
||||
}, [id, type, sourcePosition, targetPosition]);
|
||||
|
||||
const dragging = useDrag({
|
||||
onStart: onDragStart,
|
||||
onDrag: onDrag,
|
||||
onStop: onDragStop,
|
||||
nodeRef,
|
||||
disabled: hidden || !isDraggable,
|
||||
noDragClassName,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* made a selection with on or several nodes
|
||||
*/
|
||||
|
||||
import React, { memo, useCallback, useRef, MouseEvent } from 'react';
|
||||
import React, { memo, useRef, MouseEvent } from 'react';
|
||||
import cc from 'classcat';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
@@ -13,48 +13,22 @@ import { getRectOfNodes } from '../../utils/graph';
|
||||
import useDrag from '../../hooks/useDrag';
|
||||
|
||||
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;
|
||||
noPanClassName?: string;
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
transform: s.transform,
|
||||
transformString: `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`,
|
||||
userSelectionActive: s.userSelectionActive,
|
||||
...getRectOfNodes(Array.from(s.nodeInternals.values()).filter((n) => n.selected)),
|
||||
});
|
||||
|
||||
const bboxSelector = (s: ReactFlowState) => {
|
||||
const selectedNodes = Array.from(s.nodeInternals.values()).filter((n) => n.selected);
|
||||
return getRectOfNodes(selectedNodes);
|
||||
};
|
||||
|
||||
function useGetMemoizedHandler(handler?: (event: MouseEvent, nodes: Node[]) => void) {
|
||||
return useCallback((event: MouseEvent, _: Node, nodes: Node[]) => handler?.(event, nodes), [handler]);
|
||||
}
|
||||
|
||||
function NodesSelection({
|
||||
onSelectionDragStart,
|
||||
onSelectionDrag,
|
||||
onSelectionDragStop,
|
||||
onSelectionContextMenu,
|
||||
noPanClassName,
|
||||
}: NodesSelectionProps) {
|
||||
function NodesSelection({ onSelectionContextMenu, noPanClassName }: NodesSelectionProps) {
|
||||
const store = useStoreApi();
|
||||
const { transform, userSelectionActive } = useStore(selector, shallow);
|
||||
const { width, height, x: left, y: top } = useStore(bboxSelector, shallow);
|
||||
const { transformString, userSelectionActive, width, height, x: top, y: left } = useStore(selector, shallow);
|
||||
const nodeRef = useRef(null);
|
||||
|
||||
// it's important that these handlers are memoized to avoid multiple creation of d3 drag handler
|
||||
const onStart = useGetMemoizedHandler(onSelectionDragStart);
|
||||
const onDrag = useGetMemoizedHandler(onSelectionDrag);
|
||||
const onStop = useGetMemoizedHandler(onSelectionDragStop);
|
||||
|
||||
useDrag({
|
||||
onStart,
|
||||
onDrag,
|
||||
onStop,
|
||||
nodeRef,
|
||||
});
|
||||
|
||||
@@ -73,7 +47,7 @@ function NodesSelection({
|
||||
<div
|
||||
className={cc(['react-flow__nodesselection', 'react-flow__container', noPanClassName])}
|
||||
style={{
|
||||
transform: `translate(${transform[0]}px,${transform[1]}px) scale(${transform[2]})`,
|
||||
transform: transformString,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -3,56 +3,46 @@ import { SetState } from 'zustand';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import {
|
||||
Node,
|
||||
Edge,
|
||||
ReactFlowState,
|
||||
OnConnect,
|
||||
OnConnectStart,
|
||||
OnConnectStop,
|
||||
OnConnectEnd,
|
||||
CoordinateExtent,
|
||||
OnNodesChange,
|
||||
OnEdgesChange,
|
||||
ConnectionMode,
|
||||
SnapGrid,
|
||||
DefaultEdgeOptions,
|
||||
FitViewOptions,
|
||||
OnNodesDelete,
|
||||
OnEdgesDelete,
|
||||
} from '../../types';
|
||||
import { Node, Edge, ReactFlowState, CoordinateExtent, ReactFlowProps } from '../../types';
|
||||
|
||||
interface StoreUpdaterProps {
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
defaultNodes?: Node[];
|
||||
defaultEdges?: Edge[];
|
||||
onConnect?: OnConnect;
|
||||
onConnectStart?: OnConnectStart;
|
||||
onConnectStop?: OnConnectStop;
|
||||
onConnectEnd?: OnConnectEnd;
|
||||
onClickConnectStart?: OnConnectStart;
|
||||
onClickConnectStop?: OnConnectStop;
|
||||
onClickConnectEnd?: OnConnectEnd;
|
||||
nodesDraggable?: boolean;
|
||||
nodesConnectable?: boolean;
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
nodeExtent?: CoordinateExtent;
|
||||
onNodesChange?: OnNodesChange;
|
||||
onEdgesChange?: OnEdgesChange;
|
||||
elementsSelectable?: boolean;
|
||||
connectionMode?: ConnectionMode;
|
||||
snapToGrid?: boolean;
|
||||
snapGrid?: SnapGrid;
|
||||
translateExtent?: CoordinateExtent;
|
||||
connectOnClick: boolean;
|
||||
defaultEdgeOptions?: DefaultEdgeOptions;
|
||||
fitView?: boolean;
|
||||
fitViewOptions?: FitViewOptions;
|
||||
onNodesDelete?: OnNodesDelete;
|
||||
onEdgesDelete?: OnEdgesDelete;
|
||||
}
|
||||
type StoreUpdaterProps = Pick<
|
||||
ReactFlowProps,
|
||||
| 'nodes'
|
||||
| 'edges'
|
||||
| 'defaultNodes'
|
||||
| 'defaultEdges'
|
||||
| 'onConnect'
|
||||
| 'onConnectStart'
|
||||
| 'onConnectStop'
|
||||
| 'onConnectEnd'
|
||||
| 'onClickConnectStart'
|
||||
| 'onClickConnectStop'
|
||||
| 'onClickConnectEnd'
|
||||
| 'nodesDraggable'
|
||||
| 'nodesConnectable'
|
||||
| 'minZoom'
|
||||
| 'maxZoom'
|
||||
| 'nodeExtent'
|
||||
| 'onNodesChange'
|
||||
| 'onEdgesChange'
|
||||
| 'elementsSelectable'
|
||||
| 'connectionMode'
|
||||
| 'snapToGrid'
|
||||
| 'snapGrid'
|
||||
| 'translateExtent'
|
||||
| 'connectOnClick'
|
||||
| 'defaultEdgeOptions'
|
||||
| 'fitView'
|
||||
| 'fitViewOptions'
|
||||
| 'onNodesDelete'
|
||||
| 'onEdgesDelete'
|
||||
| 'onNodeDragStart'
|
||||
| 'onNodeDrag'
|
||||
| 'onNodeDragStop'
|
||||
| 'onSelectionDragStart'
|
||||
| 'onSelectionDrag'
|
||||
| 'onSelectionDragStop'
|
||||
>;
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
setNodes: s.setNodes,
|
||||
@@ -112,6 +102,12 @@ const StoreUpdater = ({
|
||||
fitViewOptions,
|
||||
onNodesDelete,
|
||||
onEdgesDelete,
|
||||
onNodeDrag,
|
||||
onNodeDragStart,
|
||||
onNodeDragStop,
|
||||
onSelectionDrag,
|
||||
onSelectionDragStart,
|
||||
onSelectionDragStop,
|
||||
}: StoreUpdaterProps) => {
|
||||
const {
|
||||
setNodes,
|
||||
@@ -154,6 +150,12 @@ const StoreUpdater = ({
|
||||
useDirectStoreUpdater('fitViewOnInitOptions', fitViewOptions, store.setState);
|
||||
useDirectStoreUpdater('onNodesDelete', onNodesDelete, store.setState);
|
||||
useDirectStoreUpdater('onEdgesDelete', onEdgesDelete, store.setState);
|
||||
useDirectStoreUpdater('onNodeDrag', onNodeDrag, store.setState);
|
||||
useDirectStoreUpdater('onNodeDragStart', onNodeDragStart, store.setState);
|
||||
useDirectStoreUpdater('onNodeDragStop', onNodeDragStop, store.setState);
|
||||
useDirectStoreUpdater('onSelectionDrag', onSelectionDrag, store.setState);
|
||||
useDirectStoreUpdater('onSelectionDragStart', onSelectionDragStart, store.setState);
|
||||
useDirectStoreUpdater('onSelectionDragStop', onSelectionDragStop, store.setState);
|
||||
|
||||
useStoreUpdater<Node[]>(nodes, setNodes);
|
||||
useStoreUpdater<Edge[]>(edges, setEdges);
|
||||
|
||||
@@ -45,9 +45,7 @@ interface EdgeRendererProps {
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
connectionNodeId: s.connectionNodeId,
|
||||
connectionHandleId: s.connectionHandleId,
|
||||
connectionHandleType: s.connectionHandleType,
|
||||
connectionPosition: s.connectionPosition,
|
||||
nodesConnectable: s.nodesConnectable,
|
||||
elementsSelectable: s.elementsSelectable,
|
||||
width: s.width,
|
||||
@@ -59,9 +57,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||
const {
|
||||
connectionNodeId,
|
||||
connectionHandleId,
|
||||
connectionHandleType,
|
||||
connectionPosition,
|
||||
nodesConnectable,
|
||||
elementsSelectable,
|
||||
width,
|
||||
@@ -201,10 +197,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||
>
|
||||
<ConnectionLine
|
||||
connectionNodeId={connectionNodeId!}
|
||||
connectionHandleId={connectionHandleId}
|
||||
connectionHandleType={connectionHandleType!}
|
||||
connectionPositionX={connectionPosition.x}
|
||||
connectionPositionY={connectionPosition.y}
|
||||
connectionLineStyle={connectionLineStyle}
|
||||
connectionLineType={connectionLineType}
|
||||
isConnectable={nodesConnectable}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { memo, ReactNode, WheelEvent, MouseEvent } from 'react';
|
||||
import shallow from 'zustand/shallow';
|
||||
|
||||
import { useStore, useStoreApi } from '../../store';
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
||||
@@ -27,10 +26,7 @@ export type FlowRendererProps = Omit<
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
resetSelectedElements: s.resetSelectedElements,
|
||||
nodesSelectionActive: s.nodesSelectionActive,
|
||||
});
|
||||
const selector = (s: ReactFlowState) => s.nodesSelectionActive;
|
||||
|
||||
const FlowRenderer = ({
|
||||
children,
|
||||
@@ -55,27 +51,24 @@ const FlowRenderer = ({
|
||||
defaultPosition,
|
||||
defaultZoom,
|
||||
preventScrolling,
|
||||
onSelectionDragStart,
|
||||
onSelectionDrag,
|
||||
onSelectionDragStop,
|
||||
onSelectionContextMenu,
|
||||
noWheelClassName,
|
||||
noPanClassName,
|
||||
}: FlowRendererProps) => {
|
||||
const store = useStoreApi();
|
||||
const { resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow);
|
||||
const nodesSelectionActive = useStore(selector);
|
||||
const selectionKeyPressed = useKeyPress(selectionKeyCode);
|
||||
|
||||
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
||||
|
||||
const onClick = (event: MouseEvent) => {
|
||||
onPaneClick?.(event);
|
||||
resetSelectedElements();
|
||||
|
||||
store.getState().resetSelectedElements();
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
};
|
||||
const onContextMenu = (event: MouseEvent) => onPaneContextMenu?.(event);
|
||||
const onWheel = (event: WheelEvent) => onPaneScroll?.(event);
|
||||
|
||||
const onContextMenu = onPaneContextMenu ? (event: MouseEvent) => onPaneContextMenu(event) : undefined;
|
||||
const onWheel = onPaneScroll ? (event: WheelEvent) => onPaneScroll(event) : undefined;
|
||||
|
||||
return (
|
||||
<ZoomPane
|
||||
@@ -101,13 +94,7 @@ const FlowRenderer = ({
|
||||
{children}
|
||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||
{nodesSelectionActive && (
|
||||
<NodesSelection
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionDrag={onSelectionDrag}
|
||||
onSelectionDragStop={onSelectionDragStop}
|
||||
onSelectionContextMenu={onSelectionContextMenu}
|
||||
noPanClassName={noPanClassName}
|
||||
/>
|
||||
<NodesSelection onSelectionContextMenu={onSelectionContextMenu} noPanClassName={noPanClassName} />
|
||||
)}
|
||||
<div
|
||||
className="react-flow__pane react-flow__container"
|
||||
|
||||
@@ -40,12 +40,6 @@ const GraphView = ({
|
||||
onNodeMouseMove,
|
||||
onNodeMouseLeave,
|
||||
onNodeContextMenu,
|
||||
onNodeDragStart,
|
||||
onNodeDrag,
|
||||
onNodeDragStop,
|
||||
onSelectionDragStart,
|
||||
onSelectionDrag,
|
||||
onSelectionDragStop,
|
||||
onSelectionContextMenu,
|
||||
connectionLineType,
|
||||
connectionLineStyle,
|
||||
@@ -110,9 +104,6 @@ const GraphView = ({
|
||||
panOnDrag={panOnDrag}
|
||||
defaultPosition={defaultPosition}
|
||||
defaultZoom={defaultZoom}
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionDrag={onSelectionDrag}
|
||||
onSelectionDragStop={onSelectionDragStop}
|
||||
onSelectionContextMenu={onSelectionContextMenu}
|
||||
preventScrolling={preventScrolling}
|
||||
noDragClassName={noDragClassName}
|
||||
@@ -150,9 +141,6 @@ const GraphView = ({
|
||||
onNodeMouseMove={onNodeMouseMove}
|
||||
onNodeMouseLeave={onNodeMouseLeave}
|
||||
onNodeContextMenu={onNodeContextMenu}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onNodeDrag={onNodeDrag}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
selectNodesOnDrag={selectNodesOnDrag}
|
||||
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
||||
noPanClassName={noPanClassName}
|
||||
|
||||
@@ -3,14 +3,7 @@ import shallow from 'zustand/shallow';
|
||||
|
||||
import useVisibleNodes from '../../hooks/useVisibleNodes';
|
||||
import { useStore } from '../../store';
|
||||
import {
|
||||
NodeDragHandler,
|
||||
NodeMouseHandler,
|
||||
NodeTypesWrapped,
|
||||
Position,
|
||||
ReactFlowState,
|
||||
WrapNodeProps,
|
||||
} from '../../types';
|
||||
import { NodeMouseHandler, NodeTypesWrapped, Position, ReactFlowState, WrapNodeProps } from '../../types';
|
||||
import { internalsSymbol } from '../../utils';
|
||||
|
||||
interface NodeRendererProps {
|
||||
@@ -22,9 +15,6 @@ interface NodeRendererProps {
|
||||
onNodeMouseMove?: NodeMouseHandler;
|
||||
onNodeMouseLeave?: NodeMouseHandler;
|
||||
onNodeContextMenu?: NodeMouseHandler;
|
||||
onNodeDragStart?: NodeDragHandler;
|
||||
onNodeDrag?: NodeDragHandler;
|
||||
onNodeDragStop?: NodeDragHandler;
|
||||
onlyRenderVisibleElements: boolean;
|
||||
noPanClassName: string;
|
||||
noDragClassName: string;
|
||||
@@ -109,9 +99,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
onMouseLeave={props.onNodeMouseLeave}
|
||||
onContextMenu={props.onNodeContextMenu}
|
||||
onDoubleClick={props.onNodeDoubleClick}
|
||||
onDragStart={props.onNodeDragStart}
|
||||
onDrag={props.onNodeDrag}
|
||||
onDragStop={props.onNodeDragStop}
|
||||
selected={!!node.selected}
|
||||
isDraggable={isDraggable}
|
||||
isSelectable={isSelectable}
|
||||
|
||||
@@ -165,9 +165,6 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
onNodeMouseLeave={onNodeMouseLeave}
|
||||
onNodeContextMenu={onNodeContextMenu}
|
||||
onNodeDoubleClick={onNodeDoubleClick}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDrag={onNodeDrag}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
nodeTypes={nodeTypesWrapped}
|
||||
edgeTypes={edgeTypesWrapped}
|
||||
connectionLineType={connectionLineType}
|
||||
@@ -193,9 +190,6 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
onPaneClick={onPaneClick}
|
||||
onPaneScroll={onPaneScroll}
|
||||
onPaneContextMenu={onPaneContextMenu}
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionDrag={onSelectionDrag}
|
||||
onSelectionDragStop={onSelectionDragStop}
|
||||
onSelectionContextMenu={onSelectionContextMenu}
|
||||
onEdgeUpdate={onEdgeUpdate}
|
||||
onEdgeContextMenu={onEdgeContextMenu}
|
||||
@@ -243,6 +237,12 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
fitViewOptions={fitViewOptions}
|
||||
onNodesDelete={onNodesDelete}
|
||||
onEdgesDelete={onEdgesDelete}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDrag={onNodeDrag}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onSelectionDrag={onSelectionDrag}
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionDragStop={onSelectionDragStop}
|
||||
/>
|
||||
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
||||
{children}
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { ReactNode } from 'react';
|
||||
import { useStore } from '../../store';
|
||||
import { ReactFlowState } from '../../types';
|
||||
|
||||
const selector = (s: ReactFlowState) => s.transform;
|
||||
const selector = (s: ReactFlowState) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
|
||||
|
||||
type ViewportProps = {
|
||||
children: ReactNode;
|
||||
@@ -13,10 +13,7 @@ function Viewport({ children }: ViewportProps) {
|
||||
const transform = useStore(selector);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="react-flow__viewport react-flow__container"
|
||||
style={{ transform: `translate(${transform[0]}px,${transform[1]}px) scale(${transform[2]})` }}
|
||||
>
|
||||
<div className="react-flow__viewport react-flow__container" style={{ transform: transform }}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { select } from 'd3-selection';
|
||||
|
||||
import { useStoreApi } from '../../store';
|
||||
import { pointToRendererPoint } from '../../utils/graph';
|
||||
import { NodeDragItem, NodeDragHandler } from '../../types';
|
||||
import { NodeDragItem, Node, SelectionDragHandler } from '../../types';
|
||||
import { getDragItems, getEventHandlerParams, hasSelector, updatePosition } from './utils';
|
||||
import { handleNodeClick } from '../../components/Nodes/utils';
|
||||
|
||||
@@ -13,9 +13,6 @@ export type UseDragData = { dx: number; dy: number };
|
||||
|
||||
type UseDragParams = {
|
||||
nodeRef: RefObject<Element>;
|
||||
onStart?: NodeDragHandler;
|
||||
onDrag?: NodeDragHandler;
|
||||
onStop?: NodeDragHandler;
|
||||
disabled?: boolean;
|
||||
noDragClassName?: string;
|
||||
handleSelector?: string;
|
||||
@@ -24,10 +21,11 @@ type UseDragParams = {
|
||||
selectNodesOnDrag?: boolean;
|
||||
};
|
||||
|
||||
function wrapSelectionDragFunc(selectionFunc?: SelectionDragHandler) {
|
||||
return (event: MouseEvent, _: Node, nodes: Node[]) => selectionFunc?.(event, nodes);
|
||||
}
|
||||
|
||||
function useDrag({
|
||||
onStart,
|
||||
onDrag,
|
||||
onStop,
|
||||
nodeRef,
|
||||
disabled = false,
|
||||
noDragClassName,
|
||||
@@ -60,7 +58,15 @@ function useDrag({
|
||||
} else {
|
||||
const dragHandler = drag()
|
||||
.on('start', (event: UseDragEvent) => {
|
||||
const { nodeInternals, multiSelectionActive, unselectNodesAndEdges } = store.getState();
|
||||
const {
|
||||
nodeInternals,
|
||||
multiSelectionActive,
|
||||
unselectNodesAndEdges,
|
||||
onNodeDragStart,
|
||||
onSelectionDragStart,
|
||||
} = store.getState();
|
||||
|
||||
const onStart = nodeId ? onNodeDragStart : wrapSelectionDragFunc(onSelectionDragStart);
|
||||
|
||||
if (!selectNodesOnDrag && !multiSelectionActive && nodeId) {
|
||||
if (!nodeInternals.get(nodeId)?.selected) {
|
||||
@@ -90,7 +96,7 @@ function useDrag({
|
||||
}
|
||||
})
|
||||
.on('drag', (event: UseDragEvent) => {
|
||||
const { updateNodePositions, nodeInternals, nodeExtent } = store.getState();
|
||||
const { updateNodePositions, nodeInternals, nodeExtent, onNodeDrag, onSelectionDrag } = store.getState();
|
||||
const pointerPos = getPointerPosition(event);
|
||||
|
||||
// skip events without movement
|
||||
@@ -100,6 +106,8 @@ function useDrag({
|
||||
updatePosition(n, pointerPos, nodeInternals, nodeExtent)
|
||||
);
|
||||
|
||||
const onDrag = nodeId ? onNodeDrag : wrapSelectionDragFunc(onSelectionDrag);
|
||||
|
||||
updateNodePositions(dragItems.current, true, true);
|
||||
setDragging(true);
|
||||
|
||||
@@ -116,6 +124,9 @@ function useDrag({
|
||||
event.on('end', (event) => {
|
||||
setDragging(false);
|
||||
if (dragItems.current) {
|
||||
const { updateNodePositions, nodeInternals, onNodeDragStop, onSelectionDragStop } = store.getState();
|
||||
const onStop = nodeId ? onNodeDragStop : wrapSelectionDragFunc(onSelectionDragStop);
|
||||
|
||||
updateNodePositions(dragItems.current, false, false);
|
||||
|
||||
if (onStop) {
|
||||
@@ -147,9 +158,6 @@ function useDrag({
|
||||
}
|
||||
}
|
||||
}, [
|
||||
onStart,
|
||||
onDrag,
|
||||
onStop,
|
||||
nodeRef,
|
||||
disabled,
|
||||
noDragClassName,
|
||||
|
||||
@@ -8,11 +8,10 @@ import { ReactFlowState } from '../types';
|
||||
function useVisibleNodes(onlyRenderVisible: boolean) {
|
||||
const nodes = useStore(
|
||||
useCallback(
|
||||
(s: ReactFlowState) => {
|
||||
return onlyRenderVisible
|
||||
(s: ReactFlowState) =>
|
||||
onlyRenderVisible
|
||||
? getNodesInside(s.nodeInternals, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
|
||||
: Array.from(s.nodeInternals.values());
|
||||
},
|
||||
: Array.from(s.nodeInternals.values()),
|
||||
[onlyRenderVisible]
|
||||
)
|
||||
);
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
OnMoveEnd,
|
||||
NodeDragHandler,
|
||||
NodeMouseHandler,
|
||||
SelectionDragHandler,
|
||||
} from '.';
|
||||
import { HandleType } from './handles';
|
||||
|
||||
@@ -73,9 +74,9 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
|
||||
onMoveStart?: OnMoveStart;
|
||||
onMoveEnd?: OnMoveEnd;
|
||||
onSelectionChange?: OnSelectionChangeFunc;
|
||||
onSelectionDragStart?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||
onSelectionDrag?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||
onSelectionDragStop?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||
onSelectionDragStart?: SelectionDragHandler;
|
||||
onSelectionDrag?: SelectionDragHandler;
|
||||
onSelectionDragStop?: SelectionDragHandler;
|
||||
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||
onPaneScroll?: (event?: WheelEvent) => void;
|
||||
onPaneClick?: (event: ReactMouseEvent) => void;
|
||||
|
||||
@@ -3,7 +3,16 @@ import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||
|
||||
import { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
|
||||
import { NodeChange, EdgeChange } from './changes';
|
||||
import { Node, NodeInternals, NodeDimensionUpdate, NodeProps, WrapNodeProps, NodeDragItem } from './nodes';
|
||||
import {
|
||||
Node,
|
||||
NodeInternals,
|
||||
NodeDimensionUpdate,
|
||||
NodeProps,
|
||||
WrapNodeProps,
|
||||
NodeDragItem,
|
||||
NodeDragHandler,
|
||||
SelectionDragHandler,
|
||||
} from './nodes';
|
||||
import { Edge, EdgeProps, WrapEdgeProps } from './edges';
|
||||
import { HandleType, StartHandle } from './handles';
|
||||
import { DefaultEdgeOptions } from '.';
|
||||
@@ -165,6 +174,14 @@ export type ReactFlowStore = {
|
||||
|
||||
connectionStartHandle: StartHandle | null;
|
||||
|
||||
onNodeDragStart?: NodeDragHandler;
|
||||
onNodeDrag?: NodeDragHandler;
|
||||
onNodeDragStop?: NodeDragHandler;
|
||||
|
||||
onSelectionDragStart?: SelectionDragHandler;
|
||||
onSelectionDrag?: SelectionDragHandler;
|
||||
onSelectionDragStop?: SelectionDragHandler;
|
||||
|
||||
onConnect?: OnConnect;
|
||||
onConnectStart?: OnConnectStart;
|
||||
onConnectStop?: OnConnectStop;
|
||||
|
||||
@@ -55,6 +55,7 @@ export interface NodeProps<T = any> {
|
||||
|
||||
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
|
||||
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
|
||||
export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||
|
||||
export interface WrapNodeProps<T = any> {
|
||||
id: string;
|
||||
@@ -74,9 +75,6 @@ export interface WrapNodeProps<T = any> {
|
||||
onMouseMove?: NodeMouseHandler;
|
||||
onMouseLeave?: NodeMouseHandler;
|
||||
onContextMenu?: NodeMouseHandler;
|
||||
onDragStart?: NodeDragHandler;
|
||||
onDrag?: NodeDragHandler;
|
||||
onDragStop?: NodeDragHandler;
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
sourcePosition: Position;
|
||||
|
||||
Reference in New Issue
Block a user