8
*/
9
10
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';
18
19
import {
20
localStorageGetItem,
29
import SuspenseRects from './SuspenseRects';
30
import SuspenseTimeline from './SuspenseTimeline';
31
import SuspenseTreeList from './SuspenseTreeList';
32
+import {
33
+ SuspenseTreeDispatcherContext,
34
+ SuspenseTreeStateContext,
35
+} from './SuspenseTreeContext';
36
+import {StoreContext} from '../context';
37
+import {TreeDispatcherContext} from '../Components/TreeContext';
38
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';
44
45
type Orientation = 'horizontal' | 'vertical';
46
64
};
65
type LayoutDispatch = (action: LayoutAction) => void;
66
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
+
152
function ToggleTreeList({
153
dispatch,
154
state,
415
</div>
416
)}
417
<div className={styles.TreeView}>
317
- <div className={styles.SuspenseTreeViewHeader}>
418
+ <header className={styles.SuspenseTreeViewHeader}>
419
{treeListDisabled ? (
420
<div />
421
) : (
422
<ToggleTreeList dispatch={dispatch} state={state} />
423
)}
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 />
426
</div>
427
+ <ToggleUniqueSuspenders />
428
+ <SelectRoot />
429
<ToggleInspectedElement
430
dispatch={dispatch}
431
state={state}
432
orientation="horizontal"
433
/>
336
- </div>
434
+ </header>
435
<div className={styles.Rects}>
436
<SuspenseRects />
437
</div>
340
- <footer>
438
+ <footer className={styles.SuspenseTreeViewFooter}>
439
+ <div className={styles.SuspenseTimeline}>
440
+ <SuspenseTimeline />
441
+ </div>
442
<ToggleInspectedElement
443
dispatch={dispatch}
444
state={state}