feat(nodes): add enter, move, leave and context handler #301
This commit is contained in:
@@ -69,6 +69,10 @@ const BasicFlow = () => <ReactFlow elements={elements} />;
|
||||
- `onElementsRemove(elements: Elements)`: element remove callback
|
||||
- `onNodeDragStart(node: Node)`: node drag start callback
|
||||
- `onNodeDragStop(node: Node)`: node drag stop callback
|
||||
- `onNodeMouseEnter(evt: MouseEvent, node: Node)`: node mouse enter callback
|
||||
- `onNodeMouseMove(evt: MouseEvent, node: Node)`: node mouse move callback
|
||||
- `onNodeMouseLeave(evt: MouseEvent, node: Node)`: node mouse leave callback
|
||||
- `onNodeContextMenu(evt: MouseEvent, node: Node)`: node context menu callback
|
||||
- `onConnect({ source, target })`: connect callback
|
||||
- `onLoad(reactFlowInstance)`: editor load callback
|
||||
- `onMove()`: move callback
|
||||
|
||||
@@ -4,6 +4,14 @@ import ReactFlow, { removeElements, addEdge } from 'react-flow-renderer';
|
||||
|
||||
const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
|
||||
|
||||
const onNodeMouseEnter = (evt, node) => console.log('mouse enter:', node);
|
||||
const onNodeMouseMove = (evt, node) => console.log('mouse move:', node);
|
||||
const onNodeMouseLeave = (evt, node) => console.log('mouse leave:', node);
|
||||
const onNodeContextMenu = (evt, node) => {
|
||||
evt.preventDefault();
|
||||
console.log('context menu:', node);
|
||||
};
|
||||
|
||||
const initialElements = [
|
||||
{
|
||||
id: '1',
|
||||
@@ -21,13 +29,13 @@ const initialElements = [
|
||||
{ id: '7', sourcePosition: 'right', targetPosition: 'left', data: { label: 'Node 7' }, position: { x: 750, y: 50 } },
|
||||
{ id: '8', sourcePosition: 'right', targetPosition: 'left', data: { label: 'Node 8' }, position: { x: 750, y: 300 } },
|
||||
|
||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
||||
{ id: 'e1-3', source: '1', target: '3', animated: true },
|
||||
{ id: 'e1-4', source: '2', target: '4', label: 'edge label' },
|
||||
{ id: 'e3-5', source: '3', target: '5', animated: true },
|
||||
{ id: 'e3-6', source: '3', target: '6', animated: true },
|
||||
{ id: 'e5-7', source: '5', target: '7', animated: true },
|
||||
{ id: 'e6-8', source: '6', target: '8', animated: true },
|
||||
{ id: 'e1-2', source: '1', type: 'smoothstep', target: '2', animated: true },
|
||||
{ id: 'e1-3', source: '1', type: 'smoothstep', target: '3', animated: true },
|
||||
{ id: 'e1-4', source: '2', type: 'smoothstep', target: '4', label: 'edge label' },
|
||||
{ id: 'e3-5', source: '3', type: 'smoothstep', target: '5', animated: true },
|
||||
{ id: 'e3-6', source: '3', type: 'smoothstep', target: '6', animated: true },
|
||||
{ id: 'e5-7', source: '5', type: 'smoothstep', target: '7', animated: true },
|
||||
{ id: 'e6-8', source: '6', type: 'smoothstep', target: '8', animated: true },
|
||||
];
|
||||
|
||||
const HorizontalFlow = () => {
|
||||
@@ -53,6 +61,10 @@ const HorizontalFlow = () => {
|
||||
onConnect={onConnect}
|
||||
onLoad={onLoad}
|
||||
selectNodesOnDrag={false}
|
||||
onNodeMouseEnter={onNodeMouseEnter}
|
||||
onNodeMouseMove={onNodeMouseMove}
|
||||
onNodeMouseLeave={onNodeMouseLeave}
|
||||
onNodeContextMenu={onNodeContextMenu}
|
||||
>
|
||||
<button onClick={changeClassName} style={{ position: 'absolute', right: 10, top: 30, zIndex: 4 }}>
|
||||
change class name
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState, memo, ComponentType, CSSProperties } from 'react';
|
||||
import React, { useEffect, useRef, useState, memo, ComponentType, CSSProperties, useMemo, MouseEvent } from 'react';
|
||||
import { DraggableCore } from 'react-draggable';
|
||||
import cx from 'classnames';
|
||||
import { ResizeObserver } from 'resize-observer';
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
NodePosUpdate,
|
||||
} from '../../types';
|
||||
|
||||
import { noop } from '../../utils';
|
||||
|
||||
const getMouseEvent = (evt: MouseEvent | TouchEvent) =>
|
||||
typeof TouchEvent !== 'undefined' && evt instanceof TouchEvent ? evt.touches[0] : (evt as MouseEvent);
|
||||
|
||||
@@ -154,6 +156,10 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
yPos,
|
||||
selected,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseMove,
|
||||
onMouseLeave,
|
||||
onContextMenu,
|
||||
onNodeDragStart,
|
||||
onNodeDragStop,
|
||||
style,
|
||||
@@ -172,6 +178,38 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
const [isDragging, setDragging] = useState(false);
|
||||
const position = { x: xPos, y: yPos };
|
||||
const nodeClasses = cx('react-flow__node', `react-flow__node-${type}`, className, { selected });
|
||||
const node = { id, type, position, data };
|
||||
const onMouseEnterHandler = useMemo(() => {
|
||||
if (!onMouseEnter || isDragging) {
|
||||
return noop;
|
||||
}
|
||||
|
||||
return (evt: MouseEvent) => onMouseEnter(evt, node);
|
||||
}, [onMouseEnter, isDragging]);
|
||||
|
||||
const onMouseMoveHandler = useMemo(() => {
|
||||
if (!onMouseMove || isDragging) {
|
||||
return noop;
|
||||
}
|
||||
|
||||
return (evt: MouseEvent) => onMouseMove(evt, node);
|
||||
}, [onMouseMove, isDragging]);
|
||||
|
||||
const onMouseLeaveHandler = useMemo(() => {
|
||||
if (!onMouseLeave || isDragging) {
|
||||
return noop;
|
||||
}
|
||||
|
||||
return (evt: MouseEvent) => onMouseLeave(evt, node);
|
||||
}, [onMouseLeave, isDragging]);
|
||||
|
||||
const onContextMenuHandler = useMemo(() => {
|
||||
if (!onContextMenu) {
|
||||
return noop;
|
||||
}
|
||||
|
||||
return (evt: MouseEvent) => onContextMenu(evt, node);
|
||||
}, [onContextMenu]);
|
||||
|
||||
const nodeStyle: CSSProperties = {
|
||||
zIndex: selected ? 10 : 3,
|
||||
@@ -237,7 +275,15 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||
disabled={!isInteractive}
|
||||
cancel=".nodrag"
|
||||
>
|
||||
<div className={nodeClasses} ref={nodeElement} style={nodeStyle}>
|
||||
<div
|
||||
className={nodeClasses}
|
||||
ref={nodeElement}
|
||||
style={nodeStyle}
|
||||
onMouseEnter={onMouseEnterHandler}
|
||||
onMouseMove={onMouseMoveHandler}
|
||||
onMouseLeave={onMouseLeaveHandler}
|
||||
onContextMenu={onContextMenuHandler}
|
||||
>
|
||||
<Provider value={id}>
|
||||
<NodeComponent
|
||||
id={id}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, memo, CSSProperties } from 'react';
|
||||
import React, { useEffect, useRef, memo, CSSProperties, MouseEvent } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { ResizeObserver } from 'resize-observer';
|
||||
|
||||
@@ -28,6 +28,10 @@ export interface GraphViewProps {
|
||||
elements: Elements;
|
||||
onElementClick?: (element: Node | Edge) => void;
|
||||
onElementsRemove?: (elements: Elements) => void;
|
||||
onNodeMouseEnter?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeMouseMove?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeMouseLeave?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeContextMenu?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeDragStart?: (node: Node) => void;
|
||||
onNodeDragStop?: (node: Node) => void;
|
||||
onConnect?: (connection: Connection | Edge) => void;
|
||||
@@ -56,6 +60,10 @@ const GraphView = memo(
|
||||
onMove,
|
||||
onLoad,
|
||||
onElementClick,
|
||||
onNodeMouseEnter,
|
||||
onNodeMouseMove,
|
||||
onNodeMouseLeave,
|
||||
onNodeContextMenu,
|
||||
onNodeDragStart,
|
||||
onNodeDragStop,
|
||||
connectionLineType,
|
||||
@@ -176,6 +184,10 @@ const GraphView = memo(
|
||||
<NodeRenderer
|
||||
nodeTypes={nodeTypes}
|
||||
onElementClick={onElementClick}
|
||||
onNodeMouseEnter={onNodeMouseEnter}
|
||||
onNodeMouseMove={onNodeMouseMove}
|
||||
onNodeMouseLeave={onNodeMouseLeave}
|
||||
onNodeContextMenu={onNodeContextMenu}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onlyRenderVisibleNodes={onlyRenderVisibleNodes}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { memo, ComponentType } from 'react';
|
||||
import React, { memo, ComponentType, MouseEvent } from 'react';
|
||||
|
||||
import { useStoreState } from '../../store/hooks';
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
@@ -8,6 +8,10 @@ interface NodeRendererProps {
|
||||
nodeTypes: NodeTypesType;
|
||||
selectNodesOnDrag: boolean;
|
||||
onElementClick?: (element: Node | Edge) => void;
|
||||
onNodeMouseEnter?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeMouseMove?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeMouseLeave?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeContextMenu?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeDragStart?: (node: Node) => void;
|
||||
onNodeDragStop?: (node: Node) => void;
|
||||
onlyRenderVisibleNodes?: boolean;
|
||||
@@ -37,6 +41,10 @@ function renderNode(
|
||||
xPos={node.__rf.position.x}
|
||||
yPos={node.__rf.position.y}
|
||||
onClick={props.onElementClick}
|
||||
onMouseEnter={props.onNodeMouseEnter}
|
||||
onMouseMove={props.onNodeMouseMove}
|
||||
onMouseLeave={props.onNodeMouseLeave}
|
||||
onContextMenu={props.onNodeContextMenu}
|
||||
onNodeDragStart={props.onNodeDragStart}
|
||||
onNodeDragStop={props.onNodeDragStop}
|
||||
transform={transform}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, CSSProperties, HTMLAttributes } from 'react';
|
||||
import React, { useMemo, CSSProperties, HTMLAttributes, MouseEvent } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
const nodeEnv: string = process.env.NODE_ENV as string;
|
||||
@@ -37,6 +37,10 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
||||
elements: Elements;
|
||||
onElementClick?: (element: Node | Edge) => void;
|
||||
onElementsRemove?: (elements: Elements) => void;
|
||||
onNodeMouseEnter?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeMouseMove?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeMouseLeave?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeContextMenu?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeDragStart?: (node: Node) => void;
|
||||
onNodeDragStop?: (node: Node) => void;
|
||||
onConnect?: (connection: Edge | Connection) => void;
|
||||
@@ -71,6 +75,10 @@ const ReactFlow = ({
|
||||
onMove,
|
||||
onElementsRemove,
|
||||
onConnect,
|
||||
onNodeMouseEnter,
|
||||
onNodeMouseMove,
|
||||
onNodeMouseLeave,
|
||||
onNodeContextMenu,
|
||||
onNodeDragStart,
|
||||
onNodeDragStop,
|
||||
onSelectionChange,
|
||||
@@ -97,6 +105,10 @@ const ReactFlow = ({
|
||||
onLoad={onLoad}
|
||||
onMove={onMove}
|
||||
onElementClick={onElementClick}
|
||||
onNodeMouseEnter={onNodeMouseEnter}
|
||||
onNodeMouseMove={onNodeMouseMove}
|
||||
onNodeMouseLeave={onNodeMouseLeave}
|
||||
onNodeContextMenu={onNodeContextMenu}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
nodeTypes={nodeTypesParsed}
|
||||
|
||||
+9
-1
@@ -1,4 +1,4 @@
|
||||
import { CSSProperties } from 'react';
|
||||
import { CSSProperties, MouseEvent } from 'react';
|
||||
|
||||
export type ElementId = string;
|
||||
|
||||
@@ -109,6 +109,10 @@ export interface NodeComponentProps {
|
||||
targetPosition?: Position;
|
||||
sourcePosition?: Position;
|
||||
onClick?: (node: Node) => void;
|
||||
onMouseEnter?: (node: Node) => void;
|
||||
onMouseMove?: (node: Node) => void;
|
||||
onMouseLeave?: (node: Node) => void;
|
||||
onContextMenu?: (node: Node) => void;
|
||||
onNodeDragStart?: (node: Node) => void;
|
||||
onNodeDragStop?: (node: Node) => void;
|
||||
style?: CSSProperties;
|
||||
@@ -125,6 +129,10 @@ export interface WrapNodeProps {
|
||||
isInteractive: boolean;
|
||||
selectNodesOnDrag: boolean;
|
||||
onClick?: (node: Node) => void;
|
||||
onMouseEnter?: (evt: MouseEvent, node: Node) => void;
|
||||
onMouseMove?: (evt: MouseEvent, node: Node) => void;
|
||||
onMouseLeave?: (evt: MouseEvent, node: Node) => void;
|
||||
onContextMenu?: (evt: MouseEvent, node: Node) => void;
|
||||
onNodeDragStart?: (node: Node) => void;
|
||||
onNodeDragStop?: (node: Node) => void;
|
||||
style?: CSSProperties;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { DraggableEvent } from 'react-draggable';
|
||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||
|
||||
export const noop = () => {};
|
||||
|
||||
export const isInputDOMNode = (e: ReactMouseEvent | DraggableEvent | KeyboardEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
return e && target && ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName);
|
||||
|
||||
Reference in New Issue
Block a user