feat(nodes): add nodeOrigin prop
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
Reference in New Issue
Block a user