feat(nodes): add nodeOrigin prop

This commit is contained in:
moklick
2022-08-17 11:43:52 +02:00
parent d2d4b76e65
commit ab14be02f4
10 changed files with 75 additions and 7 deletions
+6 -3
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, MouseEvent, ChangeEvent } from 'react'; import React, { useState, useEffect, MouseEvent, ChangeEvent, useCallback } from 'react';
import { import {
ReactFlow, ReactFlow,
MiniMap, MiniMap,
@@ -118,8 +118,11 @@ const CustomNodeFlow = () => {
]); ]);
}, []); }, []);
const onConnect = (connection: Connection) => const onConnect = useCallback(
setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)); (connection: Connection) =>
setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)),
[setEdges]
);
return ( return (
<ReactFlow <ReactFlow
@@ -10,6 +10,7 @@ import {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
Controls, Controls,
NodeOrigin,
} from '@react-flow/bundle'; } from '@react-flow/bundle';
import Sidebar from './Sidebar'; import Sidebar from './Sidebar';
@@ -33,6 +34,8 @@ const onDragOver = (event: DragEvent) => {
let id = 0; let id = 0;
const getId = () => `dndnode_${id++}`; const getId = () => `dndnode_${id++}`;
const nodeOrigin: NodeOrigin = [0.5, 0.5];
const DnDFlow = () => { const DnDFlow = () => {
const [reactFlowInstance, setReactFlowInstance] = useState<ReactFlowInstance>(); const [reactFlowInstance, setReactFlowInstance] = useState<ReactFlowInstance>();
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
@@ -74,6 +77,7 @@ const DnDFlow = () => {
onInit={onInit} onInit={onInit}
onDrop={onDrop} onDrop={onDrop}
onDragOver={onDragOver} onDragOver={onDragOver}
nodeOrigin={nodeOrigin}
> >
<Controls /> <Controls />
</ReactFlow> </ReactFlow>
@@ -24,6 +24,8 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
data, data,
xPos, xPos,
yPos, yPos,
xPosOrigin,
yPosOrigin,
selected, selected,
onClick, onClick,
onMouseEnter, onMouseEnter,
@@ -161,7 +163,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
ref={nodeRef} ref={nodeRef}
style={{ style={{
zIndex, zIndex,
transform: `translate(${xPos}px,${yPos}px)`, transform: `translate(${xPosOrigin}px,${yPosOrigin}px)`,
pointerEvents: hasPointerEvents ? 'all' : 'none', pointerEvents: hasPointerEvents ? 'all' : 'none',
visibility: initialized ? 'visible' : 'hidden', visibility: initialized ? 'visible' : 'hidden',
...style, ...style,
@@ -24,6 +24,7 @@ export type FlowRendererProps = Omit<
| 'selectNodesOnDrag' | 'selectNodesOnDrag'
| 'defaultMarkerColor' | 'defaultMarkerColor'
| 'rfId' | 'rfId'
| 'nodeOrigin'
> & { > & {
children: ReactNode; children: ReactNode;
}; };
@@ -13,6 +13,7 @@ import {
ReactFlowProps, ReactFlowProps,
Viewport, Viewport,
CoordinateExtent, CoordinateExtent,
NodeOrigin,
} from '../../types'; } from '../../types';
export interface GraphViewProps export interface GraphViewProps
@@ -35,6 +36,7 @@ export interface GraphViewProps
defaultViewport: Viewport; defaultViewport: Viewport;
rfId: string; rfId: string;
disableKeyboardA11y: boolean; disableKeyboardA11y: boolean;
nodeOrigin: NodeOrigin;
} }
const GraphView = ({ const GraphView = ({
@@ -96,6 +98,7 @@ const GraphView = ({
noPanClassName, noPanClassName,
elevateEdgesOnSelect, elevateEdgesOnSelect,
disableKeyboardA11y, disableKeyboardA11y,
nodeOrigin,
rfId, rfId,
}: GraphViewProps) => { }: GraphViewProps) => {
useOnInitHandler(onInit); useOnInitHandler(onInit);
@@ -171,6 +174,7 @@ const GraphView = ({
noPanClassName={noPanClassName} noPanClassName={noPanClassName}
noDragClassName={noDragClassName} noDragClassName={noDragClassName}
disableKeyboardA11y={disableKeyboardA11y} disableKeyboardA11y={disableKeyboardA11y}
nodeOrigin={nodeOrigin}
rfId={rfId} rfId={rfId}
/> />
</ViewportWrapper> </ViewportWrapper>
@@ -7,6 +7,7 @@ import { internalsSymbol } from '../../utils';
import { containerStyle } from '../../styles'; import { containerStyle } from '../../styles';
import { GraphViewProps } from '../GraphView'; import { GraphViewProps } from '../GraphView';
import { Position, ReactFlowState, WrapNodeProps } from '../../types'; import { Position, ReactFlowState, WrapNodeProps } from '../../types';
import { getPositionWithOrigin } from './utils';
type NodeRendererProps = Pick< type NodeRendererProps = Pick<
GraphViewProps, GraphViewProps,
@@ -23,6 +24,7 @@ type NodeRendererProps = Pick<
| 'noDragClassName' | 'noDragClassName'
| 'rfId' | 'rfId'
| 'disableKeyboardA11y' | 'disableKeyboardA11y'
| 'nodeOrigin'
>; >;
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
@@ -81,6 +83,15 @@ const NodeRenderer = (props: NodeRendererProps) => {
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined')); const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined')); const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === '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 ( return (
<NodeComponent <NodeComponent
@@ -93,8 +104,10 @@ const NodeRenderer = (props: NodeRendererProps) => {
sourcePosition={node.sourcePosition || Position.Bottom} sourcePosition={node.sourcePosition || Position.Bottom}
targetPosition={node.targetPosition || Position.Top} targetPosition={node.targetPosition || Position.Top}
hidden={node.hidden} hidden={node.hidden}
xPos={node.positionAbsolute?.x ?? 0} xPos={posX}
yPos={node.positionAbsolute?.y ?? 0} yPos={posY}
xPosOrigin={posOrigin.x}
yPosOrigin={posOrigin.y}
selectNodesOnDrag={props.selectNodesOnDrag} selectNodesOnDrag={props.selectNodesOnDrag}
onClick={props.onNodeClick} onClick={props.onNodeClick}
onMouseEnter={props.onNodeMouseEnter} onMouseEnter={props.onNodeMouseEnter}
@@ -5,7 +5,7 @@ import InputNode from '../../components/Nodes/InputNode';
import OutputNode from '../../components/Nodes/OutputNode'; import OutputNode from '../../components/Nodes/OutputNode';
import GroupNode from '../../components/Nodes/GroupNode'; import GroupNode from '../../components/Nodes/GroupNode';
import wrapNode from '../../components/Nodes/wrapNode'; 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; export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypesWrapped;
@@ -31,3 +31,31 @@ export function createNodeTypes(nodeTypes: NodeTypes): NodeTypesWrapped {
...specialTypes, ...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],
};
};
@@ -6,6 +6,7 @@ import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge }
import DefaultNode from '../../components/Nodes/DefaultNode'; import DefaultNode from '../../components/Nodes/DefaultNode';
import InputNode from '../../components/Nodes/InputNode'; import InputNode from '../../components/Nodes/InputNode';
import OutputNode from '../../components/Nodes/OutputNode'; import OutputNode from '../../components/Nodes/OutputNode';
import GroupNode from '../../components/Nodes/GroupNode';
import SelectionListener from '../../components/SelectionListener'; import SelectionListener from '../../components/SelectionListener';
import StoreUpdater from '../../components/StoreUpdater'; import StoreUpdater from '../../components/StoreUpdater';
@@ -14,6 +15,7 @@ import {
ConnectionMode, ConnectionMode,
EdgeTypes, EdgeTypes,
EdgeTypesWrapped, EdgeTypesWrapped,
NodeOrigin,
NodeTypes, NodeTypes,
NodeTypesWrapped, NodeTypesWrapped,
PanOnScrollMode, PanOnScrollMode,
@@ -33,6 +35,7 @@ const defaultNodeTypes: NodeTypes = {
input: InputNode, input: InputNode,
default: DefaultNode, default: DefaultNode,
output: OutputNode, output: OutputNode,
group: GroupNode,
}; };
const defaultEdgeTypes: EdgeTypes = { const defaultEdgeTypes: EdgeTypes = {
@@ -43,6 +46,7 @@ const defaultEdgeTypes: EdgeTypes = {
simplebezier: SimpleBezierEdge, simplebezier: SimpleBezierEdge,
}; };
const initNodeOrigin: NodeOrigin = [0, 0];
const initSnapGrid: [number, number] = [15, 15]; const initSnapGrid: [number, number] = [15, 15];
const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 }; const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 };
@@ -104,6 +108,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
selectNodesOnDrag = true, selectNodesOnDrag = true,
nodesDraggable, nodesDraggable,
nodesConnectable, nodesConnectable,
nodeOrigin = initNodeOrigin,
elementsSelectable, elementsSelectable,
defaultViewport = initDefaultViewport, defaultViewport = initDefaultViewport,
minZoom = 0.5, minZoom = 0.5,
@@ -219,6 +224,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
elevateEdgesOnSelect={elevateEdgesOnSelect} elevateEdgesOnSelect={elevateEdgesOnSelect}
rfId={rfId} rfId={rfId}
disableKeyboardA11y={disableKeyboardA11y} disableKeyboardA11y={disableKeyboardA11y}
nodeOrigin={nodeOrigin}
/> />
<StoreUpdater <StoreUpdater
nodes={nodes} nodes={nodes}
@@ -32,6 +32,7 @@ import {
NodeMouseHandler, NodeMouseHandler,
SelectionDragHandler, SelectionDragHandler,
Viewport, Viewport,
NodeOrigin,
} from '.'; } from '.';
import { HandleType } from './handles'; import { HandleType } from './handles';
@@ -91,6 +92,8 @@ export interface ReactFlowProps extends HTMLAttributes<HTMLDivElement> {
onlyRenderVisibleElements?: boolean; onlyRenderVisibleElements?: boolean;
nodesDraggable?: boolean; nodesDraggable?: boolean;
nodesConnectable?: boolean; nodesConnectable?: boolean;
nodeOrigin?: NodeOrigin;
initNodeOrigin?: NodeOrigin;
elementsSelectable?: boolean; elementsSelectable?: boolean;
selectNodesOnDrag?: boolean; selectNodesOnDrag?: boolean;
panOnDrag?: boolean; panOnDrag?: boolean;
+4
View File
@@ -68,6 +68,8 @@ export interface WrapNodeProps<T = any> {
isConnectable: boolean; isConnectable: boolean;
xPos: number; xPos: number;
yPos: number; yPos: number;
xPosOrigin: number;
yPosOrigin: number;
initialized: boolean; initialized: boolean;
isSelectable: boolean; isSelectable: boolean;
isDraggable: boolean; isDraggable: boolean;
@@ -124,3 +126,5 @@ export type NodeDragItem = {
parentNode?: string; parentNode?: string;
dragging?: boolean; dragging?: boolean;
}; };
export type NodeOrigin = [number, number];