feat(nodes): add extent option

This commit is contained in:
moklick
2021-11-04 21:43:47 +01:00
parent fefcc441af
commit fdf2571402
7 changed files with 36 additions and 25 deletions
+1
View File
@@ -36,6 +36,7 @@ const initialNodes: Node[] = [
position: { x: 15, y: 15 }, position: { x: 15, y: 15 },
className: 'light', className: 'light',
parentNode: '4', parentNode: '4',
extent: 'parent',
}, },
{ {
id: '4b', id: '4b',
+2 -2
View File
@@ -10,7 +10,7 @@ import ReactFlow, {
Node, Node,
Connection, Connection,
Edge, Edge,
NodeExtent, CoordinateExtent,
Position, Position,
} from 'react-flow-renderer'; } from 'react-flow-renderer';
import dagre from 'dagre'; import dagre from 'dagre';
@@ -22,7 +22,7 @@ import './layouting.css';
const dagreGraph = new dagre.graphlib.Graph(); const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({})); dagreGraph.setDefaultEdgeLabel(() => ({}));
const nodeExtent: NodeExtent = [ const nodeExtent: CoordinateExtent = [
[0, 0], [0, 0],
[1000, 1000], [1000, 1000],
]; ];
+3 -4
View File
@@ -10,12 +10,11 @@ import {
OnConnectStartFunc, OnConnectStartFunc,
OnConnectStopFunc, OnConnectStopFunc,
OnConnectEndFunc, OnConnectEndFunc,
NodeExtent, CoordinateExtent,
OnNodesChange, OnNodesChange,
OnEdgesChange, OnEdgesChange,
ConnectionMode, ConnectionMode,
SnapGrid, SnapGrid,
TranslateExtent,
} from '../../types'; } from '../../types';
interface StoreUpdaterProps { interface StoreUpdaterProps {
@@ -29,14 +28,14 @@ interface StoreUpdaterProps {
nodesConnectable?: boolean; nodesConnectable?: boolean;
minZoom?: number; minZoom?: number;
maxZoom?: number; maxZoom?: number;
nodeExtent?: NodeExtent; nodeExtent?: CoordinateExtent;
onNodesChange?: OnNodesChange; onNodesChange?: OnNodesChange;
onEdgesChange?: OnEdgesChange; onEdgesChange?: OnEdgesChange;
elementsSelectable?: boolean; elementsSelectable?: boolean;
connectionMode?: ConnectionMode; connectionMode?: ConnectionMode;
snapToGrid?: boolean; snapToGrid?: boolean;
snapGrid?: SnapGrid; snapGrid?: SnapGrid;
translateExtent?: TranslateExtent; translateExtent?: CoordinateExtent;
} }
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
+3 -4
View File
@@ -33,11 +33,10 @@ import {
OnConnectStopFunc, OnConnectStopFunc,
OnConnectEndFunc, OnConnectEndFunc,
OnConnectFunc, OnConnectFunc,
TranslateExtent, CoordinateExtent,
KeyCode, KeyCode,
PanOnScrollMode, PanOnScrollMode,
OnEdgeUpdateFunc, OnEdgeUpdateFunc,
NodeExtent,
NodeChange, NodeChange,
EdgeChange, EdgeChange,
} from '../../types'; } from '../../types';
@@ -111,9 +110,9 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
maxZoom?: number; maxZoom?: number;
defaultZoom?: number; defaultZoom?: number;
defaultPosition?: [number, number]; defaultPosition?: [number, number];
translateExtent?: TranslateExtent; translateExtent?: CoordinateExtent;
preventScrolling?: boolean; preventScrolling?: boolean;
nodeExtent?: NodeExtent; nodeExtent?: CoordinateExtent;
defaultMarkerColor?: string; defaultMarkerColor?: string;
zoomOnScroll?: boolean; zoomOnScroll?: boolean;
zoomOnPinch?: boolean; zoomOnPinch?: boolean;
+19 -7
View File
@@ -11,8 +11,7 @@ import {
NodeDiffUpdate, NodeDiffUpdate,
XYPosition, XYPosition,
InitD3ZoomPayload, InitD3ZoomPayload,
TranslateExtent, CoordinateExtent,
NodeExtent,
Transform, Transform,
Dimensions, Dimensions,
OnConnectFunc, OnConnectFunc,
@@ -143,7 +142,7 @@ const createStore = () =>
onNodesChange?.(nodesToChange); onNodesChange?.(nodesToChange);
}, },
updateNodePosition: ({ id, diff, isDragging }: NodeDiffUpdate) => { updateNodePosition: ({ id, diff, isDragging }: NodeDiffUpdate) => {
const { onNodesChange, nodes, nodeExtent } = get(); const { onNodesChange, nodes, nodeExtent, nodeInternals } = get();
if (onNodesChange) { if (onNodesChange) {
const matchingNodes = nodes.filter((n) => !!(n.isSelected || n.id === id)); const matchingNodes = nodes.filter((n) => !!(n.isSelected || n.id === id));
@@ -158,13 +157,26 @@ const createStore = () =>
}; };
if (diff) { if (diff) {
change.position = nodeExtent let currentExtent = nodeExtent || node.extent;
if (node.extent === 'parent' && node.parentNode && node.width && node.height) {
const parent = nodeInternals.get(node.parentNode);
currentExtent =
parent?.width && parent?.height
? [
[0, 0],
[parent.width - node.width, parent.height - node.height],
]
: currentExtent;
}
change.position = currentExtent
? clampPosition( ? clampPosition(
{ {
x: node.position.x + diff.x, x: node.position.x + diff.x,
y: node.position.y + diff.y, y: node.position.y + diff.y,
}, },
nodeExtent currentExtent
) )
: { x: node.position.x + diff.x, y: node.position.y + diff.y }; : { x: node.position.x + diff.x, y: node.position.y + diff.y };
} }
@@ -300,7 +312,7 @@ const createStore = () =>
set({ maxZoom }); set({ maxZoom });
}, },
setTranslateExtent: (translateExtent: TranslateExtent) => { setTranslateExtent: (translateExtent: CoordinateExtent) => {
const { d3Zoom } = get(); const { d3Zoom } = get();
d3Zoom?.translateExtent(translateExtent); d3Zoom?.translateExtent(translateExtent);
@@ -320,7 +332,7 @@ const createStore = () =>
onEdgesChange?.(edgesToUnselect as EdgeChange[]); onEdgesChange?.(edgesToUnselect as EdgeChange[]);
} }
}, },
setNodeExtent: (nodeExtent: NodeExtent) => setNodeExtent: (nodeExtent: CoordinateExtent) =>
set({ set({
nodeExtent, nodeExtent,
nodes: get().nodes.map((node) => { nodes: get().nodes.map((node) => {
+6 -6
View File
@@ -84,6 +84,7 @@ export interface Node<T = any> {
height?: number | null; height?: number | null;
parentNode?: ElementId; parentNode?: ElementId;
zIndex?: number; zIndex?: number;
extent?: 'parent' | CoordinateExtent;
} }
export enum ArrowHeadType { export enum ArrowHeadType {
@@ -416,8 +417,7 @@ export type FlowTransform = {
zoom: number; zoom: number;
}; };
export type TranslateExtent = [[number, number], [number, number]]; export type CoordinateExtent = [[number, number], [number, number]];
export type NodeExtent = TranslateExtent;
export type KeyCode = string | Array<string>; export type KeyCode = string | Array<string>;
@@ -486,8 +486,8 @@ export interface ReactFlowState {
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined; d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
minZoom: number; minZoom: number;
maxZoom: number; maxZoom: number;
translateExtent: TranslateExtent; translateExtent: CoordinateExtent;
nodeExtent: NodeExtent; nodeExtent: CoordinateExtent;
nodesSelectionActive: boolean; nodesSelectionActive: boolean;
selectionActive: boolean; selectionActive: boolean;
@@ -527,8 +527,8 @@ export interface ReactFlowState {
initD3Zoom: (payload: InitD3ZoomPayload) => void; initD3Zoom: (payload: InitD3ZoomPayload) => void;
setMinZoom: (minZoom: number) => void; setMinZoom: (minZoom: number) => void;
setMaxZoom: (maxZoom: number) => void; setMaxZoom: (maxZoom: number) => void;
setTranslateExtent: (translateExtent: TranslateExtent) => void; setTranslateExtent: (translateExtent: CoordinateExtent) => void;
setNodeExtent: (nodeExtent: NodeExtent) => void; setNodeExtent: (nodeExtent: CoordinateExtent) => void;
setOnConnect: (onConnectFunction: OnConnectFunc) => void; setOnConnect: (onConnectFunction: OnConnectFunc) => void;
setOnConnectStart: (onConnectFunction: OnConnectStartFunc) => void; setOnConnectStart: (onConnectFunction: OnConnectStartFunc) => void;
setOnConnectStop: (onConnectFunction: OnConnectStopFunc) => void; setOnConnectStop: (onConnectFunction: OnConnectStopFunc) => void;
+2 -2
View File
@@ -1,4 +1,4 @@
import { Dimensions, XYPosition, NodeExtent, Box, Rect } from '../types'; import { Dimensions, XYPosition, CoordinateExtent, Box, Rect } from '../types';
export const getDimensions = (node: HTMLDivElement): Dimensions => ({ export const getDimensions = (node: HTMLDivElement): Dimensions => ({
width: node.offsetWidth, width: node.offsetWidth,
@@ -7,7 +7,7 @@ export const getDimensions = (node: HTMLDivElement): Dimensions => ({
export const clamp = (val: number, min: number = 0, max: number = 1): number => Math.min(Math.max(val, min), max); export const clamp = (val: number, min: number = 0, max: number = 1): number => Math.min(Math.max(val, min), max);
export const clampPosition = (position: XYPosition, extent: NodeExtent) => ({ export const clampPosition = (position: XYPosition, extent: CoordinateExtent) => ({
x: clamp(position.x, extent[0][0], extent[1][0]), x: clamp(position.x, extent[0][0], extent[1][0]),
y: clamp(position.y, extent[0][1], extent[1][1]), y: clamp(position.y, extent[0][1], extent[1][1]),
}); });