Merge pull request #3822 from xyflow/refactor/on-node-drag-types

refactor(types): OnNodeDrag
This commit is contained in:
Moritz Klack
2024-01-23 10:05:13 +01:00
committed by GitHub
14 changed files with 74 additions and 48 deletions
+2 -1
View File
@@ -10,9 +10,10 @@ import {
Edge, Edge,
useReactFlow, useReactFlow,
Panel, Panel,
OnNodeDrag,
} from '@xyflow/react'; } from '@xyflow/react';
const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node); const onNodeDrag: OnNodeDrag = (_, node) => console.log('drag', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
@@ -1,6 +1,8 @@
import React, { memo, FC, CSSProperties, useCallback, useEffect } from 'react'; import React, { memo, FC, CSSProperties, useCallback } from 'react';
import { Handle, Position, NodeProps, Connection, Edge, useOnViewportChange, Viewport } from '@xyflow/react'; import { Handle, Position, NodeProps, Connection, Edge, useOnViewportChange, Viewport } from '@xyflow/react';
import type { ColorSelectorNode } from '.';
const targetHandleStyle: CSSProperties = { background: '#555' }; const targetHandleStyle: CSSProperties = { background: '#555' };
const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 }; const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 };
const sourceHandleStyleB: CSSProperties = { const sourceHandleStyleB: CSSProperties = {
@@ -11,7 +13,7 @@ const sourceHandleStyleB: CSSProperties = {
const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params); const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params);
const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => { const ColorSelectorNode: FC<NodeProps<ColorSelectorNode['data']>> = ({ data, isConnectable }) => {
const onStart = useCallback((viewport: Viewport) => console.log('onStart', viewport), []); const onStart = useCallback((viewport: Viewport) => console.log('onStart', viewport), []);
const onChange = useCallback((viewport: Viewport) => console.log('onChange', viewport), []); const onChange = useCallback((viewport: Viewport) => console.log('onChange', viewport), []);
const onEnd = useCallback((viewport: Viewport) => console.log('onEnd', viewport), []); const onEnd = useCallback((viewport: Viewport) => console.log('onEnd', viewport), []);
@@ -5,24 +5,32 @@ import {
Controls, Controls,
addEdge, addEdge,
Node, Node,
ReactFlowInstance,
Position, Position,
SnapGrid, SnapGrid,
Connection,
useNodesState,
useEdgesState, useEdgesState,
Background, Background,
Edge, Edge,
OnNodeDrag,
OnInit,
applyNodeChanges,
OnNodesChange,
OnConnect,
} from '@xyflow/react'; } from '@xyflow/react';
import ColorSelectorNode from './ColorSelectorNode'; import ColorSelectorNode from './ColorSelectorNode';
const onInit = (reactFlowInstance: ReactFlowInstance) => { export type ColorSelectorNode = Node<
{ color: string; onChange: (event: ChangeEvent<HTMLInputElement>) => void },
'selectorNode'
>;
export type MyNode = Node | ColorSelectorNode;
const onInit: OnInit<MyNode> = (reactFlowInstance) => {
console.log('flow loaded:', reactFlowInstance); console.log('flow loaded:', reactFlowInstance);
}; };
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeDragStop: OnNodeDrag<MyNode> = (_, node) => console.log('drag stop', node);
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); const onNodeClick = (_: MouseEvent, node: MyNode) => console.log('click', node);
const initBgColor = '#1A192B'; const initBgColor = '#1A192B';
@@ -34,7 +42,16 @@ const nodeTypes = {
}; };
const CustomNodeFlow = () => { const CustomNodeFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]); const [nodes, setNodes] = useState<MyNode[]>([]);
const onNodesChange: OnNodesChange = useCallback(
(changes) =>
setNodes((nds) => {
const nextNodes = applyNodeChanges(changes, nds);
return nextNodes;
}),
[setNodes]
);
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]); const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
const [bgColor, setBgColor] = useState<string>(initBgColor); const [bgColor, setBgColor] = useState<string>(initBgColor);
@@ -120,9 +137,8 @@ const CustomNodeFlow = () => {
]); ]);
}, []); }, []);
const onConnect = useCallback( const onConnect: OnConnect = useCallback(
(connection: Connection) => (connection) => setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)),
setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)),
[setEdges] [setEdges]
); );
@@ -145,14 +161,14 @@ const CustomNodeFlow = () => {
maxZoom={2} maxZoom={2}
> >
<MiniMap <MiniMap
nodeStrokeColor={(n: Node): string => { nodeStrokeColor={(n: MyNode): string => {
if (n.type === 'input') return '#0041d0'; if (n.type === 'input') return '#0041d0';
if (n.type === 'selectorNode') return bgColor; if (n.type === 'selectorNode') return bgColor;
if (n.type === 'output') return '#ff0072'; if (n.type === 'output') return '#ff0072';
return '#eee'; return '#eee';
}} }}
nodeColor={(n: Node): string => { nodeColor={(n: MyNode): string => {
if (n.type === 'selectorNode') return bgColor; if (n.type === 'selectorNode') return bgColor;
return '#fff'; return '#fff';
@@ -43,10 +43,8 @@
export let zIndex: $$Props['zIndex'] = undefined; export let zIndex: $$Props['zIndex'] = undefined;
export let dragging: $$Props['dragging'] = false; export let dragging: $$Props['dragging'] = false;
export let dragHandle: $$Props['dragHandle'] = undefined; export let dragHandle: $$Props['dragHandle'] = undefined;
export let positionAbsolute: $$Props['positionAbsolute'] = { export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
x: 0, export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
y: 0
};
export let isConnectable: $$Props['isConnectable'] = undefined; export let isConnectable: $$Props['isConnectable'] = undefined;
data; data;
@@ -59,7 +57,8 @@
zIndex; zIndex;
dragging; dragging;
dragHandle; dragHandle;
positionAbsolute; positionAbsoluteX;
positionAbsoluteY;
isConnectable; isConnectable;
</script> </script>
+12 -3
View File
@@ -1,19 +1,28 @@
# @xyflow/react # @xyflow/react
## 12.0.0-next.8
### Patch changes
- fix `OnNodeDrag` type
## 12.0.0-next.7 ## 12.0.0-next.7
## Minor changes ## Minor changes
- pass Node/Edge types to changes thanks @FelipeEmos
- use position instead of positionAbsolute for `getNodesBounds`
- add second option param to `screenToFlowPosition` for configuring if `snapToGrid` should be used - add second option param to `screenToFlowPosition` for configuring if `snapToGrid` should be used
### Patch changes
- pass `Node`/ `Edge` types to changes thanks @FelipeEmos
- use position instead of positionAbsolute for `getNodesBounds`
- infer types for `getIncomers`, `getOutgoers`, `updateEdge`, `addEdge` and `getConnectedEdges` thanks @joeyballentine - infer types for `getIncomers`, `getOutgoers`, `updateEdge`, `addEdge` and `getConnectedEdges` thanks @joeyballentine
- refactor handles: prefix with flow id for handling nested flows - refactor handles: prefix with flow id for handling nested flows
- add comments for types like `ReactFlowProps` or `Node` for a better developer experience - add comments for types like `ReactFlowProps` or `Node` for a better developer experience
## 12.0.0-next.6 ## 12.0.0-next.6
### Minor changes ### Patch changes
- fix `deleteElements` - fix `deleteElements`
- refactor internal `applyChanges` - refactor internal `applyChanges`
-1
View File
@@ -67,7 +67,6 @@ export {
type OnError, type OnError,
type NodeProps, type NodeProps,
type NodeOrigin, type NodeOrigin,
type OnNodeDrag,
type OnSelectionDrag, type OnSelectionDrag,
Position, Position,
type XYPosition, type XYPosition,
+4 -4
View File
@@ -40,10 +40,10 @@ import type {
OnDelete, OnDelete,
OnNodesChange, OnNodesChange,
OnEdgesChange, OnEdgesChange,
NodeDragHandler,
NodeMouseHandler, NodeMouseHandler,
SelectionDragHandler, SelectionDragHandler,
EdgeMouseHandler, EdgeMouseHandler,
OnNodeDrag,
} from '.'; } from '.';
/** /**
@@ -111,11 +111,11 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
/** This event handler is called when a user right clicks on a node */ /** This event handler is called when a user right clicks on a node */
onNodeContextMenu?: NodeMouseHandler; onNodeContextMenu?: NodeMouseHandler;
/** This event handler is called when a user starts to drag a node */ /** This event handler is called when a user starts to drag a node */
onNodeDragStart?: NodeDragHandler; onNodeDragStart?: OnNodeDrag;
/** This event handler is called when a user drags a node */ /** This event handler is called when a user drags a node */
onNodeDrag?: NodeDragHandler; onNodeDrag?: OnNodeDrag;
/** This event handler is called when a user stops dragging a node */ /** This event handler is called when a user stops dragging a node */
onNodeDragStop?: NodeDragHandler; onNodeDragStop?: OnNodeDrag;
/** This event handler is called when a user clicks on an edge */ /** This event handler is called when a user clicks on an edge */
onEdgeClick?: (event: ReactMouseEvent, edge: Edge) => void; onEdgeClick?: (event: ReactMouseEvent, edge: Edge) => void;
/** This event handler is called when a user right clicks on an edge */ /** This event handler is called when a user right clicks on an edge */
+1 -1
View File
@@ -19,7 +19,7 @@ import { ComponentType } from 'react';
export type OnNodesChange<NodeType extends Node = Node> = (changes: NodeChange<NodeType>[]) => void; export type OnNodesChange<NodeType extends Node = Node> = (changes: NodeChange<NodeType>[]) => void;
export type OnEdgesChange<EdgeType extends Edge = Edge> = (changes: EdgeChange<EdgeType>[]) => void; export type OnEdgesChange<EdgeType extends Edge = Edge> = (changes: EdgeChange<EdgeType>[]) => void;
export type OnNodesDelete = (nodes: Node[]) => void; export type OnNodesDelete<NodeType extends Node = Node> = (nodes: NodeType[]) => void;
export type OnEdgesDelete = (edges: Edge[]) => void; export type OnEdgesDelete = (edges: Edge[]) => void;
export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void; export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void;
+8 -4
View File
@@ -3,11 +3,11 @@ import type { CoordinateExtent, NodeBase, NodeOrigin, OnError } from '@xyflow/sy
import { NodeTypes } from './general'; import { NodeTypes } from './general';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/** /**
* The node data structure that gets used for the nodes prop. * The node data structure that gets used for the nodes prop.
* @public * @public
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Node<NodeData = any, NodeType extends string | undefined = string | undefined> = NodeBase< export type Node<NodeData = any, NodeType extends string | undefined = string | undefined> = NodeBase<
NodeData, NodeData,
NodeType NodeType
@@ -18,9 +18,13 @@ export type Node<NodeData = any, NodeType extends string | undefined = string |
focusable?: boolean; focusable?: boolean;
}; };
export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void; export type NodeMouseHandler<NodeType extends Node = Node> = (event: ReactMouseEvent, node: NodeType) => void;
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void; export type SelectionDragHandler<NodeType extends Node = Node> = (event: ReactMouseEvent, nodes: NodeType[]) => void;
export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void; export type OnNodeDrag<NodeType extends Node = Node> = (
event: ReactMouseEvent,
node: NodeType,
nodes: NodeType[]
) => void;
export type NodeWrapperProps = { export type NodeWrapperProps = {
id: string; id: string;
+1 -1
View File
@@ -17,7 +17,6 @@ import {
type PanBy, type PanBy,
type OnConnectStart, type OnConnectStart,
type OnConnectEnd, type OnConnectEnd,
type OnNodeDrag,
type OnSelectionDrag, type OnSelectionDrag,
type OnMoveStart, type OnMoveStart,
type OnMove, type OnMove,
@@ -43,6 +42,7 @@ import type {
OnSelectionChangeFunc, OnSelectionChangeFunc,
UnselectNodesAndEdgesParams, UnselectNodesAndEdgesParams,
OnDelete, OnDelete,
OnNodeDrag,
} from '.'; } from '.';
export type ReactFlowStore = { export type ReactFlowStore = {
-1
View File
@@ -79,7 +79,6 @@ export {
type OnError, type OnError,
type NodeProps, type NodeProps,
type NodeOrigin, type NodeOrigin,
type OnNodeDrag,
type OnSelectionDrag, type OnSelectionDrag,
Position, Position,
type XYPosition, type XYPosition,
+1 -2
View File
@@ -15,7 +15,6 @@ import {
type XYPosition, type XYPosition,
type CoordinateExtent, type CoordinateExtent,
type UpdateConnection, type UpdateConnection,
type NodeBase,
type NodeDragItem, type NodeDragItem,
errorMessages errorMessages
} from '@xyflow/system'; } from '@xyflow/system';
@@ -67,7 +66,7 @@ export function createStore({
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => { const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
store.nodes.update((nds) => { store.nodes.update((nds) => {
return nds.map((node) => { return nds.map((node) => {
const nodeDragItem = (nodeDragItems as Array<NodeBase | NodeDragItem>).find( const nodeDragItem = (nodeDragItems as Array<Node | NodeDragItem>).find(
(ndi) => ndi.id === node.id (ndi) => ndi.id === node.id
); );
+3 -5
View File
@@ -94,9 +94,9 @@ export type NodeProps<T = any> = {
positionAbsoluteY: number; positionAbsoluteY: number;
width?: number; width?: number;
height?: number; height?: number;
dragging: boolean; dragging: NodeBase['dragging'];
targetPosition?: Position; sourcePosition?: NodeBase['sourcePosition'];
sourcePosition?: Position; targetPosition?: NodeBase['targetPosition'];
}; };
export type NodeHandleBounds = { export type NodeHandleBounds = {
@@ -134,8 +134,6 @@ export type NodeDragItem = {
export type NodeOrigin = [number, number]; export type NodeOrigin = [number, number];
export type OnNodeDrag = (event: MouseEvent, node: NodeBase, nodes: NodeBase[]) => void;
export type OnSelectionDrag = (event: MouseEvent, nodes: NodeBase[]) => void; export type OnSelectionDrag = (event: MouseEvent, nodes: NodeBase[]) => void;
export type NodeHandle = Optional<HandleElement, 'width' | 'height'>; export type NodeHandle = Optional<HandleElement, 'width' | 'height'>;
+6 -6
View File
@@ -23,7 +23,6 @@ import type {
SnapGrid, SnapGrid,
Transform, Transform,
PanBy, PanBy,
OnNodeDrag,
OnSelectionDrag, OnSelectionDrag,
UpdateNodePositions, UpdateNodePositions,
Box, Box,
@@ -31,7 +30,7 @@ import type {
export type OnDrag = (event: MouseEvent, dragItems: NodeDragItem[], node: NodeBase, nodes: NodeBase[]) => void; export type OnDrag = (event: MouseEvent, dragItems: NodeDragItem[], node: NodeBase, nodes: NodeBase[]) => void;
type StoreItems = { type StoreItems<OnNodeDrag> = {
nodes: NodeBase[]; nodes: NodeBase[];
nodeLookup: Map<string, NodeBase>; nodeLookup: Map<string, NodeBase>;
edges: EdgeBase[]; edges: EdgeBase[];
@@ -58,9 +57,9 @@ type StoreItems = {
updateNodePositions: UpdateNodePositions; updateNodePositions: UpdateNodePositions;
}; };
export type XYDragParams = { export type XYDragParams<OnNodeDrag> = {
domNode: Element; domNode: Element;
getStoreItems: () => StoreItems; getStoreItems: () => StoreItems<OnNodeDrag>;
onDragStart?: OnDrag; onDragStart?: OnDrag;
onDrag?: OnDrag; onDrag?: OnDrag;
onDragStop?: OnDrag; onDragStop?: OnDrag;
@@ -80,14 +79,15 @@ export type DragUpdateParams = {
domNode: Element; domNode: Element;
}; };
export function XYDrag({ // eslint-disable-next-line @typescript-eslint/no-explicit-any
export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => void | undefined>({
domNode, domNode,
onNodeMouseDown, onNodeMouseDown,
getStoreItems, getStoreItems,
onDragStart, onDragStart,
onDrag, onDrag,
onDragStop, onDragStop,
}: XYDragParams): XYDragInstance { }: XYDragParams<OnNodeDrag>): XYDragInstance {
let lastPos: { x: number | null; y: number | null } = { x: null, y: null }; let lastPos: { x: number | null; y: number | null } = { x: null, y: null };
let autoPanId = 0; let autoPanId = 0;
let dragItems: NodeDragItem[] = []; let dragItems: NodeDragItem[] = [];