diff --git a/example/src/Overview/index.js b/example/src/Overview/index.js index a36ccc9c..619e477c 100644 --- a/example/src/Overview/index.js +++ b/example/src/Overview/index.js @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background, isNode } from 'react-flow-renderer'; const onNodeDragStart = (event, node) => console.log('drag start', node); +const onNodeDrag = (event, node) => console.log('drag', node); const onNodeDragStop = (event, node) => console.log('drag stop', node); const onPaneClick = (event) => console.log('pane click', event); const onPaneScroll = (event) => console.log('pane scroll', event); @@ -132,6 +133,7 @@ const OverviewFlow = () => { onPaneScroll={onPaneScroll} onPaneContextMenu={onPaneContextMenu} onNodeDragStart={onNodeDragStart} + onNodeDrag={onNodeDrag} onNodeDragStop={onNodeDragStop} onSelectionDragStart={onSelectionDragStart} onSelectionDrag={onSelectionDrag} diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index b8ea5fdf..ca978006 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, memo, ComponentType, CSSProperties, useMemo, MouseEvent, useCallback } from 'react'; -import { DraggableCore } from 'react-draggable'; +import { DraggableCore, DraggableData, DraggableEvent } from 'react-draggable'; import cc from 'classcat'; import { useStoreActions } from '../../store/hooks'; @@ -21,6 +21,7 @@ export default (NodeComponent: ComponentType) => { onMouseLeave, onContextMenu, onNodeDragStart, + onNodeDrag, onNodeDragStop, style, className, @@ -107,7 +108,7 @@ export default (NodeComponent: ComponentType) => { ); const onDragStart = useCallback( - (event) => { + (event: DraggableEvent) => { onNodeDragStart?.(event as MouseEvent, node); if (selectNodesOnDrag && isSelectable) { @@ -125,20 +126,26 @@ export default (NodeComponent: ComponentType) => { ); const onDrag = useCallback( - (_, data) => { + (event: DraggableEvent, draggableData: DraggableData) => { + if (onNodeDrag) { + node.position.x += draggableData.deltaX; + node.position.y += draggableData.deltaY; + onNodeDrag(event as MouseEvent, node); + } + updateNodePosDiff({ id, diff: { - x: data.deltaX, - y: data.deltaY, + x: draggableData.deltaX, + y: draggableData.deltaY, }, }); }, - [id] + [id, node, onNodeDrag] ); const onDragStop = useCallback( - (event) => { + (event: DraggableEvent) => { // onDragStop also gets called when user just clicks on a node. // Because of that we set dragging to true inside the onDrag handler and handle the click here if (!isDragging) { diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 363b50b9..780f8fcc 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -40,6 +40,7 @@ const GraphView = ({ onNodeMouseLeave, onNodeContextMenu, onNodeDragStart, + onNodeDrag, onNodeDragStop, onSelectionDragStart, onSelectionDrag, @@ -243,6 +244,7 @@ const GraphView = ({ onNodeMouseLeave={onNodeMouseLeave} onNodeContextMenu={onNodeContextMenu} onNodeDragStop={onNodeDragStop} + onNodeDrag={onNodeDrag} onNodeDragStart={onNodeDragStart} selectNodesOnDrag={selectNodesOnDrag} snapToGrid={snapToGrid} diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index ad31abeb..06577d08 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -3,7 +3,6 @@ import React, { memo, useMemo, ComponentType, MouseEvent } from 'react'; import { getNodesInside } from '../../utils/graph'; import { useStoreState, useStoreActions } from '../../store/hooks'; import { Node, NodeTypesType, WrapNodeProps, Edge } from '../../types'; - interface NodeRendererProps { nodeTypes: NodeTypesType; selectNodesOnDrag: boolean; @@ -13,6 +12,7 @@ interface NodeRendererProps { onNodeMouseLeave?: (event: MouseEvent, node: Node) => void; onNodeContextMenu?: (event: MouseEvent, node: Node) => void; onNodeDragStart?: (event: MouseEvent, node: Node) => void; + onNodeDrag?: (event: MouseEvent, node: Node) => void; onNodeDragStop?: (event: MouseEvent, node: Node) => void; snapToGrid: boolean; snapGrid: [number, number]; @@ -91,6 +91,7 @@ const NodeRenderer = (props: NodeRendererProps) => { onMouseLeave={props.onNodeMouseLeave} onContextMenu={props.onNodeContextMenu} onNodeDragStart={props.onNodeDragStart} + onNodeDrag={props.onNodeDrag} onNodeDragStop={props.onNodeDragStop} scale={transform[2]} selected={selectedElements?.some(({ id }) => id === node.id) || false} diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx index 6c974de4..77af0d96 100644 --- a/src/container/ReactFlow/index.tsx +++ b/src/container/ReactFlow/index.tsx @@ -58,6 +58,7 @@ export interface ReactFlowProps extends Omit, 'on onNodeMouseLeave?: (event: MouseEvent, node: Node) => void; onNodeContextMenu?: (event: MouseEvent, node: Node) => void; onNodeDragStart?: (event: MouseEvent, node: Node) => void; + onNodeDrag?: (event: MouseEvent, node: Node) => void; onNodeDragStop?: (event: MouseEvent, node: Node) => void; onConnect?: (connection: Edge | Connection) => void; onConnectStart?: OnConnectStartFunc; @@ -130,6 +131,7 @@ const ReactFlow = ({ onNodeMouseLeave, onNodeContextMenu, onNodeDragStart, + onNodeDrag, onNodeDragStop, onSelectionChange, onSelectionDragStart, @@ -191,6 +193,7 @@ const ReactFlow = ({ onNodeMouseLeave={onNodeMouseLeave} onNodeContextMenu={onNodeContextMenu} onNodeDragStart={onNodeDragStart} + onNodeDrag={onNodeDrag} onNodeDragStop={onNodeDragStop} nodeTypes={nodeTypesParsed} edgeTypes={edgeTypesParsed} diff --git a/src/types/index.ts b/src/types/index.ts index 888a5b00..b41e82a9 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -196,6 +196,7 @@ export interface NodeComponentProps { onMouseLeave?: (node: Node) => void; onContextMenu?: (node: Node) => void; onNodeDragStart?: (node: Node) => void; + onNodeDrag?: (node: Node) => void; onNodeDragStop?: (node: Node) => void; style?: CSSProperties; isDragging?: boolean; @@ -219,6 +220,7 @@ export interface WrapNodeProps { onMouseLeave?: (event: ReactMouseEvent, node: Node) => void; onContextMenu?: (event: ReactMouseEvent, node: Node) => void; onNodeDragStart?: (event: ReactMouseEvent, node: Node) => void; + onNodeDrag?: (event: ReactMouseEvent, node: Node) => void; onNodeDragStop?: (event: ReactMouseEvent, node: Node) => void; style?: CSSProperties; className?: string;