diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.css b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.css
index 60a7328589274..3e042402a4c3a 100644
--- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.css
+++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.css
@@ -49,10 +49,6 @@
cursor: ew-resize;
}
-.TreeView footer {
- display: none;
-}
-
@container devtools (width < 600px) {
.SuspenseTab {
flex-direction: column;
@@ -76,13 +72,13 @@
cursor: ns-resize;
}
- .TreeView footer {
- display: flex;
- justify-content: end;
- border-top: 1px solid var(--color-border);
+ .ToggleInspectedElement[data-orientation="horizontal"] {
+ display: none;
}
+}
- .ToggleInspectedElement[data-orientation="horizontal"] {
+@container devtools (width >= 600px) {
+ .ToggleInspectedElement[data-orientation="vertical"] {
display: none;
}
}
@@ -103,22 +99,18 @@
}
.Rects {
- border-top: 1px solid var(--color-border);
padding: 0.25rem;
flex-grow: 1;
overflow: auto;
}
.SuspenseTreeViewHeader {
- padding: 0.25rem;
+ flex: 0 0 42px;
+ padding: 0.5rem;
display: grid;
- grid-template-columns: auto 1fr auto;
+ grid-template-columns: auto 1fr auto auto auto;
align-items: flex-start;
-}
-
-.SuspenseTreeViewHeaderMain {
- display: grid;
- grid-template-rows: auto auto;
+ border-bottom: 1px solid var(--color-border);
}
.SuspenseBreadcrumbs {
@@ -128,3 +120,14 @@
*/
overflow-x: auto;
}
+
+.SuspenseTreeViewFooter {
+ flex: 0 0 42px;
+ display: flex;
+ justify-content: end;
+ border-top: 1px solid var(--color-border);
+ padding: 0.5rem;
+ display: grid;
+ grid-template-columns: 1fr auto;
+ align-items: flex-start;
+}
diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.js
index 8c93cf968a2ac..c7b53605b70a1 100644
--- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.js
+++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.js
@@ -8,7 +8,13 @@
*/
import * as React from 'react';
-import {useEffect, useLayoutEffect, useReducer, useRef} from 'react';
+import {
+ useContext,
+ useEffect,
+ useLayoutEffect,
+ useReducer,
+ useRef,
+} from 'react';
import {
localStorageGetItem,
@@ -23,8 +29,18 @@ import SuspenseBreadcrumbs from './SuspenseBreadcrumbs';
import SuspenseRects from './SuspenseRects';
import SuspenseTimeline from './SuspenseTimeline';
import SuspenseTreeList from './SuspenseTreeList';
+import {
+ SuspenseTreeDispatcherContext,
+ SuspenseTreeStateContext,
+} from './SuspenseTreeContext';
+import {StoreContext} from '../context';
+import {TreeDispatcherContext} from '../Components/TreeContext';
import Button from '../Button';
-import typeof {SyntheticPointerEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
+import Tooltip from '../Components/reach-ui/tooltip';
+import typeof {
+ SyntheticEvent,
+ SyntheticPointerEvent,
+} from 'react-dom-bindings/src/events/SyntheticEvent';
type Orientation = 'horizontal' | 'vertical';
@@ -48,6 +64,91 @@ type LayoutState = {
};
type LayoutDispatch = (action: LayoutAction) => void;
+function ToggleUniqueSuspenders() {
+ const store = useContext(StoreContext);
+ const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
+
+ const {selectedRootID: rootID, uniqueSuspendersOnly} = useContext(
+ SuspenseTreeStateContext,
+ );
+
+ function handleToggleUniqueSuspenders(event: SyntheticEvent) {
+ const nextUniqueSuspendersOnly = (event.currentTarget as HTMLInputElement)
+ .checked;
+ const nextTimeline =
+ rootID === null
+ ? []
+ : // TODO: Handle different timeline modes (e.g. random order)
+ store.getSuspendableDocumentOrderSuspense(
+ rootID,
+ nextUniqueSuspendersOnly,
+ );
+ suspenseTreeDispatch({
+ type: 'SET_SUSPENSE_TIMELINE',
+ payload: [nextTimeline, null, nextUniqueSuspendersOnly],
+ });
+ }
+
+ return (
+