Skip to content

Commit 7957356

Browse files
committed
Add markers for meaningful boundaries
1 parent c5398d4 commit 7957356

File tree

3 files changed

+61
-5
lines changed

3 files changed

+61
-5
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
padding: 0.25rem;
111111
display: flex;
112112
flex-direction: row;
113+
align-items: flex-start;
113114
}
114115

115116
.Timeline {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
11
.SuspenseTimelineSlider {
22
width: 100%;
33
}
4+
5+
.SuspenseTimelineMarkers {
6+
display: flex;
7+
flex-direction: row;
8+
justify-content: space-between;
9+
}
10+
11+
.SuspenseTimelineMarkers > * {
12+
flex: 1 1 0;
13+
overflow: visible;
14+
visibility: hidden;
15+
width: 0
16+
}
17+
18+
.SuspenseTimelineActiveMarker {
19+
visibility: visible;
20+
}

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

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ import type {Element, SuspenseNode} from '../../../frontend/types';
1111
import type Store from '../../store';
1212

1313
import * as React from 'react';
14-
import {useContext, useLayoutEffect, useMemo, useRef} from 'react';
14+
import {
15+
useContext,
16+
useId,
17+
useLayoutEffect,
18+
useMemo,
19+
useRef,
20+
useState,
21+
} from 'react';
1522
import {BridgeContext, StoreContext} from '../context';
1623
import {TreeDispatcherContext} from '../Components/TreeContext';
1724
import {useHighlightHostInstance} from '../hooks';
@@ -89,10 +96,36 @@ export default function SuspenseTimeline(): React$Node {
8996
const min = 0;
9097
const max = timeline.length - 1;
9198

99+
const [value, setValue] = useState(max);
100+
101+
const markersID = useId();
102+
const markers: React.Node[] = useMemo(() => {
103+
return timeline.map((suspense, index) => {
104+
const takesUpSpace =
105+
suspense.rects !== null &&
106+
suspense.rects.some(rect => {
107+
return rect.width > 0 && rect.height > 0;
108+
});
109+
110+
return takesUpSpace ? (
111+
<option
112+
key={suspense.id}
113+
className={
114+
index === value ? styles.SuspenseTimelineActiveMarker : undefined
115+
}
116+
value={index}>
117+
#{index + 1}
118+
</option>
119+
) : (
120+
<option key={suspense.id} />
121+
);
122+
});
123+
}, [timeline, value]);
124+
92125
function handleChange(event: SyntheticEvent<HTMLInputElement>) {
93-
const value = +event.currentTarget.value;
126+
const pendingValue = +event.currentTarget.value;
94127
for (let i = 0; i < timeline.length; i++) {
95-
const forceFallback = i > value;
128+
const forceFallback = i > pendingValue;
96129
const suspense = timeline[i];
97130
const elementID = suspense.id;
98131
const rendererID = store.getRendererIDForElement(elementID);
@@ -110,10 +143,11 @@ export default function SuspenseTimeline(): React$Node {
110143
}
111144
}
112145

113-
const suspense = timeline[value];
146+
const suspense = timeline[pendingValue];
114147
const elementID = suspense.id;
115148
highlightHostInstance(elementID);
116149
dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: elementID});
150+
setValue(pendingValue);
117151
}
118152

119153
function handleBlur() {
@@ -158,14 +192,18 @@ export default function SuspenseTimeline(): React$Node {
158192
type="range"
159193
min={min}
160194
max={max}
161-
defaultValue={max}
195+
list={markersID}
196+
value={value}
162197
onBlur={handleBlur}
163198
onChange={handleChange}
164199
onFocus={handleFocus}
165200
onPointerMove={handlePointerMove}
166201
onPointerUp={clearHighlightHostInstance}
167202
ref={inputRef}
168203
/>
204+
<datalist id={markersID} className={styles.SuspenseTimelineMarkers}>
205+
{markers}
206+
</datalist>
169207
</div>
170208
);
171209
}

0 commit comments

Comments
 (0)