Skip to content

Commit e3cdaac

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

File tree

3 files changed

+134
-100
lines changed

3 files changed

+134
-100
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: 113 additions & 12 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,8 +29,18 @@ 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';
27-
import typeof {SyntheticPointerEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
39+
import Tooltip from '../Components/reach-ui/tooltip';
40+
import typeof {
41+
SyntheticEvent,
42+
SyntheticPointerEvent,
43+
} from 'react-dom-bindings/src/events/SyntheticEvent';
2844

2945
type Orientation = 'horizontal' | 'vertical';
3046

@@ -48,6 +64,91 @@ type LayoutState = {
4864
};
4965
type LayoutDispatch = (action: LayoutAction) => void;
5066

67+
function ToggleUniqueSuspenders() {
68+
const store = useContext(StoreContext);
69+
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
70+
71+
const {selectedRootID: rootID, uniqueSuspendersOnly} = useContext(
72+
SuspenseTreeStateContext,
73+
);
74+
75+
function handleToggleUniqueSuspenders(event: SyntheticEvent) {
76+
const nextUniqueSuspendersOnly = (event.currentTarget as HTMLInputElement)
77+
.checked;
78+
const nextTimeline =
79+
rootID === null
80+
? []
81+
: // TODO: Handle different timeline modes (e.g. random order)
82+
store.getSuspendableDocumentOrderSuspense(
83+
rootID,
84+
nextUniqueSuspendersOnly,
85+
);
86+
suspenseTreeDispatch({
87+
type: 'SET_SUSPENSE_TIMELINE',
88+
payload: [nextTimeline, null, nextUniqueSuspendersOnly],
89+
});
90+
}
91+
92+
return (
93+
<Tooltip label="Only include boundaries with unique suspenders">
94+
<input
95+
checked={uniqueSuspendersOnly}
96+
type="checkbox"
97+
onChange={handleToggleUniqueSuspenders}
98+
/>
99+
</Tooltip>
100+
);
101+
}
102+
103+
function SelectRoot() {
104+
const store = useContext(StoreContext);
105+
const {roots, selectedRootID, uniqueSuspendersOnly} = useContext(
106+
SuspenseTreeStateContext,
107+
);
108+
const treeDispatch = useContext(TreeDispatcherContext);
109+
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
110+
111+
function handleChange(event: SyntheticEvent) {
112+
const newRootID = +event.currentTarget.value;
113+
// TODO: scrollIntoView both suspense rects and host instance.
114+
const nextTimeline = store.getSuspendableDocumentOrderSuspense(
115+
newRootID,
116+
uniqueSuspendersOnly,
117+
);
118+
suspenseTreeDispatch({
119+
type: 'SET_SUSPENSE_TIMELINE',
120+
payload: [nextTimeline, newRootID, uniqueSuspendersOnly],
121+
});
122+
if (nextTimeline.length > 0) {
123+
const milestone = nextTimeline[nextTimeline.length - 1];
124+
treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: milestone});
125+
}
126+
}
127+
return (
128+
roots.length > 0 && (
129+
<select
130+
aria-label="Select Suspense Root"
131+
className={styles.SuspenseTimelineRootSwitcher}
132+
onChange={handleChange}
133+
value={selectedRootID === null ? -1 : selectedRootID}>
134+
<option disabled={true} value={-1}>
135+
----
136+
</option>
137+
{roots.map(rootID => {
138+
// TODO: Use name
139+
const name = '#' + rootID;
140+
// TODO: Highlight host on hover
141+
return (
142+
<option key={rootID} value={rootID}>
143+
{name}
144+
</option>
145+
);
146+
})}
147+
</select>
148+
)
149+
);
150+
}
151+
51152
function ToggleTreeList({
52153
dispatch,
53154
state,
@@ -314,30 +415,30 @@ function SuspenseTab(_: {}) {
314415
</div>
315416
)}
316417
<div className={styles.TreeView}>
317-
<div className={styles.SuspenseTreeViewHeader}>
418+
<header className={styles.SuspenseTreeViewHeader}>
318419
{treeListDisabled ? (
319420
<div />
320421
) : (
321422
<ToggleTreeList dispatch={dispatch} state={state} />
322423
)}
323-
<div className={styles.SuspenseTreeViewHeaderMain}>
324-
<div className={styles.SuspenseTimeline}>
325-
<SuspenseTimeline />
326-
</div>
327-
<div className={styles.SuspenseBreadcrumbs}>
328-
<SuspenseBreadcrumbs />
329-
</div>
424+
<div className={styles.SuspenseBreadcrumbs}>
425+
<SuspenseBreadcrumbs />
330426
</div>
427+
<ToggleUniqueSuspenders />
428+
<SelectRoot />
331429
<ToggleInspectedElement
332430
dispatch={dispatch}
333431
state={state}
334432
orientation="horizontal"
335433
/>
336-
</div>
434+
</header>
337435
<div className={styles.Rects}>
338436
<SuspenseRects />
339437
</div>
340-
<footer>
438+
<footer className={styles.SuspenseTreeViewFooter}>
439+
<div className={styles.SuspenseTimeline}>
440+
<SuspenseTimeline />
441+
</div>
341442
<ToggleInspectedElement
342443
dispatch={dispatch}
343444
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)