refactor(nodes): rename computed to measured, add helpers
This commit is contained in:
@@ -15,8 +15,7 @@ const initialNodes: Node[] = nodes.map((n) => ({
|
||||
|
||||
const expectedNodes: Node[] = initialNodes.map((n) => ({
|
||||
...n,
|
||||
computed: {
|
||||
positionAbsolute: n.position,
|
||||
measured: {
|
||||
...nodeDimensions,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -48,7 +48,7 @@ describe('<ReactFlow />: onNodesChange', () => {
|
||||
id: '1',
|
||||
item: {
|
||||
...nodes[0],
|
||||
computed: { positionAbsolute: nodes[0].position, width: 200, height: 100 },
|
||||
measured: { width: 200, height: 100 },
|
||||
style: { width: 200, height: 100 },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -136,8 +136,8 @@ describe('applyChanges Testing', () => {
|
||||
];
|
||||
const nextNodes = applyNodeChanges(nodeChanges, nodes);
|
||||
|
||||
expect(nodes[0].computed).to.be.undefined;
|
||||
expect(nextNodes[0].computed).to.be.deep.equal({ width: newWidth, height: newHeight });
|
||||
expect(nodes[0].measured).to.be.undefined;
|
||||
expect(nextNodes[0].measured).to.be.deep.equal({ width: newWidth, height: newHeight });
|
||||
expect(nextNodes[0].width).to.be.undefined;
|
||||
expect(nextNodes[0].height).to.be.undefined;
|
||||
});
|
||||
@@ -153,7 +153,7 @@ describe('applyChanges Testing', () => {
|
||||
const nextNodes = applyNodeChanges(nodeChanges, nodes);
|
||||
|
||||
expect(nextNodes[0].position).to.be.deep.equal(newPosition);
|
||||
expect(nextNodes[0].computed).to.be.deep.equal({ width: newWidth, height: newHeight });
|
||||
expect(nextNodes[0].measured).to.be.deep.equal({ width: newWidth, height: newHeight });
|
||||
});
|
||||
|
||||
it('replaces nodes/edges', () => {
|
||||
|
||||
@@ -9,8 +9,8 @@ function getNodeIntersection(intersectionNode: Node, targetNode: Node) {
|
||||
width: intersectionNodeWidth,
|
||||
height: intersectionNodeHeight,
|
||||
positionAbsolute: intersectionNodePosition,
|
||||
} = intersectionNode.computed || {};
|
||||
const targetPosition = targetNode.computed?.positionAbsolute!;
|
||||
} = intersectionNode.measured || {};
|
||||
const targetPosition = targetNode.measured?.positionAbsolute!;
|
||||
|
||||
const w = intersectionNodeWidth! / 2;
|
||||
const h = intersectionNodeHeight! / 2;
|
||||
@@ -33,7 +33,7 @@ function getNodeIntersection(intersectionNode: Node, targetNode: Node) {
|
||||
|
||||
// returns the position (top,right,bottom or right) passed node compared to the intersection point
|
||||
function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
|
||||
const n = { ...node.computed?.positionAbsolute, ...node };
|
||||
const n = { ...node.measured?.positionAbsolute, ...node };
|
||||
const nx = Math.round(n.x!);
|
||||
const ny = Math.round(n.y!);
|
||||
const px = Math.round(intersectionPoint.x);
|
||||
@@ -42,13 +42,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
|
||||
if (px <= nx + 1) {
|
||||
return Position.Left;
|
||||
}
|
||||
if (px >= nx + n.computed?.width! - 1) {
|
||||
if (px >= nx + n.measured?.width! - 1) {
|
||||
return Position.Right;
|
||||
}
|
||||
if (py <= ny + 1) {
|
||||
return Position.Top;
|
||||
}
|
||||
if (py >= n.y! + n.computed?.height! - 1) {
|
||||
if (py >= n.y! + n.measured?.height! - 1) {
|
||||
return Position.Bottom;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPositi
|
||||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
||||
|
||||
const { position: intersectionNodePosition } = intersectionNode;
|
||||
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.computed ?? {
|
||||
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.measured ?? {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
@@ -42,13 +42,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
|
||||
if (px <= nx + 1) {
|
||||
return Position.Left;
|
||||
}
|
||||
if (px >= nx + (n.computed?.width ?? 0) - 1) {
|
||||
if (px >= nx + (n.measured?.width ?? 0) - 1) {
|
||||
return Position.Right;
|
||||
}
|
||||
if (py <= ny + 1) {
|
||||
return Position.Top;
|
||||
}
|
||||
if (py >= n.y + (n.computed?.height ?? 0) - 1) {
|
||||
if (py >= n.y + (n.measured?.height ?? 0) - 1) {
|
||||
return Position.Bottom;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import { MiniMapNode } from './MiniMapNode';
|
||||
import type { ReactFlowState, Node } from '../../types';
|
||||
import type { ReactFlowState, Node, InternalNode } from '../../types';
|
||||
import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types';
|
||||
|
||||
declare const window: any;
|
||||
@@ -85,7 +85,7 @@ function NodeComponentWrapperInner<NodeType extends Node>({
|
||||
shapeRendering: string;
|
||||
}) {
|
||||
const { node, x, y } = useStore((s) => {
|
||||
const node = s.nodeLookup.get(id) as NodeType;
|
||||
const node = s.nodeLookup.get(id) as InternalNode<NodeType>;
|
||||
const { x, y } = getNodePositionWithOrigin(node, node?.origin || nodeOrigin).positionAbsolute;
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,12 +5,14 @@ import {
|
||||
ResizeControlVariant,
|
||||
type XYResizerInstance,
|
||||
type XYResizerChange,
|
||||
XYResizerChildChange,
|
||||
type XYResizerChildChange,
|
||||
type NodeChange,
|
||||
type NodeDimensionChange,
|
||||
type NodePositionChange,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import { useStoreApi } from '../../hooks/useStore';
|
||||
import { useNodeId } from '../../contexts/NodeIdContext';
|
||||
import type { NodeChange, NodeDimensionChange, NodePositionChange } from '../../types';
|
||||
import type { ResizeControlProps, ResizeControlLineProps } from './types';
|
||||
|
||||
function ResizeControl({
|
||||
|
||||
@@ -12,8 +12,8 @@ import type { NodeToolbarProps } from './types';
|
||||
const nodeEqualityFn = (a?: InternalNode, b?: InternalNode) =>
|
||||
a?.internals.positionAbsolute.x !== b?.internals.positionAbsolute.x ||
|
||||
a?.internals.positionAbsolute.y !== b?.internals.positionAbsolute.y ||
|
||||
a?.computed.width !== b?.computed.width ||
|
||||
a?.computed.height !== b?.computed.height ||
|
||||
a?.measured.width !== b?.measured.width ||
|
||||
a?.measured.height !== b?.measured.height ||
|
||||
a?.selected !== b?.selected ||
|
||||
a?.internals.z !== b?.internals.z;
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ const ConnectionLine = ({
|
||||
}
|
||||
|
||||
const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
|
||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.computed.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.computed.height ?? 0;
|
||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.measured.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.measured.height ?? 0;
|
||||
const fromX = (fromNode.internals.positionAbsolute.x ?? 0) + fromHandleX;
|
||||
const fromY = (fromNode.internals.positionAbsolute.y ?? 0) + fromHandleY;
|
||||
const fromPosition = fromHandle?.position;
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
import { useRef, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import cc from 'classcat';
|
||||
import { getNodesInside, getEventPosition, SelectionMode } from '@xyflow/system';
|
||||
import { getNodesInside, getEventPosition, SelectionMode, type NodeChange, type EdgeChange } from '@xyflow/system';
|
||||
|
||||
import { UserSelection } from '../../components/UserSelection';
|
||||
import { containerStyle } from '../../styles/utils';
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { getSelectionChanges } from '../../utils';
|
||||
import type { ReactFlowProps, ReactFlowState, NodeChange, EdgeChange } from '../../types';
|
||||
import type { ReactFlowProps, ReactFlowState } from '../../types';
|
||||
|
||||
type PaneProps = {
|
||||
isSelecting: boolean;
|
||||
|
||||
21
packages/react/src/hooks/useInternalNode.ts
Normal file
21
packages/react/src/hooks/useInternalNode.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { useCallback } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore } from './useStore';
|
||||
import type { InternalNode, Node } from '../types';
|
||||
|
||||
/**
|
||||
* Hook for getting an internal node by id
|
||||
*
|
||||
* @public
|
||||
* @param id - id of the node
|
||||
* @returns array with visible node ids
|
||||
*/
|
||||
export function useInternalNode<NodeType extends Node = Node>(id: string): InternalNode<NodeType> | undefined {
|
||||
const node = useStore(
|
||||
useCallback((s) => s.nodeLookup.get(id) as InternalNode<NodeType> | undefined, [id]),
|
||||
shallow
|
||||
);
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -1,12 +1,5 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
getElementsToRemove,
|
||||
getOverlappingArea,
|
||||
isRectObject,
|
||||
nodeHasDimensions,
|
||||
nodeToRect,
|
||||
type Rect,
|
||||
} from '@xyflow/system';
|
||||
import { getElementsToRemove, getOverlappingArea, isRectObject, nodeToRect, type Rect } from '@xyflow/system';
|
||||
|
||||
import useViewportHelper from './useViewportHelper';
|
||||
import { useStoreApi } from './useStore';
|
||||
@@ -32,7 +25,7 @@ export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge
|
||||
}, []);
|
||||
|
||||
const getNode = useCallback<Instance.GetNode<NodeType>>((id) => {
|
||||
return store.getState().nodeLookup.get(id)?.internals.userProvidedNode as NodeType;
|
||||
return store.getState().nodeLookup.get(id)?.internals.userNode as NodeType;
|
||||
}, []);
|
||||
|
||||
const getEdges = useCallback<Instance.GetEdges<EdgeType>>(() => {
|
||||
@@ -223,13 +216,9 @@ export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge
|
||||
[]
|
||||
);
|
||||
|
||||
const getNodeRect = useCallback((nodeOrRect: NodeType | { id: NodeType['id'] }): Rect | null => {
|
||||
const node =
|
||||
isNode(nodeOrRect) && nodeHasDimensions(nodeOrRect)
|
||||
? nodeOrRect
|
||||
: (store.getState().nodeLookup.get(nodeOrRect.id)?.internals.userProvidedNode as NodeType);
|
||||
|
||||
return node ? nodeToRect(node) : null;
|
||||
const getNodeRect = useCallback(({ id }: { id: string }): Rect | null => {
|
||||
const internalNode = store.getState().nodeLookup.get(id);
|
||||
return internalNode ? nodeToRect(internalNode) : null;
|
||||
}, []);
|
||||
|
||||
const getIntersectingNodes = useCallback<Instance.GetIntersectingNodes<NodeType>>(
|
||||
|
||||
@@ -48,12 +48,12 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
||||
return { x, y, zoom };
|
||||
},
|
||||
fitView: (options) => {
|
||||
const { nodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
|
||||
const { nodeLookup, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
|
||||
|
||||
return panZoom
|
||||
? fitView(
|
||||
{
|
||||
nodes,
|
||||
nodeLookup,
|
||||
width,
|
||||
height,
|
||||
nodeOrigin,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { getNodesInside } from '@xyflow/system';
|
||||
import { useCallback } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { getNodesInside } from '@xyflow/system';
|
||||
|
||||
import { useStore } from './useStore';
|
||||
import type { Node, ReactFlowState } from '../types';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
|
||||
return onlyRenderVisible
|
||||
? getNodesInside<Node>(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map(
|
||||
? getNodesInside<Node>(s.nodeLookup, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map(
|
||||
(node) => node.id
|
||||
)
|
||||
: Array.from(s.nodeLookup.keys());
|
||||
|
||||
@@ -26,6 +26,7 @@ export { useNodesInitialized, type UseNodesInitializedOptions } from './hooks/us
|
||||
export { useHandleConnections } from './hooks/useHandleConnections';
|
||||
export { useNodesData } from './hooks/useNodesData';
|
||||
export { useConnection } from './hooks/useConnection';
|
||||
export { useInternalNode } from './hooks/useInternalNode';
|
||||
export { useNodeId } from './contexts/NodeIdContext';
|
||||
|
||||
export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
|
||||
|
||||
@@ -2,27 +2,20 @@ import { createWithEqualityFn } from 'zustand/traditional';
|
||||
import {
|
||||
clampPosition,
|
||||
fitView as fitViewSystem,
|
||||
adoptUserProvidedNodes,
|
||||
adoptUserNodes,
|
||||
updateAbsolutePositions,
|
||||
panBy as panBySystem,
|
||||
updateNodeDimensions as updateNodeDimensionsSystem,
|
||||
updateConnectionLookup,
|
||||
handleParentExpand,
|
||||
NodeChange,
|
||||
EdgeSelectionChange,
|
||||
NodeSelectionChange,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||
import getInitialState from './initialState';
|
||||
import type {
|
||||
ReactFlowState,
|
||||
Node,
|
||||
Edge,
|
||||
EdgeSelectionChange,
|
||||
NodeSelectionChange,
|
||||
UnselectNodesAndEdgesParams,
|
||||
FitViewOptions,
|
||||
InternalNode,
|
||||
} from '../types';
|
||||
import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams, FitViewOptions, InternalNode } from '../types';
|
||||
|
||||
const createRFStore = ({
|
||||
nodes,
|
||||
@@ -52,7 +45,7 @@ const createRFStore = ({
|
||||
//
|
||||
// When this happens, we take the note objects passed by the user and extend them with fields
|
||||
// relevant for internal React Flow operations.
|
||||
adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
||||
adoptUserNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
||||
|
||||
set({ nodes });
|
||||
},
|
||||
@@ -263,21 +256,22 @@ const createRFStore = ({
|
||||
triggerEdgeChanges(edgeChanges);
|
||||
},
|
||||
setNodeExtent: (nodeExtent) => {
|
||||
const { nodes } = get();
|
||||
const { nodeLookup } = get();
|
||||
|
||||
for (const [, node] of nodeLookup) {
|
||||
const positionAbsolute = clampPosition(node.position, nodeExtent);
|
||||
|
||||
nodeLookup.set(node.id, {
|
||||
...node,
|
||||
internals: {
|
||||
...node.internals,
|
||||
positionAbsolute,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
set({
|
||||
nodeExtent,
|
||||
nodes: nodes.map((node) => {
|
||||
const positionAbsolute = clampPosition(node.position, nodeExtent);
|
||||
|
||||
return {
|
||||
...node,
|
||||
computed: {
|
||||
...node.computed,
|
||||
positionAbsolute,
|
||||
},
|
||||
};
|
||||
}),
|
||||
});
|
||||
},
|
||||
panBy: (delta): boolean => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
infiniteExtent,
|
||||
ConnectionMode,
|
||||
adoptUserProvidedNodes,
|
||||
adoptUserNodes,
|
||||
getNodesBounds,
|
||||
getViewportForBounds,
|
||||
Transform,
|
||||
@@ -35,7 +35,7 @@ const getInitialState = ({
|
||||
const storeNodes = defaultNodes ?? nodes ?? [];
|
||||
|
||||
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
||||
adoptUserProvidedNodes(storeNodes, nodeLookup, {
|
||||
adoptUserNodes(storeNodes, nodeLookup, {
|
||||
nodeOrigin: [0, 0],
|
||||
elevateNodesOnSelect: false,
|
||||
});
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import type { XYPosition, Dimensions } from '@xyflow/system';
|
||||
|
||||
import type { Node, Edge } from '.';
|
||||
|
||||
export type NodeDimensionChange = {
|
||||
id: string;
|
||||
type: 'dimensions';
|
||||
dimensions?: Dimensions;
|
||||
resizing?: boolean;
|
||||
};
|
||||
|
||||
export type NodePositionChange = {
|
||||
id: string;
|
||||
type: 'position';
|
||||
position?: XYPosition;
|
||||
positionAbsolute?: XYPosition;
|
||||
dragging?: boolean;
|
||||
};
|
||||
|
||||
export type NodeSelectionChange = {
|
||||
id: string;
|
||||
type: 'select';
|
||||
selected: boolean;
|
||||
};
|
||||
|
||||
export type NodeRemoveChange = {
|
||||
id: string;
|
||||
type: 'remove';
|
||||
};
|
||||
|
||||
export type NodeAddChange<NodeType extends Node = Node> = {
|
||||
item: NodeType;
|
||||
type: 'add';
|
||||
};
|
||||
|
||||
export type NodeReplaceChange<NodeType extends Node = Node> = {
|
||||
id: string;
|
||||
item: NodeType;
|
||||
type: 'replace';
|
||||
};
|
||||
|
||||
/**
|
||||
* Union type of all possible node changes.
|
||||
* @public
|
||||
*/
|
||||
export type NodeChange<NodeType extends Node = Node> =
|
||||
| NodeDimensionChange
|
||||
| NodePositionChange
|
||||
| NodeSelectionChange
|
||||
| NodeRemoveChange
|
||||
| NodeAddChange<NodeType>
|
||||
| NodeReplaceChange<NodeType>;
|
||||
|
||||
export type EdgeSelectionChange = NodeSelectionChange;
|
||||
export type EdgeRemoveChange = NodeRemoveChange;
|
||||
export type EdgeAddChange<EdgeType extends Edge = Edge> = {
|
||||
item: EdgeType;
|
||||
type: 'add';
|
||||
};
|
||||
|
||||
export type EdgeReplaceChange<EdgeType extends Edge = Edge> = {
|
||||
id: string;
|
||||
item: EdgeType;
|
||||
type: 'replace';
|
||||
};
|
||||
|
||||
export type EdgeChange<EdgeType extends Edge = Edge> =
|
||||
| EdgeSelectionChange
|
||||
| EdgeRemoveChange
|
||||
| EdgeAddChange<EdgeType>
|
||||
| EdgeReplaceChange<EdgeType>;
|
||||
@@ -12,9 +12,11 @@ import {
|
||||
XYPosition,
|
||||
OnBeforeDeleteBase,
|
||||
Connection,
|
||||
NodeChange,
|
||||
EdgeChange,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.';
|
||||
import type { Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.';
|
||||
|
||||
export type OnNodesChange<NodeType extends Node = Node> = (changes: NodeChange<NodeType>[]) => void;
|
||||
export type OnEdgesChange<EdgeType extends Edge = Edge> = (changes: EdgeChange<EdgeType>[]) => void;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export * from './nodes';
|
||||
export * from './edges';
|
||||
export * from './changes';
|
||||
export * from './component-props';
|
||||
export * from './general';
|
||||
export * from './store';
|
||||
|
||||
@@ -25,12 +25,13 @@ import {
|
||||
type EdgeLookup,
|
||||
type ConnectionLookup,
|
||||
type NodeLookup,
|
||||
NodeChange,
|
||||
EdgeChange,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type {
|
||||
Edge,
|
||||
Node,
|
||||
NodeChange,
|
||||
OnNodesChange,
|
||||
OnEdgesChange,
|
||||
DefaultEdgeOptions,
|
||||
@@ -43,7 +44,6 @@ import type {
|
||||
OnNodeDrag,
|
||||
OnBeforeDelete,
|
||||
IsValidConnection,
|
||||
EdgeChange,
|
||||
InternalNode,
|
||||
} from '.';
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { EdgeLookup, NodeLookup } from '@xyflow/system';
|
||||
import type {
|
||||
Node,
|
||||
Edge,
|
||||
import {
|
||||
EdgeLookup,
|
||||
NodeLookup,
|
||||
EdgeChange,
|
||||
NodeChange,
|
||||
NodeSelectionChange,
|
||||
EdgeSelectionChange,
|
||||
InternalNode,
|
||||
} from '../types';
|
||||
} from '@xyflow/system';
|
||||
import type { Node, Edge, InternalNode } from '../types';
|
||||
|
||||
// This function applies changes to nodes or edges that are triggered by React Flow internally.
|
||||
// When you drag a node for example, React Flow will send a position change update.
|
||||
@@ -97,9 +96,9 @@ function applyChange(change: any, element: any): any {
|
||||
|
||||
case 'dimensions': {
|
||||
if (typeof change.dimensions !== 'undefined') {
|
||||
element.computed ??= {};
|
||||
element.computed.width = change.dimensions.width;
|
||||
element.computed.height = change.dimensions.height;
|
||||
element.measured ??= {};
|
||||
element.measured.width = change.dimensions.width;
|
||||
element.measured.height = change.dimensions.height;
|
||||
|
||||
if (change.resizing) {
|
||||
element.width = change.dimensions.width;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type Writable
|
||||
} from 'svelte/store';
|
||||
import {
|
||||
adoptUserProvidedNodes,
|
||||
adoptUserNodes,
|
||||
updateConnectionLookup,
|
||||
type Viewport,
|
||||
type PanZoomInstance,
|
||||
@@ -141,7 +141,7 @@ export const createNodesStore = (
|
||||
let elevateNodesOnSelect = true;
|
||||
|
||||
const _set = (nds: Node[]): Node[] => {
|
||||
const nextNodes = adoptUserProvidedNodes(nds, nodeLookup, {
|
||||
const nextNodes = adoptUserNodes(nds, nodeLookup, {
|
||||
elevateNodesOnSelect,
|
||||
defaults
|
||||
});
|
||||
|
||||
@@ -127,7 +127,7 @@ export type SelectionRect = Rect & {
|
||||
|
||||
export type OnError = (id: string, message: string) => void;
|
||||
|
||||
export type UpdateNodePositions = (dragItems: NodeDragItem[] | NodeBase[], dragging?: boolean) => void;
|
||||
export type UpdateNodePositions = (dragItems: NodeDragItem[] | InternalNodeBase[], dragging?: boolean) => void;
|
||||
export type PanBy = (delta: XYPosition) => boolean;
|
||||
|
||||
export type UpdateConnection = (params: {
|
||||
|
||||
@@ -59,27 +59,25 @@ export type NodeBase<
|
||||
*/
|
||||
origin?: NodeOrigin;
|
||||
handles?: NodeHandle[];
|
||||
computed?: {
|
||||
measured?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type InternalNodeBase<NodeType extends NodeBase = NodeBase> = NodeType & {
|
||||
computed: {
|
||||
measured: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
// Only used internally
|
||||
internals: {
|
||||
positionAbsolute: XYPosition;
|
||||
z: number;
|
||||
// @todo should we rename this to "handles" and use same type as node.handles?
|
||||
isParent: boolean;
|
||||
/** Holds a reference to the original node object provided by the user
|
||||
* (which may lack some fields, like `computed` or `[internalSymbol]`. Used
|
||||
* as an optimization to avoid certain operations. */
|
||||
userProvidedNode: NodeType;
|
||||
/** Holds a reference to the original node object provided by the user.
|
||||
* Used as an optimization to avoid certain operations. */
|
||||
userNode: NodeType;
|
||||
handleBounds?: NodeHandleBounds;
|
||||
};
|
||||
};
|
||||
@@ -123,7 +121,7 @@ export type NodeDragItem = {
|
||||
position: XYPosition;
|
||||
// distance from the mouse cursor to the node when start dragging
|
||||
distance: XYPosition;
|
||||
computed: {
|
||||
measured: {
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ function isNodeInitialized(node: InternalNodeBase): boolean {
|
||||
return (
|
||||
node &&
|
||||
!!(node.internals.handleBounds || node.handles?.length) &&
|
||||
!!(node.computed.width || node.width || node.initialWidth)
|
||||
!!(node.measured.width || node.width || node.initialWidth)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,23 +66,23 @@ export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
|
||||
height: y2 - y,
|
||||
});
|
||||
|
||||
export const nodeToRect = (node: InternalNodeBase, nodeOrigin: NodeOrigin = [0, 0]): Rect => {
|
||||
export const nodeToRect = (node: InternalNodeBase | NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Rect => {
|
||||
const { positionAbsolute } = getNodePositionWithOrigin(node, node.origin || nodeOrigin);
|
||||
|
||||
return {
|
||||
...positionAbsolute,
|
||||
width: node.computed?.width ?? node.width ?? 0,
|
||||
height: node.computed?.height ?? node.height ?? 0,
|
||||
width: node.measured?.width ?? node.width ?? 0,
|
||||
height: node.measured?.height ?? node.height ?? 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const nodeToBox = (node: InternalNodeBase, nodeOrigin: NodeOrigin = [0, 0]): Box => {
|
||||
export const nodeToBox = (node: InternalNodeBase | NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Box => {
|
||||
const { positionAbsolute } = getNodePositionWithOrigin(node, node.origin || nodeOrigin);
|
||||
|
||||
return {
|
||||
...positionAbsolute,
|
||||
x2: positionAbsolute.x + (node.computed?.width ?? node.width ?? 0),
|
||||
y2: positionAbsolute.y + (node.computed?.height ?? node.height ?? 0),
|
||||
x2: positionAbsolute.x + (node.measured?.width ?? node.width ?? 0),
|
||||
y2: positionAbsolute.y + (node.measured?.height ?? node.height ?? 0),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -208,14 +208,14 @@ export function getNodeDimensions<NodeType extends NodeBase = NodeBase>(
|
||||
node: NodeType
|
||||
): { width: number; height: number } {
|
||||
return {
|
||||
width: node.computed?.width ?? node.width ?? node.initialWidth ?? 0,
|
||||
height: node.computed?.height ?? node.height ?? node.initialHeight ?? 0,
|
||||
width: node.measured?.width ?? node.width ?? node.initialWidth ?? 0,
|
||||
height: node.measured?.height ?? node.height ?? node.initialHeight ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function nodeHasDimensions<NodeType extends NodeBase = NodeBase>(node: NodeType): boolean {
|
||||
return (
|
||||
(node.computed?.width ?? node.width ?? node.initialWidth) !== undefined &&
|
||||
(node.computed?.height ?? node.height ?? node.initialHeight) !== undefined
|
||||
(node.measured?.width ?? node.width ?? node.initialWidth) !== undefined &&
|
||||
(node.measured?.height ?? node.height ?? node.initialHeight) !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export const isNodeBase = <NodeType extends NodeBase = NodeBase>(element: any):
|
||||
|
||||
export const isInternalNodeBase = <NodeType extends InternalNodeBase = InternalNodeBase>(
|
||||
element: any
|
||||
): element is NodeType => 'id' in element && 'computed' in element && !('source' in element) && !('target' in element);
|
||||
): element is NodeType => 'id' in element && 'internals' in element && !('source' in element) && !('target' in element);
|
||||
|
||||
/**
|
||||
* Pass in a node, and get connected nodes where edge.source === node.id
|
||||
@@ -106,7 +106,7 @@ export const getIncomers = <NodeType extends NodeBase = NodeBase, EdgeType exten
|
||||
};
|
||||
|
||||
export const getNodePositionWithOrigin = (
|
||||
node: InternalNodeBase | undefined,
|
||||
node: InternalNodeBase | NodeBase | undefined,
|
||||
nodeOrigin: NodeOrigin = [0, 0]
|
||||
): { position: XYPosition; positionAbsolute: XYPosition } => {
|
||||
if (!node) {
|
||||
@@ -133,10 +133,13 @@ export const getNodePositionWithOrigin = (
|
||||
|
||||
return {
|
||||
position,
|
||||
positionAbsolute: {
|
||||
x: node.internals.positionAbsolute.x - offsetX,
|
||||
y: node.internals.positionAbsolute.y - offsetY,
|
||||
},
|
||||
positionAbsolute:
|
||||
'internals' in node
|
||||
? {
|
||||
x: node.internals.positionAbsolute.x - offsetX,
|
||||
y: node.internals.positionAbsolute.y - offsetY,
|
||||
}
|
||||
: position,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -165,7 +168,6 @@ export const getNodesBounds = (
|
||||
|
||||
const box = nodes.reduce(
|
||||
(currBox, node) => {
|
||||
// @ts-expect-error
|
||||
const nodePos = getNodePositionWithOrigin(node, node.origin || params.nodeOrigin);
|
||||
return getBoundsOfBoxes(
|
||||
currBox,
|
||||
@@ -238,9 +240,9 @@ export const getNodesInside = <NodeType extends NodeBase = NodeBase>(
|
||||
const visibleNodes: NodeType[] = [];
|
||||
|
||||
for (const [, node] of nodeLookup) {
|
||||
const { computed, selectable = true, hidden = false } = node;
|
||||
const width = computed?.width ?? node.width ?? node.initialWidth ?? null;
|
||||
const height = computed?.height ?? node.height ?? node.initialHeight ?? null;
|
||||
const { measured, selectable = true, hidden = false } = node;
|
||||
const width = measured.width ?? node.width ?? node.initialWidth ?? null;
|
||||
const height = measured.height ?? node.height ?? node.initialHeight ?? null;
|
||||
|
||||
if ((excludeNonSelectableNodes && !selectable) || hidden) {
|
||||
continue;
|
||||
@@ -286,7 +288,7 @@ export function fitView<Params extends FitViewParamsBase<NodeBase>, Options exte
|
||||
const filteredNodes: InternalNodeBase[] = [];
|
||||
|
||||
nodeLookup.forEach((n) => {
|
||||
const isVisible = n.computed?.width && n.computed?.height && (options?.includeHiddenNodes || !n.hidden);
|
||||
const isVisible = n.measured.width && n.measured.height && (options?.includeHiddenNodes || !n.hidden);
|
||||
|
||||
if (
|
||||
isVisible &&
|
||||
@@ -331,7 +333,7 @@ function clampNodeExtent<NodeType extends NodeBase>(
|
||||
if (!extent || extent === 'parent') {
|
||||
return extent;
|
||||
}
|
||||
return [extent[0], [extent[1][0] - (node.computed?.width ?? 0), extent[1][1] - (node.computed?.height ?? 0)]];
|
||||
return [extent[0], [extent[1][0] - (node.measured?.width ?? 0), extent[1][1] - (node.measured?.height ?? 0)]];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,10 +369,10 @@ export function calculateNodePosition<NodeType extends NodeBase>({
|
||||
if (!parentNode) {
|
||||
onError?.('005', errorMessages['error005']());
|
||||
} else {
|
||||
const nodeWidth = node.computed.width;
|
||||
const nodeHeight = node.computed.height;
|
||||
const parentWidth = parentNode.computed.width;
|
||||
const parentHeight = parentNode.computed.height;
|
||||
const nodeWidth = node.measured.width;
|
||||
const nodeHeight = node.measured.height;
|
||||
const parentWidth = parentNode.measured.width;
|
||||
const parentHeight = parentNode.measured.height;
|
||||
|
||||
if (nodeWidth && nodeHeight && parentWidth && parentHeight) {
|
||||
const currNodeOrigin = node.origin || nodeOrigin;
|
||||
|
||||
@@ -70,7 +70,7 @@ type UpdateNodesOptions<NodeType extends NodeBase> = {
|
||||
defaults?: Partial<NodeType>;
|
||||
};
|
||||
|
||||
export function adoptUserProvidedNodes<NodeType extends NodeBase>(
|
||||
export function adoptUserNodes<NodeType extends NodeBase>(
|
||||
nodes: NodeType[],
|
||||
nodeLookup: Map<string, InternalNodeBase<NodeType>>,
|
||||
options: UpdateNodesOptions<NodeType> = {
|
||||
@@ -84,33 +84,31 @@ export function adoptUserProvidedNodes<NodeType extends NodeBase>(
|
||||
const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0;
|
||||
const parentNodeIds = new Set<string>();
|
||||
|
||||
nodes.forEach((n) => {
|
||||
const currentStoreNode = tmpLookup.get(n.id);
|
||||
nodes.forEach((userNode) => {
|
||||
const currentStoreNode = tmpLookup.get(userNode.id);
|
||||
|
||||
if (n.parentNode) {
|
||||
parentNodeIds.add(n.parentNode);
|
||||
if (userNode.parentNode) {
|
||||
parentNodeIds.add(userNode.parentNode);
|
||||
}
|
||||
|
||||
if (n === currentStoreNode?.internals.userProvidedNode) {
|
||||
nodeLookup.set(n.id, currentStoreNode);
|
||||
if (userNode === currentStoreNode?.internals.userNode) {
|
||||
nodeLookup.set(userNode.id, currentStoreNode);
|
||||
} else {
|
||||
const node: InternalNodeBase<NodeType> = {
|
||||
nodeLookup.set(userNode.id, {
|
||||
...options.defaults,
|
||||
...n,
|
||||
computed: {
|
||||
width: n.computed?.width,
|
||||
height: n.computed?.height,
|
||||
...userNode,
|
||||
measured: {
|
||||
width: userNode.measured?.width,
|
||||
height: userNode.measured?.height,
|
||||
},
|
||||
internals: {
|
||||
positionAbsolute: n.position,
|
||||
positionAbsolute: userNode.position,
|
||||
handleBounds: currentStoreNode?.internals?.handleBounds,
|
||||
z: (isNumeric(n.zIndex) ? n.zIndex : 0) + (n.selected ? selectedNodeZ : 0),
|
||||
userProvidedNode: n,
|
||||
z: (isNumeric(userNode.zIndex) ? userNode.zIndex : 0) + (userNode.selected ? selectedNodeZ : 0),
|
||||
userNode,
|
||||
isParent: false,
|
||||
},
|
||||
};
|
||||
|
||||
nodeLookup.set(node.id, node);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -241,14 +239,14 @@ export function updateNodeDimensions<NodeType extends InternalNodeBase>(
|
||||
const doUpdate = !!(
|
||||
dimensions.width &&
|
||||
dimensions.height &&
|
||||
(node.computed?.width !== dimensions.width || node.computed?.height !== dimensions.height || update.force)
|
||||
(node.measured?.width !== dimensions.width || node.measured?.height !== dimensions.height || update.force)
|
||||
);
|
||||
|
||||
if (doUpdate) {
|
||||
const newNode = {
|
||||
...node,
|
||||
computed: {
|
||||
...node.computed,
|
||||
measured: {
|
||||
...node.measured,
|
||||
...dimensions,
|
||||
},
|
||||
internals: {
|
||||
|
||||
@@ -139,10 +139,10 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
|
||||
if (dragItems.length > 1 && nodeExtent && !n.extent) {
|
||||
const { positionAbsolute } = n.internals;
|
||||
const x1 = positionAbsolute.x - nodesBox.x + nodeExtent[0][0];
|
||||
const x2 = positionAbsolute.x + (n.computed?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0];
|
||||
const x2 = positionAbsolute.x + (n.measured?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0];
|
||||
|
||||
const y1 = positionAbsolute.y - nodesBox.y + nodeExtent[0][1];
|
||||
const y2 = positionAbsolute.y + (n.computed?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1];
|
||||
const y2 = positionAbsolute.y + (n.measured?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1];
|
||||
|
||||
adjustedNodeExtent = [
|
||||
[x1, y1],
|
||||
|
||||
@@ -65,9 +65,9 @@ export function getDragItems<NodeType extends NodeBase>(
|
||||
internals: {
|
||||
positionAbsolute: internalNode.internals.positionAbsolute || { x: 0, y: 0 },
|
||||
},
|
||||
computed: {
|
||||
width: internalNode.computed.width || 0,
|
||||
height: internalNode.computed.height || 0,
|
||||
measured: {
|
||||
width: internalNode.measured.width || 0,
|
||||
height: internalNode.measured.height || 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -93,8 +93,8 @@ export function getEventHandlerParams<NodeType extends NodeBase>({
|
||||
return {
|
||||
...node,
|
||||
position: n.position,
|
||||
computed: {
|
||||
...n.computed,
|
||||
measured: {
|
||||
...n.measured,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -71,15 +71,15 @@ export type XYResizerInstance = {
|
||||
function nodeToParentExtent(node: NodeBase): CoordinateExtent {
|
||||
return [
|
||||
[0, 0],
|
||||
[node.computed!.width!, node.computed!.height!],
|
||||
[node.measured!.width!, node.measured!.height!],
|
||||
];
|
||||
}
|
||||
|
||||
function nodeToChildExtent(child: NodeBase, parent: NodeBase, nodeOrigin: NodeOrigin): CoordinateExtent {
|
||||
const x = parent.position.x + child.position.x;
|
||||
const y = parent.position.y + child.position.y;
|
||||
const width = child.computed!.width! ?? 0;
|
||||
const height = child.computed!.height! ?? 0;
|
||||
const width = child.measured!.width! ?? 0;
|
||||
const height = child.measured!.height! ?? 0;
|
||||
const originOffsetX = nodeOrigin[0] * width;
|
||||
const originOffsetY = nodeOrigin[1] * height;
|
||||
|
||||
@@ -121,8 +121,8 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange }: XYResize
|
||||
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
||||
|
||||
prevValues = {
|
||||
width: node.computed?.width ?? 0,
|
||||
height: node.computed?.height ?? 0,
|
||||
width: node.measured?.width ?? 0,
|
||||
height: node.measured?.height ?? 0,
|
||||
x: node.position.x ?? 0,
|
||||
y: node.position.y ?? 0,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user