Skip to content

Commit b187de8

Browse files
committed
Move Timeline to footer instead of header
1 parent df38ac9 commit b187de8

File tree

3 files changed

+130
-99
lines changed

3 files changed

+130
-99
lines changed

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.css

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@
4949
cursor: ew-resize;
5050
}
5151

52-
.TreeView footer {
53-
display: none;
54-
}
55-
5652
@container devtools (width < 600px) {
5753
.SuspenseTab {
5854
flex-direction: column;
@@ -76,13 +72,13 @@
7672
cursor: ns-resize;
7773
}
7874

79-
.TreeView footer {
80-
display: flex;
81-
justify-content: end;
82-
border-top: 1px solid var(--color-border);
75+
.ToggleInspectedElement[data-orientation="horizontal"] {
76+
display: none;
8377
}
78+
}
8479

85-
.ToggleInspectedElement[data-orientation="horizontal"] {
80+
@container devtools (width >= 600px) {
81+
.ToggleInspectedElement[data-orientation="vertical"] {
8682
display: none;
8783
}
8884
}
@@ -103,22 +99,18 @@
10399
}
104100

105101
.Rects {
106-
border-top: 1px solid var(--color-border);
107102
padding: 0.25rem;
108103
flex-grow: 1;
109104
overflow: auto;
110105
}
111106

112107
.SuspenseTreeViewHeader {
113-
padding: 0.25rem;
108+
flex: 0 0 42px;
109+
padding: 0.5rem;
114110
display: grid;
115-
grid-template-columns: auto 1fr auto;
111+
grid-template-columns: auto 1fr auto auto auto;
116112
align-items: flex-start;
117-
}
118-
119-
.SuspenseTreeViewHeaderMain {
120-
display: grid;
121-
grid-template-rows: auto auto;
113+
border-bottom: 1px solid var(--color-border);
122114
}
123115

124116
.SuspenseBreadcrumbs {
@@ -128,3 +120,14 @@
128120
*/
129121
overflow-x: auto;
130122
}
123+
124+
.SuspenseTreeViewFooter {
125+
flex: 0 0 42px;
126+
display: flex;
127+
justify-content: end;
128+
border-top: 1px solid var(--color-border);
129+
padding: 0.5rem;
130+
display: grid;
131+
grid-template-columns: 1fr auto;
132+
align-items: flex-start;
133+
}

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTab.js

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
*/
99

1010
import * as React from 'react';
11-
import {useEffect, useLayoutEffect, useReducer, useRef} from 'react';
11+
import {
12+
useContext,
13+
useEffect,
14+
useLayoutEffect,
15+
useReducer,
16+
useRef,
17+
} from 'react';
1218

1319
import {
1420
localStorageGetItem,
@@ -23,7 +29,14 @@ import SuspenseBreadcrumbs from './SuspenseBreadcrumbs';
2329
import SuspenseRects from './SuspenseRects';
2430
import SuspenseTimeline from './SuspenseTimeline';
2531
import SuspenseTreeList from './SuspenseTreeList';
32+
import {
33+
SuspenseTreeDispatcherContext,
34+
SuspenseTreeStateContext,
35+
} from './SuspenseTreeContext';
36+
import {StoreContext} from '../context';
37+
import {TreeDispatcherContext} from '../Components/TreeContext';
2638
import Button from '../Button';
39+
import Tooltip from '../Components/reach-ui/tooltip';
2740
import typeof {SyntheticPointerEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
2841

2942
type Orientation = 'horizontal' | 'vertical';
@@ -48,6 +61,91 @@ type LayoutState = {
4861
};
4962
type LayoutDispatch = (action: LayoutAction) => void;
5063

64+
function ToggleUniqueSuspenders() {
65+
const store = useContext(StoreContext);
66+
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
67+
68+
const {selectedRootID: rootID, uniqueSuspendersOnly} = useContext(
69+
SuspenseTreeStateContext,
70+
);
71+
72+
function handleToggleUniqueSuspenders(event: SyntheticEvent) {
73+
const nextUniqueSuspendersOnly = (event.currentTarget as HTMLInputElement)
74+
.checked;
75+
const nextTimeline =
76+
rootID === null
77+
? []
78+
: // TODO: Handle different timeline modes (e.g. random order)
79+
store.getSuspendableDocumentOrderSuspense(
80+
rootID,
81+
nextUniqueSuspendersOnly,
82+
);
83+
suspenseTreeDispatch({
84+
type: 'SET_SUSPENSE_TIMELINE',
85+
payload: [nextTimeline, null, nextUniqueSuspendersOnly],
86+
});
87+
}
88+
89+
return (
90+
<Tooltip label="Only include boundaries with unique suspenders">
91+
<input
92+
checked={uniqueSuspendersOnly}
93+
type="checkbox"
94+
onChange={handleToggleUniqueSuspenders}
95+
/>
96+
</Tooltip>
97+
);
98+
}
99+
100+
function SelectRoot() {
101+
const store = useContext(StoreContext);
102+
const {roots, selectedRootID, uniqueSuspendersOnly} = useContext(
103+
SuspenseTreeStateContext,
104+
);
105+
const treeDispatch = useContext(TreeDispatcherContext);
106+
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
107+
108+
function handleChange(event: SyntheticEvent) {
109+
const newRootID = +event.currentTarget.value;
110+
// TODO: scrollIntoView both suspense rects and host instance.
111+
const nextTimeline = store.getSuspendableDocumentOrderSuspense(
112+
newRootID,
113+
uniqueSuspendersOnly,
114+
);
115+
suspenseTreeDispatch({
116+
type: 'SET_SUSPENSE_TIMELINE',
117+
payload: [nextTimeline, newRootID, uniqueSuspendersOnly],
118+
});
119+
if (nextTimeline.length > 0) {
120+
const milestone = nextTimeline[nextTimeline.length - 1];
121+
treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: milestone});
122+
}
123+
}
124+
return (
125+
roots.length > 0 && (
126+
<select
127+
aria-label="Select Suspense Root"
128+
className={styles.SuspenseTimelineRootSwitcher}
129+
onChange={handleChange}
130+
value={selectedRootID === null ? -1 : selectedRootID}>
131+
<option disabled={true} value={-1}>
132+
----
133+
</option>
134+
{roots.map(rootID => {
135+
// TODO: Use name
136+
const name = '#' + rootID;
137+
// TODO: Highlight host on hover
138+
return (
139+
<option key={rootID} value={rootID}>
140+
{name}
141+
</option>
142+
);
143+
})}
144+
</select>
145+
)
146+
);
147+
}
148+
51149
function ToggleTreeList({
52150
dispatch,
53151
state,
@@ -314,30 +412,30 @@ function SuspenseTab(_: {}) {
314412
</div>
315413
)}
316414
<div className={styles.TreeView}>
317-
<div className={styles.SuspenseTreeViewHeader}>
415+
<header className={styles.SuspenseTreeViewHeader}>
318416
{treeListDisabled ? (
319417
<div />
320418
) : (
321419
<ToggleTreeList dispatch={dispatch} state={state} />
322420
)}
323-
<div className={styles.SuspenseTreeViewHeaderMain}>
324-
<div className={styles.SuspenseTimeline}>
325-
<SuspenseTimeline />
326-
</div>
327-
<div className={styles.SuspenseBreadcrumbs}>
328-
<SuspenseBreadcrumbs />
329-
</div>
421+
<div className={styles.SuspenseBreadcrumbs}>
422+
<SuspenseBreadcrumbs />
330423
</div>
424+
<ToggleUniqueSuspenders />
425+
<SelectRoot />
331426
<ToggleInspectedElement
332427
dispatch={dispatch}
333428
state={state}
334429
orientation="horizontal"
335430
/>
336-
</div>
431+
</header>
337432
<div className={styles.Rects}>
338433
<SuspenseRects />
339434
</div>
340-
<footer>
435+
<footer className={styles.SuspenseTreeViewFooter}>
436+
<div className={styles.SuspenseTimeline}>
437+
<SuspenseTimeline />
438+
</div>
341439
<ToggleInspectedElement
342440
dispatch={dispatch}
343441
state={state}

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js

Lines changed: 1 addition & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import * as React from 'react';
1111
import {useContext, useLayoutEffect, useRef} from 'react';
1212
import {BridgeContext, StoreContext} from '../context';
1313
import {TreeDispatcherContext} from '../Components/TreeContext';
14-
import Tooltip from '../Components/reach-ui/tooltip';
1514
import {useHighlightHostInstance} from '../hooks';
1615
import {
1716
SuspenseTreeDispatcherContext,
@@ -35,26 +34,8 @@ function SuspenseTimelineInput() {
3534
selectedRootID: rootID,
3635
timeline,
3736
timelineIndex,
38-
uniqueSuspendersOnly,
3937
} = useContext(SuspenseTreeStateContext);
4038

41-
function handleToggleUniqueSuspenders(event: SyntheticEvent) {
42-
const nextUniqueSuspendersOnly = (event.currentTarget as HTMLInputElement)
43-
.checked;
44-
const nextTimeline =
45-
rootID === null
46-
? []
47-
: // TODO: Handle different timeline modes (e.g. random order)
48-
store.getSuspendableDocumentOrderSuspense(
49-
rootID,
50-
nextUniqueSuspendersOnly,
51-
);
52-
suspenseTreeDispatch({
53-
type: 'SET_SUSPENSE_TIMELINE',
54-
payload: [nextTimeline, null, nextUniqueSuspendersOnly],
55-
});
56-
}
57-
5839
const inputRef = useRef<HTMLElement | null>(null);
5940
const inputBBox = useRef<ClientRect | null>(null);
6041
useLayoutEffect(() => {
@@ -190,66 +171,15 @@ function SuspenseTimelineInput() {
190171
ref={inputRef}
191172
/>
192173
</div>
193-
<Tooltip label="Only include boundaries with unique suspenders">
194-
<input
195-
checked={uniqueSuspendersOnly}
196-
type="checkbox"
197-
onChange={handleToggleUniqueSuspenders}
198-
/>
199-
</Tooltip>
200174
</>
201175
);
202176
}
203177

204178
export default function SuspenseTimeline(): React$Node {
205-
const store = useContext(StoreContext);
206-
const {roots, selectedRootID, uniqueSuspendersOnly} = useContext(
207-
SuspenseTreeStateContext,
208-
);
209-
const treeDispatch = useContext(TreeDispatcherContext);
210-
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
211-
212-
function handleChange(event: SyntheticEvent) {
213-
const newRootID = +event.currentTarget.value;
214-
// TODO: scrollIntoView both suspense rects and host instance.
215-
const nextTimeline = store.getSuspendableDocumentOrderSuspense(
216-
newRootID,
217-
uniqueSuspendersOnly,
218-
);
219-
suspenseTreeDispatch({
220-
type: 'SET_SUSPENSE_TIMELINE',
221-
payload: [nextTimeline, newRootID, uniqueSuspendersOnly],
222-
});
223-
if (nextTimeline.length > 0) {
224-
const milestone = nextTimeline[nextTimeline.length - 1];
225-
treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: milestone});
226-
}
227-
}
228-
179+
const {selectedRootID} = useContext(SuspenseTreeStateContext);
229180
return (
230181
<div className={styles.SuspenseTimelineContainer}>
231182
<SuspenseTimelineInput key={selectedRootID} />
232-
{roots.length > 0 && (
233-
<select
234-
aria-label="Select Suspense Root"
235-
className={styles.SuspenseTimelineRootSwitcher}
236-
onChange={handleChange}
237-
value={selectedRootID === null ? -1 : selectedRootID}>
238-
<option disabled={true} value={-1}>
239-
----
240-
</option>
241-
{roots.map(rootID => {
242-
// TODO: Use name
243-
const name = '#' + rootID;
244-
// TODO: Highlight host on hover
245-
return (
246-
<option key={rootID} value={rootID}>
247-
{name}
248-
</option>
249-
);
250-
})}
251-
</select>
252-
)}
253183
</div>
254184
);
255185
}

0 commit comments

Comments
 (0)