refactor(react): use array for nodeInternals, rename to nodes

This commit is contained in:
moklick
2023-06-12 16:56:02 +02:00
parent cefdfd4e70
commit c6501aff62
33 changed files with 146 additions and 197 deletions
@@ -4,8 +4,8 @@ import { useStore, getStraightPath, EdgeProps } from '@xyflow/react';
import { getEdgeParams } from './utils.js'; import { getEdgeParams } from './utils.js';
function FloatingEdge({ id, source, target, markerEnd, style }: EdgeProps) { function FloatingEdge({ id, source, target, markerEnd, style }: EdgeProps) {
const sourceNode = useStore(useCallback((store) => store.nodeInternals.get(source), [source])); const sourceNode = useStore(useCallback((store) => store.nodes.find((n) => n.id === source), [source]));
const targetNode = useStore(useCallback((store) => store.nodeInternals.get(target), [target])); const targetNode = useStore(useCallback((store) => store.nodes.find((n) => n.id === target), [target]));
if (!sourceNode || !targetNode) { if (!sourceNode || !targetNode) {
return null; return null;
@@ -14,7 +14,7 @@ const CustomEdge: FC<EdgeProps> = ({
data, data,
}) => { }) => {
const isConnectedNodeDragging = useStore((s) => const isConnectedNodeDragging = useStore((s) =>
s.getNodes().find((n) => n.dragging && (target === n.id || source === n.id)) s.nodes.find((n) => n.dragging && (target === n.id || source === n.id))
); );
const [edgePath, labelX, labelY] = getBezierPath({ const [edgePath, labelX, labelY] = getBezierPath({
@@ -14,7 +14,7 @@ const CustomEdge: FC<EdgeProps> = ({
data, data,
}) => { }) => {
const isConnectedNodeDragging = useStore((s) => const isConnectedNodeDragging = useStore((s) =>
s.getNodes().find((n) => n.dragging && (target === n.id || source === n.id)) s.nodes.find((n) => n.dragging && (target === n.id || source === n.id))
); );
const [edgePath, labelX, labelY] = getBezierPath({ const [edgePath, labelX, labelY] = getBezierPath({
@@ -1,15 +1,15 @@
import { FC, useMemo, CSSProperties } from 'react'; import { FC, CSSProperties } from 'react';
import { EdgeProps, useStore, getBezierPath, ReactFlowState } from '@xyflow/react'; import { EdgeProps, useStore, getBezierPath } from '@xyflow/react';
import { getEdgeParams } from './utils'; import { getEdgeParams } from './utils';
const nodeSelector = (s: ReactFlowState) => s.nodeInternals;
const FloatingEdge: FC<EdgeProps> = ({ id, source, target, style }) => { const FloatingEdge: FC<EdgeProps> = ({ id, source, target, style }) => {
const nodeInternals = useStore(nodeSelector); const { sourceNode, targetNode } = useStore((s) => {
const sourceNode = s.nodes.find((n) => n.id === source);
const targetNode = s.nodes.find((n) => n.id === target);
const sourceNode = useMemo(() => nodeInternals.get(source), [source, nodeInternals]); return { sourceNode, targetNode };
const targetNode = useMemo(() => nodeInternals.get(target), [target, nodeInternals]); });
if (!sourceNode || !targetNode) { if (!sourceNode || !targetNode) {
return null; return null;
@@ -1,10 +1,7 @@
import { NodeToolbar, ReactFlowState, useStore } from '@xyflow/react'; import { NodeToolbar, ReactFlowState, useStore } from '@xyflow/react';
const selectedNodesSelector = (state: ReactFlowState) => const selectedNodesSelector = (state: ReactFlowState) =>
state state.nodes.filter((node) => node.selected).map((node) => node.id);
.getNodes()
.filter((node) => node.selected)
.map((node) => node.id);
export default function SelectedNodesToolbar() { export default function SelectedNodesToolbar() {
const selectedNodeIds = useStore(selectedNodesSelector); const selectedNodeIds = useStore(selectedNodesSelector);
@@ -5,7 +5,7 @@ import styles from './provider.module.css';
const Sidebar = () => { const Sidebar = () => {
const { setNodes } = useReactFlow(); const { setNodes } = useReactFlow();
const nodeInfos = useStore((store) => const nodeInfos = useStore((store) =>
store.getNodes().map((n) => `Node ${n.id} - x: ${n.position.x.toFixed(2)}, y: ${n.position.y.toFixed(2)}`) store.nodes.map((n) => `Node ${n.id} - x: ${n.position.x.toFixed(2)}, y: ${n.position.y.toFixed(2)}`)
); );
const transform = useStore((store) => store.transform); const transform = useStore((store) => store.transform);
@@ -16,7 +16,6 @@ const defaultWidth = 200;
const defaultHeight = 150; const defaultHeight = 150;
const selector = (s: ReactFlowState) => { const selector = (s: ReactFlowState) => {
const nodes = s.getNodes();
const viewBB: Rect = { const viewBB: Rect = {
x: -s.transform[0] / s.transform[2], x: -s.transform[0] / s.transform[2],
y: -s.transform[1] / s.transform[2], y: -s.transform[1] / s.transform[2],
@@ -26,7 +25,7 @@ const selector = (s: ReactFlowState) => {
return { return {
viewBB, viewBB,
boundingRect: nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(nodes, s.nodeOrigin), viewBB) : viewBB, boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(s.nodes, s.nodeOrigin), viewBB) : viewBB,
rfId: s.rfId, rfId: s.rfId,
nodeOrigin: s.nodeOrigin, nodeOrigin: s.nodeOrigin,
panZoom: s.panZoom, panZoom: s.panZoom,
@@ -118,7 +117,7 @@ function MiniMap({
const onSvgNodeClick = onNodeClick const onSvgNodeClick = onNodeClick
? useCallback((event: MouseEvent, nodeId: string) => { ? useCallback((event: MouseEvent, nodeId: string) => {
const node = store.getState().nodeInternals.get(nodeId)!; const node = store.getState().nodes.find((n) => n.id === nodeId)!;
onNodeClick(event, node); onNodeClick(event, node);
}, []) }, [])
: undefined; : undefined;
@@ -12,7 +12,7 @@ import type { MiniMapNodes, GetMiniMapNodeAttribute } from './types';
declare const window: any; declare const window: any;
const selector = (s: ReactFlowState) => s.nodeOrigin; const selector = (s: ReactFlowState) => s.nodeOrigin;
const selectorNodes = (s: ReactFlowState) => s.getNodes().filter((node) => !node.hidden && node.width && node.height); const selectorNodes = (s: ReactFlowState) => s.nodes.filter((node) => !node.hidden && node.width && node.height);
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func); const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
function MiniMapNodes({ function MiniMapNodes({
@@ -65,8 +65,8 @@ function ResizeControl({
const dragHandler = drag<HTMLDivElement, unknown>() const dragHandler = drag<HTMLDivElement, unknown>()
.on('start', (event: ResizeDragEvent) => { .on('start', (event: ResizeDragEvent) => {
const { nodeInternals, transform, snapGrid, snapToGrid } = store.getState(); const { nodes, transform, snapGrid, snapToGrid } = store.getState();
const node = nodeInternals.get(id); const node = nodes.find((n) => n.id === id);
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
prevValues.current = { prevValues.current = {
@@ -86,9 +86,9 @@ function ResizeControl({
onResizeStart?.(event, { ...prevValues.current }); onResizeStart?.(event, { ...prevValues.current });
}) })
.on('drag', (event: ResizeDragEvent) => { .on('drag', (event: ResizeDragEvent) => {
const { nodeInternals, transform, snapGrid, snapToGrid, triggerNodeChanges } = store.getState(); const { nodes, transform, snapGrid, snapToGrid, triggerNodeChanges } = store.getState();
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
const node = nodeInternals.get(id); const node = nodes.find((n) => n.id === id);
if (node) { if (node) {
const changes: NodeChange[] = []; const changes: NodeChange[] = [];
@@ -24,7 +24,7 @@ const nodesEqualityFn = (a: Node[], b: Node[]) => {
const storeSelector = (state: ReactFlowState) => ({ const storeSelector = (state: ReactFlowState) => ({
transform: state.transform, transform: state.transform,
nodeOrigin: state.nodeOrigin, nodeOrigin: state.nodeOrigin,
selectedNodesCount: state.getNodes().filter((node) => node.selected).length, selectedNodesCount: state.nodes.filter((node) => node.selected).length,
}); });
function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number, align: Align): string { function getTransform(nodeRect: Rect, transform: Transform, position: Position, offset: number, align: Align): string {
@@ -87,7 +87,7 @@ function NodeToolbar({
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || '']; const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ''];
return nodeIds.reduce<Node[]>((acc, id) => { return nodeIds.reduce<Node[]>((acc, id) => {
const node = state.nodeInternals.get(id); const node = state.nodes.find((n) => n.id === id);
if (node) { if (node) {
acc.push(node); acc.push(node);
} }
@@ -43,7 +43,7 @@ const ConnectionLine = ({
const { fromNode, handleId, toX, toY, connectionMode } = useStore( const { fromNode, handleId, toX, toY, connectionMode } = useStore(
useCallback( useCallback(
(s: ReactFlowStore) => ({ (s: ReactFlowStore) => ({
fromNode: s.nodeInternals.get(nodeId), fromNode: s.nodes.find((n) => n.id === nodeId),
handleId: s.connectionStartHandle?.handleId, handleId: s.connectionStartHandle?.handleId,
toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2], toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2], toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
@@ -54,8 +54,8 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
const [updating, setUpdating] = useState<boolean>(false); const [updating, setUpdating] = useState<boolean>(false);
const store = useStoreApi(); const store = useStoreApi();
const edgePosition = useStore((state) => { const edgePosition = useStore((state) => {
const sourceNode = state.nodeInternals.get(source); const sourceNode = state.nodes.find((n) => n.id === source);
const targetNode = state.nodeInternals.get(target); const targetNode = state.nodes.find((n) => n.id === target);
if (!sourceNode || !targetNode) { if (!sourceNode || !targetNode) {
return null; return null;
@@ -118,7 +118,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
onConnectStart, onConnectStart,
onConnectEnd, onConnectEnd,
cancelConnection, cancelConnection,
getNodes, nodes,
panBy, panBy,
updateConnection, updateConnection,
} = store.getState(); } = store.getState();
@@ -128,7 +128,6 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
const isTarget = isSourceHandle; const isTarget = isSourceHandle;
const edge = edges.find((e) => e.id === id)!; const edge = edges.find((e) => e.id === id)!;
const nodes = getNodes();
setUpdating(true); setUpdating(true);
onEdgeUpdateStart?.(event, edge, handleType); onEdgeUpdateStart?.(event, edge, handleType);
@@ -106,7 +106,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
connectionMode: currentStore.connectionMode, connectionMode: currentStore.connectionMode,
connectionRadius: currentStore.connectionRadius, connectionRadius: currentStore.connectionRadius,
domNode: currentStore.domNode, domNode: currentStore.domNode,
nodes: currentStore.getNodes(), nodes: currentStore.nodes,
lib: currentStore.lib, lib: currentStore.lib,
isTarget, isTarget,
handleId, handleId,
+3 -3
View File
@@ -11,7 +11,7 @@ export function getMouseHandler(
return handler === undefined return handler === undefined
? handler ? handler
: (event: MouseEvent) => { : (event: MouseEvent) => {
const node = getState().nodeInternals.get(id)!; const node = getState().nodes.find((n) => n.id === id)!;
handler(event, { ...node }); handler(event, { ...node });
}; };
} }
@@ -34,8 +34,8 @@ export function handleNodeClick({
unselect?: boolean; unselect?: boolean;
nodeRef?: RefObject<HTMLDivElement>; nodeRef?: RefObject<HTMLDivElement>;
}) { }) {
const { addSelectedNodes, unselectNodesAndEdges, multiSelectionActive, nodeInternals } = store.getState(); const { addSelectedNodes, unselectNodesAndEdges, multiSelectionActive, nodes } = store.getState();
const node = nodeInternals.get(id)!; const node = nodes.find((n) => n.id === id)!;
store.setState({ nodesSelectionActive: false }); store.setState({ nodesSelectionActive: false });
@@ -78,7 +78,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
} }
if (onClick) { if (onClick) {
const node = store.getState().nodeInternals.get(id)!; const node = store.getState().nodes.find((n) => n.id === id)!;
onClick(event, { ...node }); onClick(event, { ...node });
} }
}; };
@@ -21,7 +21,7 @@ export type NodesSelectionProps = {
}; };
const selector = (s: ReactFlowState) => { const selector = (s: ReactFlowState) => {
const selectedNodes = s.getNodes().filter((n) => n.selected); const selectedNodes = s.nodes.filter((n) => n.selected);
const { width, height, x, y } = getRectOfNodes(selectedNodes, s.nodeOrigin); const { width, height, x, y } = getRectOfNodes(selectedNodes, s.nodeOrigin);
return { return {
@@ -57,10 +57,7 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
const onContextMenu = onSelectionContextMenu const onContextMenu = onSelectionContextMenu
? (event: MouseEvent) => { ? (event: MouseEvent) => {
const selectedNodes = store const selectedNodes = store.getState().nodes.filter((n) => n.selected);
.getState()
.getNodes()
.filter((n) => n.selected);
onSelectionContextMenu(event, selectedNodes); onSelectionContextMenu(event, selectedNodes);
} }
: undefined; : undefined;
@@ -9,7 +9,7 @@ type SelectionListenerProps = {
}; };
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
selectedNodes: s.getNodes().filter((n) => n.selected), selectedNodes: s.nodes.filter((n) => n.selected),
selectedEdges: s.edges.filter((e) => e.selected), selectedEdges: s.edges.filter((e) => e.selected),
}); });
+1 -3
View File
@@ -129,8 +129,7 @@ const Pane = memo(
}; };
const onMouseMove = (event: ReactMouseEvent): void => { const onMouseMove = (event: ReactMouseEvent): void => {
const { userSelectionRect, edges, transform, onNodesChange, onEdgesChange, nodeOrigin, getNodes } = const { userSelectionRect, edges, transform, nodeOrigin, nodes, onNodesChange, onEdgesChange } = store.getState();
store.getState();
if (!isSelecting || !containerBounds.current || !userSelectionRect) { if (!isSelecting || !containerBounds.current || !userSelectionRect) {
return; return;
} }
@@ -149,7 +148,6 @@ const Pane = memo(
height: Math.abs(mousePos.y - startY), height: Math.abs(mousePos.y - startY),
}; };
const nodes = getNodes();
const selectedNodes = getNodesInside<Node>( const selectedNodes = getNodesInside<Node>(
nodes, nodes,
nextUserSelectRect, nextUserSelectRect,
+1 -8
View File
@@ -22,14 +22,7 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
if (nodeRef?.current) { if (nodeRef?.current) {
xyDrag.current = XYDrag({ xyDrag.current = XYDrag({
domNode: nodeRef.current, domNode: nodeRef.current,
getStoreItems: () => { getStoreItems: () => store.getState(),
const currentStore = store.getState();
return {
nodes: currentStore.getNodes(),
...store.getState(),
};
},
onNodeClick: () => { onNodeClick: () => {
if (nodeId) { if (nodeId) {
handleNodeClick({ handleNodeClick({
@@ -4,6 +4,9 @@ import type { KeyCode } from '@xyflow/system';
import { useStoreApi } from '../hooks/useStore'; import { useStoreApi } from '../hooks/useStore';
import useKeyPress from './useKeyPress'; import useKeyPress from './useKeyPress';
import useReactFlow from './useReactFlow'; import useReactFlow from './useReactFlow';
import { Edge, Node } from '../types';
const getSelected = (item: Node | Edge) => item.selected;
export default ({ export default ({
deleteKeyCode, deleteKeyCode,
@@ -20,10 +23,8 @@ export default ({
useEffect(() => { useEffect(() => {
if (deleteKeyPressed) { if (deleteKeyPressed) {
const { edges, getNodes } = store.getState(); const { edges, nodes } = store.getState();
const selectedNodes = getNodes().filter((node) => node.selected); deleteElements({ nodes: nodes.filter(getSelected), edges: edges.filter(getSelected) });
const selectedEdges = edges.filter((edge) => edge.selected);
deleteElements({ nodes: selectedNodes, edges: selectedEdges });
store.setState({ nodesSelectionActive: false }); store.setState({ nodesSelectionActive: false });
} }
}, [deleteKeyPressed]); }, [deleteKeyPressed]);
+1 -1
View File
@@ -3,7 +3,7 @@ import { shallow } from 'zustand/shallow';
import { useStore } from '../hooks/useStore'; import { useStore } from '../hooks/useStore';
import type { Node, ReactFlowState } from '../types'; import type { Node, ReactFlowState } from '../types';
const nodesSelector = (state: ReactFlowState) => state.getNodes(); const nodesSelector = (state: ReactFlowState) => state.nodes;
function useNodes<NodeData>(): Node<NodeData>[] { function useNodes<NodeData>(): Node<NodeData>[] {
const nodes = useStore(nodesSelector, shallow); const nodes = useStore(nodesSelector, shallow);
@@ -8,12 +8,11 @@ export type UseNodesInitializedOptions = {
}; };
const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) => { const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) => {
if (s.nodeInternals.size === 0) { if (s.nodes.length === 0) {
return false; return false;
} }
return s return s.nodes
.getNodes()
.filter((n) => (options.includeHiddenNodes ? true : !n.hidden)) .filter((n) => (options.includeHiddenNodes ? true : !n.hidden))
.every((n) => n[internalsSymbol]?.handleBounds !== undefined); .every((n) => n[internalsSymbol]?.handleBounds !== undefined);
}; };
+11 -21
View File
@@ -23,14 +23,11 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
const store = useStoreApi(); const store = useStoreApi();
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => { const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
return store return store.getState().nodes.map((n) => ({ ...n }));
.getState()
.getNodes()
.map((n) => ({ ...n }));
}, []); }, []);
const getNode = useCallback<Instance.GetNode<NodeData>>((id) => { const getNode = useCallback<Instance.GetNode<NodeData>>((id) => {
return store.getState().nodeInternals.get(id); return store.getState().nodes.find((n) => n.id === id);
}, []); }, []);
const getEdges = useCallback<Instance.GetEdges<EdgeData>>(() => { const getEdges = useCallback<Instance.GetEdges<EdgeData>>(() => {
@@ -44,8 +41,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
}, []); }, []);
const setNodes = useCallback<Instance.SetNodes<NodeData>>((payload) => { const setNodes = useCallback<Instance.SetNodes<NodeData>>((payload) => {
const { getNodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState(); const { nodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
const nodes = getNodes();
const nextNodes = typeof payload === 'function' ? payload(nodes) : payload; const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
if (hasDefaultNodes) { if (hasDefaultNodes) {
@@ -76,10 +72,9 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
const addNodes = useCallback<Instance.AddNodes<NodeData>>((payload) => { const addNodes = useCallback<Instance.AddNodes<NodeData>>((payload) => {
const nodes = Array.isArray(payload) ? payload : [payload]; const nodes = Array.isArray(payload) ? payload : [payload];
const { getNodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState(); const { nodes: currentNodes, hasDefaultNodes, onNodesChange, setNodes } = store.getState();
if (hasDefaultNodes) { if (hasDefaultNodes) {
const currentNodes = getNodes();
const nextNodes = [...currentNodes, ...nodes]; const nextNodes = [...currentNodes, ...nodes];
setNodes(nextNodes); setNodes(nextNodes);
} else if (onNodesChange) { } else if (onNodesChange) {
@@ -101,10 +96,10 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
}, []); }, []);
const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => { const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => {
const { getNodes, edges = [], transform } = store.getState(); const { nodes = [], edges = [], transform } = store.getState();
const [x, y, zoom] = transform; const [x, y, zoom] = transform;
return { return {
nodes: getNodes().map((n) => ({ ...n })), nodes: nodes.map((n) => ({ ...n })),
edges: edges.map((e) => ({ ...e })), edges: edges.map((e) => ({ ...e })),
viewport: { viewport: {
x, x,
@@ -116,8 +111,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
const deleteElements = useCallback<Instance.DeleteElements>(({ nodes: nodesDeleted, edges: edgesDeleted }) => { const deleteElements = useCallback<Instance.DeleteElements>(({ nodes: nodesDeleted, edges: edgesDeleted }) => {
const { const {
nodeInternals, nodes,
getNodes,
edges, edges,
hasDefaultNodes, hasDefaultNodes,
hasDefaultEdges, hasDefaultEdges,
@@ -129,7 +123,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
const { matchingNodes, matchingEdges } = getElementsToRemove<Node, Edge>({ const { matchingNodes, matchingEdges } = getElementsToRemove<Node, Edge>({
nodesToRemove: nodesDeleted || [], nodesToRemove: nodesDeleted || [],
edgesToRemove: edgesDeleted || [], edgesToRemove: edgesDeleted || [],
nodes: getNodes(), nodes,
edges, edges,
}); });
@@ -142,12 +136,8 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
} }
if (hasDefaultNodes) { if (hasDefaultNodes) {
matchingNodes.forEach((node) => {
nodeInternals.delete(node.id);
});
store.setState({ store.setState({
nodeInternals: new Map(nodeInternals), nodes: nodes.filter((n) => !matchingNodes.some((mN) => mN.id === n.id)),
}); });
} }
} }
@@ -181,7 +171,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
nodeOrRect: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect nodeOrRect: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect
): [Rect | null, Node<NodeData> | null | undefined, boolean] => { ): [Rect | null, Node<NodeData> | null | undefined, boolean] => {
const isRect = isRectObject(nodeOrRect); const isRect = isRectObject(nodeOrRect);
const node = isRect ? null : store.getState().nodeInternals.get(nodeOrRect.id); const node = isRect ? null : store.getState().nodes.find((n) => n.id === nodeOrRect.id);
if (!isRect && !node) { if (!isRect && !node) {
[null, null, isRect]; [null, null, isRect];
@@ -202,7 +192,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
return []; return [];
} }
return (nodes || store.getState().getNodes()).filter((n) => { return (nodes || store.getState().nodes).filter((n) => {
if (!isRect && (n.id === node!.id || !n.positionAbsolute)) { if (!isRect && (n.id === node!.id || !n.positionAbsolute)) {
return false; return false;
} }
@@ -7,9 +7,7 @@ function useUpdateNodePositions() {
const store = useStoreApi(); const store = useStoreApi();
const updatePositions = useCallback((params: { x: number; y: number; isShiftPressed: boolean }) => { const updatePositions = useCallback((params: { x: number; y: number; isShiftPressed: boolean }) => {
const { nodeExtent, updateNodePositions, getNodes, snapToGrid, snapGrid, onError, nodesDraggable } = const { nodeExtent, updateNodePositions, nodes, snapToGrid, snapGrid, onError, nodesDraggable } = store.getState();
store.getState();
const nodes = getNodes();
const selectedNodes = nodes.filter( const selectedNodes = nodes.filter(
(n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined')) (n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'))
); );
@@ -36,12 +36,12 @@ const useViewportHelper = (): ViewportHelperFunctions => {
return { x, y, zoom }; return { x, y, zoom };
}, },
fitView: (options) => { fitView: (options) => {
const { getNodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState(); const { nodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
return panZoom return panZoom
? fitView( ? fitView(
{ {
nodes: getNodes(), nodes,
width, width,
height, height,
nodeOrigin, nodeOrigin,
+3 -3
View File
@@ -12,8 +12,8 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole
const visibleEdges = const visibleEdges =
onlyRenderVisible && s.width && s.height onlyRenderVisible && s.width && s.height
? s.edges.filter((e) => { ? s.edges.filter((e) => {
const sourceNode = s.nodeInternals.get(e.source); const sourceNode = s.nodes.find((n) => n.id === e.source);
const targetNode = s.nodeInternals.get(e.target); const targetNode = s.nodes.find((n) => n.id === e.target);
return ( return (
sourceNode && sourceNode &&
@@ -29,7 +29,7 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole
}) })
: s.edges; : s.edges;
return groupEdgesByZLevel(visibleEdges, s.nodeInternals, elevateEdgesOnSelect); return groupEdgesByZLevel(visibleEdges, s.nodes, elevateEdgesOnSelect);
}, },
[onlyRenderVisible, elevateEdgesOnSelect] [onlyRenderVisible, elevateEdgesOnSelect]
), ),
+2 -2
View File
@@ -9,8 +9,8 @@ function useVisibleNodes(onlyRenderVisible: boolean) {
useCallback( useCallback(
(s: ReactFlowState) => (s: ReactFlowState) =>
onlyRenderVisible onlyRenderVisible
? getNodesInside<Node>(s.getNodes(), { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true) ? getNodesInside<Node>(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
: s.getNodes(), : s.nodes,
[onlyRenderVisible] [onlyRenderVisible]
) )
); );
+46 -43
View File
@@ -26,11 +26,11 @@ const createRFStore = () =>
createStore<ReactFlowState>((set, get) => ({ createStore<ReactFlowState>((set, get) => ({
...initialState, ...initialState,
setNodes: (nodes: Node[]) => { setNodes: (nodes: Node[]) => {
const { nodeInternals, nodeOrigin, elevateNodesOnSelect } = get(); const { nodes: storeNodes, nodeOrigin, elevateNodesOnSelect } = get();
set({ nodeInternals: createNodeInternals(nodes, nodeInternals, nodeOrigin, elevateNodesOnSelect) }); set({ nodes: createNodeInternals(nodes, storeNodes, nodeOrigin, elevateNodesOnSelect) });
}, },
getNodes: () => { getNodes: () => {
return Array.from(get().nodeInternals.values()); return get().nodes;
}, },
setEdges: (edges: Edge[]) => { setEdges: (edges: Edge[]) => {
const { defaultEdgeOptions = {} } = get(); const { defaultEdgeOptions = {} } = get();
@@ -40,17 +40,17 @@ const createRFStore = () =>
const hasDefaultNodes = typeof nodes !== 'undefined'; const hasDefaultNodes = typeof nodes !== 'undefined';
const hasDefaultEdges = typeof edges !== 'undefined'; const hasDefaultEdges = typeof edges !== 'undefined';
const nodeInternals = hasDefaultNodes const nextNodes = hasDefaultNodes
? createNodeInternals(nodes, new Map(), get().nodeOrigin, get().elevateNodesOnSelect) ? createNodeInternals(nodes, [], get().nodeOrigin, get().elevateNodesOnSelect)
: new Map(); : [];
const nextEdges = hasDefaultEdges ? edges : []; const nextEdges = hasDefaultEdges ? edges : [];
set({ nodeInternals, edges: nextEdges, hasDefaultNodes, hasDefaultEdges }); set({ nodes: nextNodes, edges: nextEdges, hasDefaultNodes, hasDefaultEdges });
}, },
updateNodeDimensions: (updates) => { updateNodeDimensions: (updates) => {
const { const {
onNodesChange, onNodesChange,
nodeInternals, nodes,
fitViewOnInit, fitViewOnInit,
fitViewOnInitDone, fitViewOnInitDone,
fitViewOnInitOptions, fitViewOnInitOptions,
@@ -70,11 +70,12 @@ const createRFStore = () =>
const style = window.getComputedStyle(viewportNode); const style = window.getComputedStyle(viewportNode);
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform); const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
const changes: NodeDimensionChange[] = [];
const changes: NodeDimensionChange[] = updates.reduce<NodeDimensionChange[]>((res, update) => { const nextNodes = nodes.map((node) => {
const node = nodeInternals.get(update.id); const update = updates.find((change) => change.id === node.id);
if (node) { if (update) {
const dimensions = getDimensions(update.nodeElement); const dimensions = getDimensions(update.nodeElement);
const doUpdate = !!( const doUpdate = !!(
dimensions.width && dimensions.width &&
@@ -83,8 +84,15 @@ const createRFStore = () =>
); );
if (doUpdate) { if (doUpdate) {
nodeInternals.set(node.id, { changes.push({
id: node.id,
type: 'dimensions',
dimensions,
});
return {
...node, ...node,
...dimensions,
[internalsSymbol]: { [internalsSymbol]: {
...node[internalsSymbol], ...node[internalsSymbol],
handleBounds: { handleBounds: {
@@ -92,21 +100,14 @@ const createRFStore = () =>
target: getHandleBounds('.target', update.nodeElement, zoom, node.origin || nodeOrigin), target: getHandleBounds('.target', update.nodeElement, zoom, node.origin || nodeOrigin),
}, },
}, },
...dimensions, };
});
res.push({
id: node.id,
type: 'dimensions',
dimensions,
});
} }
} }
return res; return node;
}, []); });
updateAbsoluteNodePositions(nodeInternals, nodeOrigin); updateAbsoluteNodePositions(nextNodes, nodeOrigin);
const nextFitViewOnInitDone = const nextFitViewOnInitDone =
fitViewOnInitDone || fitViewOnInitDone ||
@@ -115,7 +116,7 @@ const createRFStore = () =>
!!panZoom && !!panZoom &&
fitView( fitView(
{ {
nodes: Array.from(nodeInternals.values()), nodes: nextNodes,
width, width,
height, height,
panZoom, panZoom,
@@ -125,7 +126,7 @@ const createRFStore = () =>
}, },
fitViewOnInitOptions fitViewOnInitOptions
)); ));
set({ nodeInternals: new Map(nodeInternals), fitViewOnInitDone: nextFitViewOnInitDone }); set({ nodes: nextNodes, fitViewOnInitDone: nextFitViewOnInitDone });
if (changes?.length > 0) { if (changes?.length > 0) {
onNodesChange?.(changes); onNodesChange?.(changes);
@@ -153,13 +154,13 @@ const createRFStore = () =>
}, },
triggerNodeChanges: (changes) => { triggerNodeChanges: (changes) => {
const { onNodesChange, nodeInternals, hasDefaultNodes, nodeOrigin, getNodes, elevateNodesOnSelect } = get(); const { onNodesChange, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get();
if (changes?.length) { if (changes?.length) {
if (hasDefaultNodes) { if (hasDefaultNodes) {
const nodes = applyNodeChanges(changes, getNodes()); const updatedNodes = applyNodeChanges(changes, nodes);
const nextNodeInternals = createNodeInternals(nodes, nodeInternals, nodeOrigin, elevateNodesOnSelect); const nextNodes = createNodeInternals(updatedNodes, nodes, nodeOrigin, elevateNodesOnSelect);
set({ nodeInternals: nextNodeInternals }); set({ nodes: nextNodes });
} }
onNodesChange?.(changes); onNodesChange?.(changes);
@@ -167,14 +168,14 @@ const createRFStore = () =>
}, },
addSelectedNodes: (selectedNodeIds) => { addSelectedNodes: (selectedNodeIds) => {
const { multiSelectionActive, edges, getNodes } = get(); const { multiSelectionActive, edges, nodes } = get();
let changedNodes: NodeSelectionChange[]; let changedNodes: NodeSelectionChange[];
let changedEdges: EdgeSelectionChange[] | null = null; let changedEdges: EdgeSelectionChange[] | null = null;
if (multiSelectionActive) { if (multiSelectionActive) {
changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)) as NodeSelectionChange[]; changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)) as NodeSelectionChange[];
} else { } else {
changedNodes = getSelectionChanges(getNodes(), selectedNodeIds); changedNodes = getSelectionChanges(nodes, selectedNodeIds);
changedEdges = getSelectionChanges(edges, []); changedEdges = getSelectionChanges(edges, []);
} }
@@ -186,7 +187,7 @@ const createRFStore = () =>
}); });
}, },
addSelectedEdges: (selectedEdgeIds) => { addSelectedEdges: (selectedEdgeIds) => {
const { multiSelectionActive, edges, getNodes } = get(); const { multiSelectionActive, edges, nodes } = get();
let changedEdges: EdgeSelectionChange[]; let changedEdges: EdgeSelectionChange[];
let changedNodes: NodeSelectionChange[] | null = null; let changedNodes: NodeSelectionChange[] | null = null;
@@ -194,7 +195,7 @@ const createRFStore = () =>
changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)) as EdgeSelectionChange[]; changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)) as EdgeSelectionChange[];
} else { } else {
changedEdges = getSelectionChanges(edges, selectedEdgeIds); changedEdges = getSelectionChanges(edges, selectedEdgeIds);
changedNodes = getSelectionChanges(getNodes(), []); changedNodes = getSelectionChanges(nodes, []);
} }
updateNodesAndEdgesSelections({ updateNodesAndEdgesSelections({
@@ -205,8 +206,8 @@ const createRFStore = () =>
}); });
}, },
unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => { unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => {
const { edges: storeEdges, getNodes } = get(); const { edges: storeEdges, nodes: storeNodes } = get();
const nodesToUnselect = nodes ? nodes : getNodes(); const nodesToUnselect = nodes ? nodes : storeNodes;
const edgesToUnselect = edges ? edges : storeEdges; const edgesToUnselect = edges ? edges : storeEdges;
const changedNodes = nodesToUnselect.map((n) => { const changedNodes = nodesToUnselect.map((n) => {
@@ -242,8 +243,7 @@ const createRFStore = () =>
set({ translateExtent }); set({ translateExtent });
}, },
resetSelectedElements: () => { resetSelectedElements: () => {
const { edges, getNodes } = get(); const { edges, nodes } = get();
const nodes = getNodes();
const nodesToUnselect = nodes const nodesToUnselect = nodes
.filter((e) => e.selected) .filter((e) => e.selected)
@@ -260,15 +260,18 @@ const createRFStore = () =>
}); });
}, },
setNodeExtent: (nodeExtent) => { setNodeExtent: (nodeExtent) => {
const { nodeInternals } = get(); const { nodes } = get();
nodeInternals.forEach((node) => {
node.positionAbsolute = clampPosition(node.position, nodeExtent);
});
set({ set({
nodeExtent, nodeExtent,
nodeInternals: new Map(nodeInternals), nodes: nodes.map((node) => {
const positionAbsolute = clampPosition(node.position, nodeExtent);
return {
...node,
positionAbsolute,
};
}),
}); });
}, },
panBy: (delta): boolean => { panBy: (delta): boolean => {
+1 -1
View File
@@ -7,7 +7,7 @@ const initialState: ReactFlowStore = {
width: 0, width: 0,
height: 0, height: 0,
transform: [0, 0, 1], transform: [0, 0, 1],
nodeInternals: new Map(), nodes: [],
edges: [], edges: [],
onNodesChange: null, onNodesChange: null,
onEdgesChange: null, onEdgesChange: null,
+32 -50
View File
@@ -7,25 +7,21 @@ import {
type NodeOrigin, type NodeOrigin,
} from '@xyflow/system'; } from '@xyflow/system';
import type { Edge, EdgeSelectionChange, Node, NodeInternals, NodeSelectionChange, ReactFlowState } from '../types'; import type { Edge, EdgeSelectionChange, Node, NodeSelectionChange, ReactFlowState } from '../types';
type ParentNodes = Record<string, boolean>; type ParentNodes = Record<string, boolean>;
function calculateXYZPosition( function calculateXYZPosition(node: Node, nodes: Node[], result: XYZPosition, nodeOrigin: NodeOrigin): XYZPosition {
node: Node,
nodeInternals: NodeInternals,
result: XYZPosition,
nodeOrigin: NodeOrigin
): XYZPosition {
if (!node.parentNode) { if (!node.parentNode) {
return result; return result;
} }
const parentNode = nodeInternals.get(node.parentNode)!;
const parentNode = nodes.find((n) => n.id === node.parentNode)!;
const parentNodePosition = getNodePositionWithOrigin(parentNode, parentNode?.origin || nodeOrigin); const parentNodePosition = getNodePositionWithOrigin(parentNode, parentNode?.origin || nodeOrigin);
return calculateXYZPosition( return calculateXYZPosition(
parentNode, parentNode,
nodeInternals, nodes,
{ {
x: (result.x ?? 0) + parentNodePosition.x, x: (result.x ?? 0) + parentNodePosition.x,
y: (result.y ?? 0) + parentNodePosition.y, y: (result.y ?? 0) + parentNodePosition.y,
@@ -35,21 +31,17 @@ function calculateXYZPosition(
); );
} }
export function updateAbsoluteNodePositions( export function updateAbsoluteNodePositions(nodes: Node[], nodeOrigin: NodeOrigin, parentNodes?: ParentNodes) {
nodeInternals: NodeInternals, nodes.forEach((node) => {
nodeOrigin: NodeOrigin, if (node.parentNode && !nodes.find((n) => n.id === node.parentNode)) {
parentNodes?: ParentNodes
) {
nodeInternals.forEach((node) => {
if (node.parentNode && !nodeInternals.has(node.parentNode)) {
throw new Error(`Parent node ${node.parentNode} not found`); throw new Error(`Parent node ${node.parentNode} not found`);
} }
if (node.parentNode || parentNodes?.[node.id]) { if (node.parentNode || parentNodes?.[node.id]) {
const parentNode = node.parentNode ? nodeInternals.get(node.parentNode) : null; const parentNode = node.parentNode ? nodes.find((n) => n.id === node.parentNode) : null;
const { x, y, z } = calculateXYZPosition( const { x, y, z } = calculateXYZPosition(
node, node,
nodeInternals, nodes,
{ {
...node.position, ...node.position,
z: node[internalsSymbol]?.z ?? 0, z: node[internalsSymbol]?.z ?? 0,
@@ -73,19 +65,19 @@ export function updateAbsoluteNodePositions(
export function createNodeInternals( export function createNodeInternals(
nodes: Node[], nodes: Node[],
nodeInternals: NodeInternals, storeNodes: Node[],
nodeOrigin: NodeOrigin, nodeOrigin: NodeOrigin,
elevateNodesOnSelect: boolean elevateNodesOnSelect: boolean
): NodeInternals { ): Node[] {
const nextNodeInternals = new Map<string, Node>(); const nextNodes: Node[] = [];
const parentNodes: ParentNodes = {}; const parentNodes: ParentNodes = {};
const selectedNodeZ: number = elevateNodesOnSelect ? 1000 : 0; const selectedNodeZ: number = elevateNodesOnSelect ? 1000 : 0;
nodes.forEach((node) => { nodes.forEach((node) => {
const z = (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0); const z = (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0);
const currInternals = nodeInternals.get(node.id); const currInternals = storeNodes.find((n) => n.id === node.id);
const internals: Node = { const updatedNode: Node = {
width: currInternals?.width, width: currInternals?.width,
height: currInternals?.height, height: currInternals?.height,
...node, ...node,
@@ -96,11 +88,11 @@ export function createNodeInternals(
}; };
if (node.parentNode) { if (node.parentNode) {
internals.parentNode = node.parentNode; updatedNode.parentNode = node.parentNode;
parentNodes[node.parentNode] = true; parentNodes[node.parentNode] = true;
} }
Object.defineProperty(internals, internalsSymbol, { Object.defineProperty(updatedNode, internalsSymbol, {
enumerable: false, enumerable: false,
value: { value: {
handleBounds: currInternals?.[internalsSymbol]?.handleBounds, handleBounds: currInternals?.[internalsSymbol]?.handleBounds,
@@ -108,36 +100,26 @@ export function createNodeInternals(
}, },
}); });
nextNodeInternals.set(node.id, internals); nextNodes.push(updatedNode);
}); });
updateAbsoluteNodePositions(nextNodeInternals, nodeOrigin, parentNodes); updateAbsoluteNodePositions(nodes, nodeOrigin, parentNodes);
return nextNodeInternals; return nextNodes;
} }
export function handleControlledNodeSelectionChange(nodeChanges: NodeSelectionChange[], nodeInternals: NodeInternals) { export function handleControlledSelectionChange<NodeOrEdge extends Node | Edge>(
nodeChanges.forEach((change) => { changes: NodeSelectionChange[] | EdgeSelectionChange[],
const node = nodeInternals.get(change.id); items: NodeOrEdge[]
if (node) { ): NodeOrEdge[] {
nodeInternals.set(node.id, { return items.map((item) => {
...node, const change = changes.find((change) => change.id === item.id);
[internalsSymbol]: node[internalsSymbol],
selected: change.selected,
});
}
});
return new Map(nodeInternals);
}
export function handleControlledEdgeSelectionChange(edgeChanges: EdgeSelectionChange[], edges: Edge[]) {
return edges.map((e) => {
const change = edgeChanges.find((change) => change.id === e.id);
if (change) { if (change) {
e.selected = change.selected; item.selected = change.selected;
} }
return e;
return item;
}); });
} }
@@ -149,11 +131,11 @@ type UpdateNodesAndEdgesParams = {
}; };
export function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get, set }: UpdateNodesAndEdgesParams) { export function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get, set }: UpdateNodesAndEdgesParams) {
const { nodeInternals, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get(); const { nodes, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get();
if (changedNodes?.length) { if (changedNodes?.length) {
if (hasDefaultNodes) { if (hasDefaultNodes) {
set({ nodeInternals: handleControlledNodeSelectionChange(changedNodes, nodeInternals) }); set({ nodes: handleControlledSelectionChange(changedNodes, nodes) });
} }
onNodesChange?.(changedNodes); onNodesChange?.(changedNodes);
@@ -161,7 +143,7 @@ export function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get,
if (changedEdges?.length) { if (changedEdges?.length) {
if (hasDefaultEdges) { if (hasDefaultEdges) {
set({ edges: handleControlledEdgeSelectionChange(changedEdges, edges) }); set({ edges: handleControlledSelectionChange(changedEdges, edges) });
} }
onEdgesChange?.(changedEdges); onEdgesChange?.(changedEdges);
-2
View File
@@ -41,5 +41,3 @@ export type WrapNodeProps<NodeData = any> = Pick<
rfId: string; rfId: string;
disableKeyboardA11y: boolean; disableKeyboardA11y: boolean;
}; };
export type NodeInternals = Map<string, Node>;
+1 -2
View File
@@ -32,7 +32,6 @@ import type {
NodeChange, NodeChange,
OnNodesChange, OnNodesChange,
OnEdgesChange, OnEdgesChange,
NodeInternals,
DefaultEdgeOptions, DefaultEdgeOptions,
FitViewOptions, FitViewOptions,
OnNodesDelete, OnNodesDelete,
@@ -46,7 +45,7 @@ export type ReactFlowStore = {
width: number; width: number;
height: number; height: number;
transform: Transform; transform: Transform;
nodeInternals: NodeInternals; nodes: Node[];
edges: Edge[]; edges: Edge[];
onNodesChange: OnNodesChange | null; onNodesChange: OnNodesChange | null;
onEdgesChange: OnEdgesChange | null; onEdgesChange: OnEdgesChange | null;
+3 -7
View File
@@ -33,13 +33,11 @@ export type GroupedEdges<EdgeType extends BaseEdge> = {
export function groupEdgesByZLevel<EdgeType extends BaseEdge>( export function groupEdgesByZLevel<EdgeType extends BaseEdge>(
edges: EdgeType[], edges: EdgeType[],
nodes: Map<string, BaseNode> | BaseNode[], nodes: BaseNode[],
elevateEdgesOnSelect = false elevateEdgesOnSelect = false
): GroupedEdges<EdgeType>[] { ): GroupedEdges<EdgeType>[] {
let maxLevel = -1; let maxLevel = -1;
const isNodeInternals = 'get' in nodes;
const levelLookup = edges.reduce<Record<string, EdgeType[]>>((tree, edge) => { const levelLookup = edges.reduce<Record<string, EdgeType[]>>((tree, edge) => {
const hasZIndex = isNumeric(edge.zIndex); const hasZIndex = isNumeric(edge.zIndex);
let z = hasZIndex ? edge.zIndex! : 0; let z = hasZIndex ? edge.zIndex! : 0;
@@ -48,10 +46,8 @@ export function groupEdgesByZLevel<EdgeType extends BaseEdge>(
z = hasZIndex z = hasZIndex
? edge.zIndex! ? edge.zIndex!
: Math.max( : Math.max(
(isNodeInternals ? nodes.get(edge.source) : nodes.find((n) => n.id === edge.source))?.[internalsSymbol] nodes.find((n) => n.id === edge.source)?.[internalsSymbol]?.z || 0,
?.z || 0, nodes.find((n) => n.id === edge.target)?.[internalsSymbol]?.z || 0
(isNodeInternals ? nodes.get(edge.target) : nodes.find((n) => n.id === edge.target))?.[internalsSymbol]
?.z || 0
); );
} }