;
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
(nodes, setNodes);
useStoreUpdater(edges, setEdges);
diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx
index 98f90b1d..b5f47151 100644
--- a/src/container/EdgeRenderer/index.tsx
+++ b/src/container/EdgeRenderer/index.tsx
@@ -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) => {
>
({
- 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 (
{nodesSelectionActive && (
-
+
)}
{
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}
diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx
index 3e815045..e5fc446b 100644
--- a/src/container/ReactFlow/index.tsx
+++ b/src/container/ReactFlow/index.tsx
@@ -165,9 +165,6 @@ const ReactFlow = forwardRef
(
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(
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(
fitViewOptions={fitViewOptions}
onNodesDelete={onNodesDelete}
onEdgesDelete={onEdgesDelete}
+ onNodeDragStart={onNodeDragStart}
+ onNodeDrag={onNodeDrag}
+ onNodeDragStop={onNodeDragStop}
+ onSelectionDrag={onSelectionDrag}
+ onSelectionDragStart={onSelectionDragStart}
+ onSelectionDragStop={onSelectionDragStop}
/>
{onSelectionChange && }
{children}
diff --git a/src/container/Viewport/index.tsx b/src/container/Viewport/index.tsx
index 590b7516..ec6877bd 100644
--- a/src/container/Viewport/index.tsx
+++ b/src/container/Viewport/index.tsx
@@ -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 (
-
+
{children}
);
diff --git a/src/hooks/useDrag/index.ts b/src/hooks/useDrag/index.ts
index b14ad14d..da626085 100644
--- a/src/hooks/useDrag/index.ts
+++ b/src/hooks/useDrag/index.ts
@@ -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
;
- 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,
diff --git a/src/hooks/useVisibleNodes.ts b/src/hooks/useVisibleNodes.ts
index ebda8953..8c8b7190 100644
--- a/src/hooks/useVisibleNodes.ts
+++ b/src/hooks/useVisibleNodes.ts
@@ -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]
)
);
diff --git a/src/types/component-props.ts b/src/types/component-props.ts
index 200495eb..75261021 100644
--- a/src/types/component-props.ts
+++ b/src/types/component-props.ts
@@ -38,6 +38,7 @@ import {
OnMoveEnd,
NodeDragHandler,
NodeMouseHandler,
+ SelectionDragHandler,
} from '.';
import { HandleType } from './handles';
@@ -73,9 +74,9 @@ export interface ReactFlowProps extends HTMLAttributes {
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;
diff --git a/src/types/general.ts b/src/types/general.ts
index 2e870eed..a1708cb9 100644
--- a/src/types/general.ts
+++ b/src/types/general.ts
@@ -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;
diff --git a/src/types/nodes.ts b/src/types/nodes.ts
index 31712102..36c612e2 100644
--- a/src/types/nodes.ts
+++ b/src/types/nodes.ts
@@ -55,6 +55,7 @@ export interface NodeProps {
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 {
id: string;
@@ -74,9 +75,6 @@ export interface WrapNodeProps {
onMouseMove?: NodeMouseHandler;
onMouseLeave?: NodeMouseHandler;
onContextMenu?: NodeMouseHandler;
- onDragStart?: NodeDragHandler;
- onDrag?: NodeDragHandler;
- onDragStop?: NodeDragHandler;
style?: CSSProperties;
className?: string;
sourcePosition: Position;