chore(utils): cleanup

This commit is contained in:
moklick
2024-04-18 09:57:30 +02:00
parent 6c30686127
commit b6f458fc5a
3 changed files with 28 additions and 64 deletions
@@ -5,13 +5,13 @@
import { useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react'; import { useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { getNodesBounds } from '@xyflow/system'; import { getInternalNodesBounds } from '@xyflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
import { useDrag } from '../../hooks/useDrag'; import { useDrag } from '../../hooks/useDrag';
import { useMoveSelectedNodes } from '../../hooks/useMoveSelectedNodes'; import { useMoveSelectedNodes } from '../../hooks/useMoveSelectedNodes';
import { arrowKeyDiffs } from '../NodeWrapper/utils'; import { arrowKeyDiffs } from '../NodeWrapper/utils';
import type { InternalNode, Node, ReactFlowState } from '../../types'; import type { Node, ReactFlowState } from '../../types';
export type NodesSelectionProps<NodeType> = { export type NodesSelectionProps<NodeType> = {
onSelectionContextMenu?: (event: MouseEvent, nodes: NodeType[]) => void; onSelectionContextMenu?: (event: MouseEvent, nodes: NodeType[]) => void;
@@ -20,14 +20,10 @@ export type NodesSelectionProps<NodeType> = {
}; };
const selector = (s: ReactFlowState) => { const selector = (s: ReactFlowState) => {
const selectedNodes: InternalNode[] = []; const { width, height, x, y } = getInternalNodesBounds(s.nodeLookup, {
for (const [, node] of s.nodeLookup) { nodeOrigin: s.nodeOrigin,
if (node.selected) { filter: (node) => !!node.selected,
selectedNodes.push(node); });
}
}
const { width, height, x, y } = getNodesBounds(selectedNodes, { nodeOrigin: s.nodeOrigin });
return { return {
width, width,
+7 -7
View File
@@ -2,14 +2,14 @@ import {
infiniteExtent, infiniteExtent,
ConnectionMode, ConnectionMode,
adoptUserNodes, adoptUserNodes,
getNodesBounds,
getViewportForBounds, getViewportForBounds,
Transform, Transform,
updateConnectionLookup, updateConnectionLookup,
devWarn, devWarn,
getInternalNodesBounds,
} from '@xyflow/system'; } from '@xyflow/system';
import type { Edge, Node, ReactFlowStore } from '../types'; import type { Edge, InternalNode, Node, ReactFlowStore } from '../types';
const getInitialState = ({ const getInitialState = ({
nodes, nodes,
@@ -28,7 +28,7 @@ const getInitialState = ({
height?: number; height?: number;
fitView?: boolean; fitView?: boolean;
} = {}): ReactFlowStore => { } = {}): ReactFlowStore => {
const nodeLookup = new Map(); const nodeLookup = new Map<string, InternalNode>();
const parentLookup = new Map(); const parentLookup = new Map();
const connectionLookup = new Map(); const connectionLookup = new Map();
const edgeLookup = new Map(); const edgeLookup = new Map();
@@ -44,11 +44,11 @@ const getInitialState = ({
let transform: Transform = [0, 0, 1]; let transform: Transform = [0, 0, 1];
if (fitView && width && height) { if (fitView && width && height) {
const nodesWithDimensions = storeNodes.filter(
(node) => (node.width || node.initialWidth) && (node.height || node.initialHeight)
);
// @todo users nodeOrigin should be used here // @todo users nodeOrigin should be used here
const bounds = getNodesBounds(nodesWithDimensions, { nodeOrigin: [0, 0] }); const bounds = getInternalNodesBounds(nodeLookup, {
nodeOrigin: [0, 0],
filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
});
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1); const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
transform = [x, y, zoom]; transform = [x, y, zoom];
} }
+15 -47
View File
@@ -4,13 +4,12 @@ import {
clampPosition, clampPosition,
getBoundsOfBoxes, getBoundsOfBoxes,
getOverlappingArea, getOverlappingArea,
rectToBox,
nodeToRect, nodeToRect,
pointToRendererPoint, pointToRendererPoint,
getViewportForBounds, getViewportForBounds,
isCoordinateExtent, isCoordinateExtent,
getNodeDimensions, getNodeDimensions,
getPositionWithOrigin, nodeToBox,
} from './general'; } from './general';
import { import {
type Transform, type Transform,
@@ -131,7 +130,6 @@ export const getNodePositionWithOrigin = (
export type GetNodesBoundsParams = { export type GetNodesBoundsParams = {
nodeOrigin?: NodeOrigin; nodeOrigin?: NodeOrigin;
useRelativePosition?: boolean;
}; };
/** /**
@@ -140,28 +138,17 @@ export type GetNodesBoundsParams = {
* @remarks Useful when combined with {@link getViewportForBounds} to calculate the correct transform to fit the given nodes in a viewport. * @remarks Useful when combined with {@link getViewportForBounds} to calculate the correct transform to fit the given nodes in a viewport.
* @param nodes - Nodes to calculate the bounds for * @param nodes - Nodes to calculate the bounds for
* @param params.nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center * @param params.nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center
* @param params.useRelativePosition - Whether to use the relative or absolute node positions
* @returns Bounding box enclosing all nodes * @returns Bounding box enclosing all nodes
*/ */
// @todo how to handle this if users do not have absolute positions? export const getNodesBounds = (nodes: NodeBase[], params: GetNodesBoundsParams = { nodeOrigin: [0, 0] }): Rect => {
export const getNodesBounds = (
nodes: NodeBase[],
params: GetNodesBoundsParams = { nodeOrigin: [0, 0], useRelativePosition: false }
): Rect => {
if (nodes.length === 0) { if (nodes.length === 0) {
return { x: 0, y: 0, width: 0, height: 0 }; return { x: 0, y: 0, width: 0, height: 0 };
} }
const box = nodes.reduce( const box = nodes.reduce(
(currBox, node) => { (currBox, node) => {
const nodePos = getNodePositionWithOrigin(node, params.nodeOrigin); const nodeBox = nodeToBox(node, params.nodeOrigin);
return getBoundsOfBoxes( return getBoundsOfBoxes(currBox, nodeBox);
currBox,
rectToBox({
...nodePos[params.useRelativePosition ? 'position' : 'positionAbsolute'],
...getNodeDimensions(node),
})
);
}, },
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity } { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity }
); );
@@ -169,19 +156,19 @@ export const getNodesBounds = (
return boxToRect(box); return boxToRect(box);
}; };
export type GetInternalNodesBoundsParams = { export type GetInternalNodesBoundsParams<NodeType> = {
nodeOrigin?: NodeOrigin; nodeOrigin?: NodeOrigin;
useRelativePosition?: boolean; useRelativePosition?: boolean;
filter?: (node: NodeBase | NodeDragItem) => boolean; filter?: (node: NodeType) => boolean;
}; };
/** /**
* Determines a bounding box that contains all given nodes in an array * Determines a bounding box that contains all given nodes in an array
* @internal * @internal
*/ */
export const getInternalNodesBounds = ( export const getInternalNodesBounds = <NodeType extends InternalNodeBase | NodeDragItem>(
nodeLookup: NodeLookup | Map<string, NodeDragItem>, nodeLookup: Map<string, NodeType>,
params: GetInternalNodesBoundsParams = { params: GetInternalNodesBoundsParams<NodeType> = {
nodeOrigin: [0, 0], nodeOrigin: [0, 0],
} }
): Rect => { ): Rect => {
@@ -193,24 +180,8 @@ export const getInternalNodesBounds = (
nodeLookup.forEach((node) => { nodeLookup.forEach((node) => {
if (params.filter == undefined || params.filter(node)) { if (params.filter == undefined || params.filter(node)) {
const { width, height } = getNodeDimensions(node); const nodeBox = nodeToBox(node as InternalNodeBase, params.nodeOrigin);
const { x, y } = getPositionWithOrigin({ box = getBoundsOfBoxes(box, nodeBox);
x: node.internals.positionAbsolute.x,
y: node.internals.positionAbsolute.x,
width,
height,
origin: node.origin || params.nodeOrigin,
});
box = getBoundsOfBoxes(
box,
rectToBox({
x,
y,
width,
height,
})
);
} }
}); });
@@ -218,7 +189,7 @@ export const getInternalNodesBounds = (
}; };
export const getNodesInside = <NodeType extends NodeBase = NodeBase>( export const getNodesInside = <NodeType extends NodeBase = NodeBase>(
nodeLookup: Map<string, InternalNodeBase<NodeType>>, nodes: Map<string, InternalNodeBase<NodeType>>,
rect: Rect, rect: Rect,
[tx, ty, tScale]: Transform = [0, 0, 1], [tx, ty, tScale]: Transform = [0, 0, 1],
partially = false, partially = false,
@@ -234,7 +205,7 @@ export const getNodesInside = <NodeType extends NodeBase = NodeBase>(
const visibleNodes: InternalNodeBase<NodeType>[] = []; const visibleNodes: InternalNodeBase<NodeType>[] = [];
for (const [, node] of nodeLookup) { for (const [, node] of nodes) {
const { measured, selectable = true, hidden = false } = node; const { measured, selectable = true, hidden = false } = node;
const width = measured.width ?? node.width ?? node.initialWidth ?? null; const width = measured.width ?? node.width ?? node.initialWidth ?? null;
const height = measured.height ?? node.height ?? node.initialHeight ?? null; const height = measured.height ?? node.height ?? node.initialHeight ?? null;
@@ -281,15 +252,12 @@ export function fitView<Params extends FitViewParamsBase<NodeBase>, Options exte
options?: Options options?: Options
) { ) {
const filteredNodes: InternalNodeBase[] = []; const filteredNodes: InternalNodeBase[] = [];
const optionNodeIds = options?.nodes ? new Set(options.nodes.map((node) => node.id)) : null;
nodeLookup.forEach((n) => { nodeLookup.forEach((n) => {
const isVisible = n.measured.width && n.measured.height && (options?.includeHiddenNodes || !n.hidden); const isVisible = n.measured.width && n.measured.height && (options?.includeHiddenNodes || !n.hidden);
// TODO: this remove options.nodes.some with a Set if (isVisible && (!optionNodeIds || optionNodeIds.has(n.id))) {
if (
isVisible &&
(!options?.nodes || (options?.nodes.length && options?.nodes.some((optionNode) => optionNode.id === n.id)))
) {
filteredNodes.push(n); filteredNodes.push(n);
} }
}); });