diff --git a/.changeset/giant-foxes-roll.md b/.changeset/giant-foxes-roll.md
new file mode 100644
index 00000000..14d73c64
--- /dev/null
+++ b/.changeset/giant-foxes-roll.md
@@ -0,0 +1,6 @@
+---
+'@xyflow/svelte': patch
+'@xyflow/system': patch
+---
+
+Do not crash minimap if all nodes are hidden
diff --git a/examples/react/src/examples/CustomMiniMapNode/index.tsx b/examples/react/src/examples/CustomMiniMapNode/index.tsx
index 21b1d073..5766d109 100644
--- a/examples/react/src/examples/CustomMiniMapNode/index.tsx
+++ b/examples/react/src/examples/CustomMiniMapNode/index.tsx
@@ -1,4 +1,4 @@
-import { MouseEvent, CSSProperties, useCallback } from 'react';
+import { MouseEvent, CSSProperties, useCallback, useState } from 'react';
import {
ReactFlow,
@@ -14,19 +14,13 @@ import {
ReactFlowInstance,
useEdgesState,
useNodesState,
+ Panel,
} from '@xyflow/react';
const onInit = (reactFlowInstance: ReactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
-const buttonStyle: CSSProperties = {
- position: 'absolute',
- left: 10,
- top: 10,
- zIndex: 4,
-};
-
const CustomMiniMapNode = ({ x, y, width, height }: MiniMapNodeProps) => {
return ;
};
@@ -34,6 +28,7 @@ const CustomMiniMapNode = ({ x, y, width, height }: MiniMapNodeProps) => {
const CustomMiniMapNodeFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
+ const [hideAllNodes, setHideAllNodes] = useState(false);
const onConnect = useCallback((params: Connection | Edge) => setEdges((els) => addEdge(params, els)), [setEdges]);
const addRandomNode = () => {
@@ -45,10 +40,20 @@ const CustomMiniMapNodeFlow = () => {
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
},
+ hidden: hideAllNodes,
};
setNodes((nds) => nds.concat(newNode));
};
+ const toggleHideAllNodes = () => {
+ setHideAllNodes(prev => {
+ const next = !prev;
+ setNodes(nds => nds.map(n => ({ ...n, hidden: next })));
+ setEdges(eds => eds.map(e => ({ ...e, hidden: next })));
+ return next;
+ });
+ };
+
return (
{
-
+
+
+
+
);
};
diff --git a/examples/svelte/src/routes/examples/custom-minimap/+page.svelte b/examples/svelte/src/routes/examples/custom-minimap/+page.svelte
index 1e9c96ca..0a0c734c 100644
--- a/examples/svelte/src/routes/examples/custom-minimap/+page.svelte
+++ b/examples/svelte/src/routes/examples/custom-minimap/+page.svelte
@@ -4,15 +4,17 @@
Background,
Controls,
MiniMap,
+ Panel,
type Node,
- type Edge,
+ type Edge
} from '@xyflow/svelte';
-
+
import '@xyflow/svelte/dist/style.css';
import CustomMiniMapNode from './CustomMiniMapNode.svelte';
let nodes = $state.raw([]);
let edges = $state.raw([]);
+ let hideAllNodes = $state(false);
function addRandomNode() {
const nodeId = (nodes.length + 1).toString();
@@ -21,28 +23,33 @@
data: { label: `Node: ${nodeId}` },
position: {
x: Math.random() * (typeof window !== 'undefined' ? window.innerWidth : 800),
- y: Math.random() * (typeof window !== 'undefined' ? window.innerHeight : 600),
+ y: Math.random() * (typeof window !== 'undefined' ? window.innerHeight : 600)
},
- type: 'default'
+ type: 'default',
+ hidden: hideAllNodes
};
nodes = [...nodes, newNode];
}
+
+ function toggleHideAllNodes() {
+ hideAllNodes = !hideAllNodes;
+ nodes = nodes.map((node) => ({
+ ...node,
+ hidden: hideAllNodes
+ }));
+ }
-
+
-
+
+
+
+
+
diff --git a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte
index 33209dcb..90c6abb4 100644
--- a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte
+++ b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte
@@ -60,14 +60,11 @@
width: store.width / store.viewport.zoom,
height: store.height / store.viewport.zoom
});
+
let boundingRect = $derived(
- store.nodeLookup.size > 0
- ? getBoundsOfRects(
- getInternalNodesBounds(store.nodeLookup, { filter: (n) => !n.hidden }),
- viewBB
- )
- : viewBB
+ getBoundsOfRects(getInternalNodesBounds(store.nodeLookup, { filter: (n) => !n.hidden }), viewBB)
);
+
let scaledWidth = $derived(boundingRect.width / width);
let scaledHeight = $derived(boundingRect.height / height);
let viewScale = $derived(Math.max(scaledWidth, scaledHeight));
@@ -121,7 +118,7 @@
{#each store.nodes as userNode (userNode.id)}
{@const node = store.nodeLookup.get(userNode.id)}
- {#if node && nodeHasDimensions(node)}
+ {#if node && nodeHasDimensions(node) && !node.hidden}
{@const nodeDimesions = getNodeDimensions(node)}
,
params: GetInternalNodesBoundsParams = {}
): Rect => {
- if (nodeLookup.size === 0) {
- return { x: 0, y: 0, width: 0, height: 0 };
- }
-
let box = { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity };
+ let hasVisibleNodes = false;
nodeLookup.forEach((node) => {
if (params.filter === undefined || params.filter(node)) {
- const nodeBox = nodeToBox(node as InternalNodeBase);
- box = getBoundsOfBoxes(box, nodeBox);
+ box = getBoundsOfBoxes(box, nodeToBox(node as InternalNodeBase));
+ hasVisibleNodes = true;
}
});
- return boxToRect(box);
+ return hasVisibleNodes ? boxToRect(box) : { x: 0, y: 0, width: 0, height: 0 };
};
export const getNodesInside = (