From ab14be02f4147528d88072e03f95ad52242b55a2 Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 17 Aug 2022 11:43:52 +0200 Subject: [PATCH] feat(nodes): add nodeOrigin prop --- examples/nextjs/pages/CustomNode/index.tsx | 9 ++++-- examples/nextjs/pages/DragNDrop/index.tsx | 4 +++ .../core/src/components/Nodes/wrapNode.tsx | 4 ++- .../core/src/container/FlowRenderer/index.tsx | 1 + .../core/src/container/GraphView/index.tsx | 4 +++ .../core/src/container/NodeRenderer/index.tsx | 17 +++++++++-- .../core/src/container/NodeRenderer/utils.ts | 30 ++++++++++++++++++- .../core/src/container/ReactFlow/index.tsx | 6 ++++ packages/core/src/types/component-props.ts | 3 ++ packages/core/src/types/nodes.ts | 4 +++ 10 files changed, 75 insertions(+), 7 deletions(-) diff --git a/examples/nextjs/pages/CustomNode/index.tsx b/examples/nextjs/pages/CustomNode/index.tsx index 40f2add1..0daa35b0 100644 --- a/examples/nextjs/pages/CustomNode/index.tsx +++ b/examples/nextjs/pages/CustomNode/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, MouseEvent, ChangeEvent } from 'react'; +import React, { useState, useEffect, MouseEvent, ChangeEvent, useCallback } from 'react'; import { ReactFlow, MiniMap, @@ -118,8 +118,11 @@ const CustomNodeFlow = () => { ]); }, []); - const onConnect = (connection: Connection) => - setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)); + const onConnect = useCallback( + (connection: Connection) => + setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)), + [setEdges] + ); return ( { let id = 0; const getId = () => `dndnode_${id++}`; +const nodeOrigin: NodeOrigin = [0.5, 0.5]; + const DnDFlow = () => { const [reactFlowInstance, setReactFlowInstance] = useState(); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); @@ -74,6 +77,7 @@ const DnDFlow = () => { onInit={onInit} onDrop={onDrop} onDragOver={onDragOver} + nodeOrigin={nodeOrigin} > diff --git a/packages/core/src/components/Nodes/wrapNode.tsx b/packages/core/src/components/Nodes/wrapNode.tsx index bdca639b..854175b5 100644 --- a/packages/core/src/components/Nodes/wrapNode.tsx +++ b/packages/core/src/components/Nodes/wrapNode.tsx @@ -24,6 +24,8 @@ export default (NodeComponent: ComponentType) => { data, xPos, yPos, + xPosOrigin, + yPosOrigin, selected, onClick, onMouseEnter, @@ -161,7 +163,7 @@ export default (NodeComponent: ComponentType) => { ref={nodeRef} style={{ zIndex, - transform: `translate(${xPos}px,${yPos}px)`, + transform: `translate(${xPosOrigin}px,${yPosOrigin}px)`, pointerEvents: hasPointerEvents ? 'all' : 'none', visibility: initialized ? 'visible' : 'hidden', ...style, diff --git a/packages/core/src/container/FlowRenderer/index.tsx b/packages/core/src/container/FlowRenderer/index.tsx index 99725dfe..76accfbb 100644 --- a/packages/core/src/container/FlowRenderer/index.tsx +++ b/packages/core/src/container/FlowRenderer/index.tsx @@ -24,6 +24,7 @@ export type FlowRendererProps = Omit< | 'selectNodesOnDrag' | 'defaultMarkerColor' | 'rfId' + | 'nodeOrigin' > & { children: ReactNode; }; diff --git a/packages/core/src/container/GraphView/index.tsx b/packages/core/src/container/GraphView/index.tsx index f48d2b74..70b2d326 100644 --- a/packages/core/src/container/GraphView/index.tsx +++ b/packages/core/src/container/GraphView/index.tsx @@ -13,6 +13,7 @@ import { ReactFlowProps, Viewport, CoordinateExtent, + NodeOrigin, } from '../../types'; export interface GraphViewProps @@ -35,6 +36,7 @@ export interface GraphViewProps defaultViewport: Viewport; rfId: string; disableKeyboardA11y: boolean; + nodeOrigin: NodeOrigin; } const GraphView = ({ @@ -96,6 +98,7 @@ const GraphView = ({ noPanClassName, elevateEdgesOnSelect, disableKeyboardA11y, + nodeOrigin, rfId, }: GraphViewProps) => { useOnInitHandler(onInit); @@ -171,6 +174,7 @@ const GraphView = ({ noPanClassName={noPanClassName} noDragClassName={noDragClassName} disableKeyboardA11y={disableKeyboardA11y} + nodeOrigin={nodeOrigin} rfId={rfId} /> diff --git a/packages/core/src/container/NodeRenderer/index.tsx b/packages/core/src/container/NodeRenderer/index.tsx index 85ef9181..0b3f3ea3 100644 --- a/packages/core/src/container/NodeRenderer/index.tsx +++ b/packages/core/src/container/NodeRenderer/index.tsx @@ -7,6 +7,7 @@ import { internalsSymbol } from '../../utils'; import { containerStyle } from '../../styles'; import { GraphViewProps } from '../GraphView'; import { Position, ReactFlowState, WrapNodeProps } from '../../types'; +import { getPositionWithOrigin } from './utils'; type NodeRendererProps = Pick< GraphViewProps, @@ -23,6 +24,7 @@ type NodeRendererProps = Pick< | 'noDragClassName' | 'rfId' | 'disableKeyboardA11y' + | 'nodeOrigin' >; const selector = (s: ReactFlowState) => ({ @@ -81,6 +83,15 @@ const NodeRenderer = (props: NodeRendererProps) => { const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')); const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined')); const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined')); + const posX = node.position?.x ?? 0; + const posY = node.position?.y ?? 0; + const posOrigin = getPositionWithOrigin({ + x: posX, + y: posY, + width: node.width ?? 0, + height: node.height ?? 0, + origin: props.nodeOrigin, + }); return ( { sourcePosition={node.sourcePosition || Position.Bottom} targetPosition={node.targetPosition || Position.Top} hidden={node.hidden} - xPos={node.positionAbsolute?.x ?? 0} - yPos={node.positionAbsolute?.y ?? 0} + xPos={posX} + yPos={posY} + xPosOrigin={posOrigin.x} + yPosOrigin={posOrigin.y} selectNodesOnDrag={props.selectNodesOnDrag} onClick={props.onNodeClick} onMouseEnter={props.onNodeMouseEnter} diff --git a/packages/core/src/container/NodeRenderer/utils.ts b/packages/core/src/container/NodeRenderer/utils.ts index 0683279a..6d1d68a6 100644 --- a/packages/core/src/container/NodeRenderer/utils.ts +++ b/packages/core/src/container/NodeRenderer/utils.ts @@ -5,7 +5,7 @@ import InputNode from '../../components/Nodes/InputNode'; import OutputNode from '../../components/Nodes/OutputNode'; import GroupNode from '../../components/Nodes/GroupNode'; import wrapNode from '../../components/Nodes/wrapNode'; -import { NodeTypes, NodeProps, NodeTypesWrapped } from '../../types'; +import { NodeTypes, NodeProps, NodeTypesWrapped, NodeOrigin, XYPosition } from '../../types'; export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypesWrapped; @@ -31,3 +31,31 @@ export function createNodeTypes(nodeTypes: NodeTypes): NodeTypesWrapped { ...specialTypes, }; } + +export const getPositionWithOrigin = ({ + x, + y, + width, + height, + origin, +}: { + x: number; + y: number; + width: number; + height: number; + origin: NodeOrigin; +}): XYPosition => { + if (!width || !height) { + return { x, y }; + } + + if (origin[0] < 0 || origin[1] < 0 || origin[0] > 1 || origin[1] > 1) { + console.warn('[ReactFlow]: nodeOrigin must be between 0 and 1'); + return { x, y }; + } + + return { + x: x - width * origin[0], + y: y - height * origin[1], + }; +}; diff --git a/packages/core/src/container/ReactFlow/index.tsx b/packages/core/src/container/ReactFlow/index.tsx index 224debc3..0285ba73 100644 --- a/packages/core/src/container/ReactFlow/index.tsx +++ b/packages/core/src/container/ReactFlow/index.tsx @@ -6,6 +6,7 @@ import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } import DefaultNode from '../../components/Nodes/DefaultNode'; import InputNode from '../../components/Nodes/InputNode'; import OutputNode from '../../components/Nodes/OutputNode'; +import GroupNode from '../../components/Nodes/GroupNode'; import SelectionListener from '../../components/SelectionListener'; import StoreUpdater from '../../components/StoreUpdater'; @@ -14,6 +15,7 @@ import { ConnectionMode, EdgeTypes, EdgeTypesWrapped, + NodeOrigin, NodeTypes, NodeTypesWrapped, PanOnScrollMode, @@ -33,6 +35,7 @@ const defaultNodeTypes: NodeTypes = { input: InputNode, default: DefaultNode, output: OutputNode, + group: GroupNode, }; const defaultEdgeTypes: EdgeTypes = { @@ -43,6 +46,7 @@ const defaultEdgeTypes: EdgeTypes = { simplebezier: SimpleBezierEdge, }; +const initNodeOrigin: NodeOrigin = [0, 0]; const initSnapGrid: [number, number] = [15, 15]; const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 }; @@ -104,6 +108,7 @@ const ReactFlow = forwardRef( selectNodesOnDrag = true, nodesDraggable, nodesConnectable, + nodeOrigin = initNodeOrigin, elementsSelectable, defaultViewport = initDefaultViewport, minZoom = 0.5, @@ -219,6 +224,7 @@ const ReactFlow = forwardRef( elevateEdgesOnSelect={elevateEdgesOnSelect} rfId={rfId} disableKeyboardA11y={disableKeyboardA11y} + nodeOrigin={nodeOrigin} /> { onlyRenderVisibleElements?: boolean; nodesDraggable?: boolean; nodesConnectable?: boolean; + nodeOrigin?: NodeOrigin; + initNodeOrigin?: NodeOrigin; elementsSelectable?: boolean; selectNodesOnDrag?: boolean; panOnDrag?: boolean; diff --git a/packages/core/src/types/nodes.ts b/packages/core/src/types/nodes.ts index c9dbb65c..5ba91cf4 100644 --- a/packages/core/src/types/nodes.ts +++ b/packages/core/src/types/nodes.ts @@ -68,6 +68,8 @@ export interface WrapNodeProps { isConnectable: boolean; xPos: number; yPos: number; + xPosOrigin: number; + yPosOrigin: number; initialized: boolean; isSelectable: boolean; isDraggable: boolean; @@ -124,3 +126,5 @@ export type NodeDragItem = { parentNode?: string; dragging?: boolean; }; + +export type NodeOrigin = [number, number];