8
*/
9
10
import * as React from 'react';
11
-import {useContext, useLayoutEffect, useEffect, useRef} from 'react';
11
+import {useContext, useEffect} from 'react';
12
import {BridgeContext, StoreContext} from '../context';
13
import {TreeDispatcherContext} from '../Components/TreeContext';
14
import {useHighlightHostInstance} from '../hooks';
17
SuspenseTreeStateContext,
18
} from './SuspenseTreeContext';
19
import styles from './SuspenseTimeline.css';
20
-import typeof {
21
- SyntheticEvent,
22
- SyntheticPointerEvent,
23
-} from 'react-dom-bindings/src/events/SyntheticEvent';
20
+import SuspenseScrubber from './SuspenseScrubber';
21
import Button from '../Button';
22
import ButtonIcon from '../ButtonIcon';
23
36
playing,
37
} = useContext(SuspenseTreeStateContext);
38
42
- const inputRef = useRef<HTMLElement | null>(null);
43
- const inputBBox = useRef<ClientRect | null>(null);
44
- useLayoutEffect(() => {
45
- if (timeline.length === 0) {
46
- return;
47
- }
48
-
49
- const input = inputRef.current;
50
- if (input === null) {
51
- throw new Error('Expected an input HTML element to be present.');
52
- }
53
-
54
- inputBBox.current = input.getBoundingClientRect();
55
- const observer = new ResizeObserver(entries => {
56
- inputBBox.current = input.getBoundingClientRect();
57
- });
58
- observer.observe(input);
59
- return () => {
60
- inputBBox.current = null;
61
- observer.disconnect();
62
- };
63
- }, [timeline.length]);
64
-
39
const min = 0;
40
const max = timeline.length > 0 ? timeline.length - 1 : 0;
41
74
});
75
}
76
103
- function handleChange(event: SyntheticEvent) {
104
- const pendingTimelineIndex = +event.currentTarget.value;
77
+ function handleChange(pendingTimelineIndex: number) {
78
switchSuspenseNode(pendingTimelineIndex);
79
}
80
86
switchSuspenseNode(timelineIndex);
87
}
88
116
- function handlePointerMove(event: SyntheticPointerEvent) {
117
- const bbox = inputBBox.current;
118
- if (bbox === null) {
119
- throw new Error('Bounding box of slider is unknown.');
120
- }
121
-
122
- const hoveredValue = Math.max(
123
- min,
124
- Math.min(
125
- Math.round(
126
- min + ((event.clientX - bbox.left) / bbox.width) * (max - min),
127
- ),
128
- max,
129
- ),
130
- );
89
+ function handleHoverSegment(hoveredValue: number) {
90
const suspenseID = timeline[hoveredValue];
91
if (suspenseID === undefined) {
92
throw new Error(
134
- `Suspense node not found for value ${hoveredValue} in timeline when on ${event.clientX} in bounding box ${JSON.stringify(bbox)}.`,
93
+ `Suspense node not found for value ${hoveredValue} in timeline.`,
94
);
95
}
96
highlightHostInstance(suspenseID);
198
<div
199
className={styles.SuspenseTimelineInput}
200
title={timelineIndex + '/' + max}>
242
- <input
243
- className={styles.SuspenseTimelineSlider}
244
- type="range"
201
+ <SuspenseScrubber
202
min={min}
203
max={max}
204
value={timelineIndex}
205
onBlur={handleBlur}
206
onChange={handleChange}
207
onFocus={handleFocus}
251
- onPointerMove={handlePointerMove}
252
- onPointerUp={clearHighlightHostInstance}
253
- ref={inputRef}
208
+ onHoverSegment={handleHoverSegment}
209
+ onHoverLeave={clearHighlightHostInstance}
210
/>
211
</div>
212
</>