chore(utils): cleanup
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user