From f78b2343cc1af0647110b52fcefef8d5abffaaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Sun, 28 Sep 2025 10:15:31 -0400 Subject: [PATCH] [DevTools] Recursively compute the bounding rect of the roots (#34629) It's possible for the children to overflow the bounding rect of the root in general when they overflow in the DOM. However even when it doesn't overflow in the DOM, the bounding rect of the root can shrink while the content is suspended. In fact, it's very likely. Originally I thought we didn't need to consider this recursively because document scrolling takes absolute positioned content into account but because we're using nested overflow scrolling, we have to manually compute this. --- .../views/SuspenseTab/SuspenseRects.js | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js index 5dac93a182..d67cc9a9fe 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js @@ -184,6 +184,42 @@ function getBoundingBox(rects: $ReadOnlyArray | null): Rect { }; } +function computeBoundingRectRecursively( + store: Store, + node: SuspenseNode, + bounds: { + minX: number, + minY: number, + maxX: number, + maxY: number, + }, +): void { + const rects = node.rects; + if (rects !== null) { + for (let j = 0; j < rects.length; j++) { + const rect = rects[j]; + if (rect.x < bounds.minX) { + bounds.minX = rect.x; + } + if (rect.x + rect.width > bounds.maxX) { + bounds.maxX = rect.x + rect.width; + } + if (rect.y < bounds.minY) { + bounds.minY = rect.y; + } + if (rect.y + rect.height > bounds.maxY) { + bounds.maxY = rect.y + rect.height; + } + } + } + for (let i = 0; i < node.children.length; i++) { + const child = store.getSuspenseByID(node.children[i]); + if (child !== null) { + computeBoundingRectRecursively(store, child, bounds); + } + } +} + function getDocumentBoundingRect( store: Store, roots: $ReadOnlyArray, @@ -192,10 +228,12 @@ function getDocumentBoundingRect( return {x: 0, y: 0, width: 0, height: 0}; } - let minX = Number.POSITIVE_INFINITY; - let minY = Number.POSITIVE_INFINITY; - let maxX = Number.NEGATIVE_INFINITY; - let maxY = Number.NEGATIVE_INFINITY; + const bounds = { + minX: Number.POSITIVE_INFINITY, + minY: Number.POSITIVE_INFINITY, + maxX: Number.NEGATIVE_INFINITY, + maxY: Number.NEGATIVE_INFINITY, + }; for (let i = 0; i < roots.length; i++) { const rootID = roots[i]; @@ -203,30 +241,19 @@ function getDocumentBoundingRect( if (root === null) { continue; } - - const rects = root.rects; - if (rects === null) { - continue; - } - for (let j = 0; j < rects.length; j++) { - const rect = rects[j]; - minX = Math.min(minX, rect.x); - minY = Math.min(minY, rect.y); - maxX = Math.max(maxX, rect.x + rect.width); - maxY = Math.max(maxY, rect.y + rect.height); - } + computeBoundingRectRecursively(store, root, bounds); } - if (minX === Number.POSITIVE_INFINITY) { + if (bounds.minX === Number.POSITIVE_INFINITY) { // No rects found, return empty rect return {x: 0, y: 0, width: 0, height: 0}; } return { - x: minX, - y: minY, - width: maxX - minX, - height: maxY - minY, + x: bounds.minX, + y: bounds.minY, + width: bounds.maxX - bounds.minX, + height: bounds.maxY - bounds.minY, }; }