@czi-sds/mcp 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/data/component-docs/Accordion.md +291 -0
- package/data/component-docs/Autocomplete.md +525 -0
- package/data/component-docs/Banner.md +231 -0
- package/data/component-docs/Button.md +224 -0
- package/data/component-docs/ButtonDropdown.md +256 -0
- package/data/component-docs/ButtonGroup.md +320 -0
- package/data/component-docs/ButtonToggle.md +397 -0
- package/data/component-docs/Callout.md +268 -0
- package/data/component-docs/CellBasic.md +122 -0
- package/data/component-docs/CellComponent.md +63 -0
- package/data/component-docs/CellHeader.md +128 -0
- package/data/component-docs/ComplexFilter.md +294 -0
- package/data/component-docs/ContentCard.md +406 -0
- package/data/component-docs/Dialog.md +651 -0
- package/data/component-docs/Dropdown.md +155 -0
- package/data/component-docs/DropdownMenu.md +949 -0
- package/data/component-docs/HeatmapChart.md +782 -0
- package/data/component-docs/Hero.md +363 -0
- package/data/component-docs/Icon.md +283 -0
- package/data/component-docs/InputCheckbox.md +205 -0
- package/data/component-docs/InputDropdown.md +482 -0
- package/data/component-docs/InputRadio.md +173 -0
- package/data/component-docs/InputSearch.md +81 -0
- package/data/component-docs/InputSlider.md +189 -0
- package/data/component-docs/InputText.md +110 -0
- package/data/component-docs/InputToggle.md +141 -0
- package/data/component-docs/IntentMessage.md +293 -0
- package/data/component-docs/Legend.md +442 -0
- package/data/component-docs/Link.md +23 -0
- package/data/component-docs/List.md +300 -0
- package/data/component-docs/LoadingIndicator.md +198 -0
- package/data/component-docs/Menu.md +298 -0
- package/data/component-docs/MenuItem.md +100 -0
- package/data/component-docs/NavigationFooter.md +165 -0
- package/data/component-docs/NavigationHeader.md +500 -0
- package/data/component-docs/NavigationJumpTo.md +414 -0
- package/data/component-docs/Notification.md +288 -0
- package/data/component-docs/Pagination.md +67 -0
- package/data/component-docs/Panel.md +636 -0
- package/data/component-docs/Popover.md +448 -0
- package/data/component-docs/PreComposedTable.md +2947 -0
- package/data/component-docs/SegmentedControl.md +396 -0
- package/data/component-docs/StackedBarChart.md +475 -0
- package/data/component-docs/Table.md +2000 -0
- package/data/component-docs/TableHeader.md +53 -0
- package/data/component-docs/TableRow.md +74 -0
- package/data/component-docs/Tabs.md +455 -0
- package/data/component-docs/Tag.md +647 -0
- package/data/component-docs/TagFilter.md +131 -0
- package/data/component-docs/Tooltip.md +561 -0
- package/data/component-docs/TooltipCondensed.md +203 -0
- package/data/component-docs/TooltipTable.md +145 -0
- package/data/component-docs/index.json +262 -0
- package/data/component-list.json +2 -5
- package/data/component-props/Accordion.json +31 -11
- package/data/component-props/Alert.json +3 -3
- package/data/component-props/Autocomplete.json +150 -15
- package/data/component-props/Banner.json +31 -17
- package/data/component-props/Button.json +45 -20
- package/data/component-props/ButtonDropdown.json +36 -17
- package/data/component-props/ButtonDropdownLegacy.json +11 -11
- package/data/component-props/ButtonGroup.json +32 -11
- package/data/component-props/ButtonIcon.json +10 -10
- package/data/component-props/ButtonLegacy.json +19 -19
- package/data/component-props/ButtonToggle.json +32 -19
- package/data/component-props/ButtonToggleLegacy.json +11 -11
- package/data/component-props/Callout.json +58 -17
- package/data/component-props/CellBasic.json +94 -41
- package/data/component-props/CellComponent.json +23 -7
- package/data/component-props/CellHeader.json +62 -19
- package/data/component-props/ComplexFilter.json +52 -21
- package/data/component-props/ContentCard.json +70 -47
- package/data/component-props/Dialog.json +47 -14
- package/data/component-props/Dropdown.json +68 -31
- package/data/component-props/DropdownMenu.json +128 -23
- package/data/component-props/HeatmapChart.json +88 -12
- package/data/component-props/Hero.json +80 -39
- package/data/component-props/Icon.json +20 -9
- package/data/component-props/InputCheckbox.json +36 -14
- package/data/component-props/InputDropdown.json +75 -27
- package/data/component-props/InputRadio.json +31 -13
- package/data/component-props/InputSearch.json +41 -18
- package/data/component-props/InputSlider.json +46 -19
- package/data/component-props/InputText.json +26 -19
- package/data/component-props/InputToggle.json +26 -9
- package/data/component-props/IntentMessage.json +38 -9
- package/data/component-props/Link.json +13 -7
- package/data/component-props/LoadingIndicator.json +11 -3
- package/data/component-props/MenuItem.json +38 -19
- package/data/component-props/MenuSelect.json +8 -8
- package/data/component-props/NavigationFooter.json +49 -19
- package/data/component-props/NavigationHeader.json +150 -37
- package/data/component-props/NavigationJumpTo.json +33 -7
- package/data/component-props/Notification.json +39 -21
- package/data/component-props/Pagination.json +39 -11
- package/data/component-props/Panel.json +62 -9
- package/data/component-props/Popover.json +80 -7
- package/data/component-props/PreComposedTable.json +99 -27
- package/data/component-props/SegmentedControl.json +42 -3
- package/data/component-props/StackedBarChart.json +116 -42
- package/data/component-props/TableRow.json +38 -19
- package/data/component-props/Tabs.json +66 -9
- package/data/component-props/Tag.json +34 -17
- package/data/component-props/TagFilter.json +10 -3
- package/data/component-props/Tooltip.json +49 -18
- package/data/component-props/TooltipCondensed.json +18 -9
- package/data/component-props/TooltipTable.json +17 -7
- package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
- package/data/resources/figma-rules.md +2 -2
- package/dist/stdio.js +112 -208
- package/package.json +9 -5
- package/data/component-props/ButtonDropdownV2.json +0 -34
- package/data/component-props/ButtonNew.json +0 -47
- package/data/component-props/ButtonToggleV2.json +0 -44
- package/data/component-props/ButtonV2.json +0 -43
- package/data/zeroheight-cache.json +0 -10259
- package/data/zeroheight-exports/Accordion.md +0 -774
- package/data/zeroheight-exports/Banner.md +0 -691
- package/data/zeroheight-exports/Borders.md +0 -117
- package/data/zeroheight-exports/Buttons.md +0 -1854
- package/data/zeroheight-exports/Callout.md +0 -931
- package/data/zeroheight-exports/Colors.md +0 -1755
- package/data/zeroheight-exports/Content-Card.md +0 -309
- package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
- package/data/zeroheight-exports/Control-Inputs.md +0 -2818
- package/data/zeroheight-exports/Corners.md +0 -104
- package/data/zeroheight-exports/Cover-page.md +0 -3
- package/data/zeroheight-exports/Dialog.md +0 -1367
- package/data/zeroheight-exports/Downloads.md +0 -236
- package/data/zeroheight-exports/Drop-Shadows.md +0 -79
- package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
- package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
- package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
- package/data/zeroheight-exports/Field-Inputs.md +0 -1405
- package/data/zeroheight-exports/Filters.md +0 -1111
- package/data/zeroheight-exports/Getting-Started.md +0 -1157
- package/data/zeroheight-exports/Hero.md +0 -414
- package/data/zeroheight-exports/Icons.md +0 -2067
- package/data/zeroheight-exports/Intent.md +0 -86
- package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
- package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
- package/data/zeroheight-exports/Introduction.md +0 -115
- package/data/zeroheight-exports/Link.md +0 -432
- package/data/zeroheight-exports/Lists.md +0 -986
- package/data/zeroheight-exports/Loading-Indicator.md +0 -41
- package/data/zeroheight-exports/Navigation.md +0 -1190
- package/data/zeroheight-exports/Notification.md +0 -1038
- package/data/zeroheight-exports/Panel.md +0 -1030
- package/data/zeroheight-exports/README.md +0 -122
- package/data/zeroheight-exports/Responsive-Design.md +0 -330
- package/data/zeroheight-exports/Search-Input.md +0 -91
- package/data/zeroheight-exports/Search.md +0 -822
- package/data/zeroheight-exports/Segmented-Control.md +0 -609
- package/data/zeroheight-exports/Spacing.md +0 -297
- package/data/zeroheight-exports/Table.md +0 -5345
- package/data/zeroheight-exports/Tabs.md +0 -1324
- package/data/zeroheight-exports/Tags.md +0 -1545
- package/data/zeroheight-exports/Text-Link.md +0 -268
- package/data/zeroheight-exports/Theming.md +0 -362
- package/data/zeroheight-exports/Tooltips.md +0 -2015
- package/data/zeroheight-exports/Typography.md +0 -800
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# NavigationJumpTo
|
|
2
|
+
|
|
3
|
+
## Source Code
|
|
4
|
+
|
|
5
|
+
The component's source code in the SDS codebase can be found [here](https://github.com/chanzuckerberg/sci-components/blob/main/packages/components/src/core/NavigationJumpTo/index.tsx).
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
**React TypeScript**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { NavigationJumpTo } from "@czi-sds/components";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Code examples
|
|
16
|
+
|
|
17
|
+
### Default NavigationJumpTo
|
|
18
|
+
|
|
19
|
+
Three items, with the two sub-sections of Methods indented under it. The highlight starts on the first item and moves with a click. No refs are attached to sections here, so nothing scrolls: this is the list's default state and nothing more.
|
|
20
|
+
|
|
21
|
+
**Example: DefaultNavigationJumpTo**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// The list on its own: items in the order they are given, sub-items indented
|
|
25
|
+
// one level under the item they belong to, and the highlight starting on the
|
|
26
|
+
// first item.
|
|
27
|
+
//
|
|
28
|
+
// The refs are held but never handed to an element, which keeps this to the
|
|
29
|
+
// shape of the component. Nothing scrolls, and the highlight moves only when an
|
|
30
|
+
// item is clicked. The examples below point the refs at sections, which is what
|
|
31
|
+
// puts the highlight under the reader's own scrolling.
|
|
32
|
+
|
|
33
|
+
import { NavigationJumpTo } from "@czi-sds/components";
|
|
34
|
+
import { useRef } from "react";
|
|
35
|
+
|
|
36
|
+
function App() {
|
|
37
|
+
const overviewRef = useRef<HTMLElement | null>(null);
|
|
38
|
+
const methodsRef = useRef<HTMLElement | null>(null);
|
|
39
|
+
const samplePrepRef = useRef<HTMLElement | null>(null);
|
|
40
|
+
const sequencingRef = useRef<HTMLElement | null>(null);
|
|
41
|
+
const resultsRef = useRef<HTMLElement | null>(null);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="app">
|
|
45
|
+
<NavigationJumpTo
|
|
46
|
+
items={[
|
|
47
|
+
{ elementRef: overviewRef, title: "Overview" },
|
|
48
|
+
{
|
|
49
|
+
elementRef: methodsRef,
|
|
50
|
+
subItems: [
|
|
51
|
+
{ elementRef: samplePrepRef, title: "Sample prep" },
|
|
52
|
+
{ elementRef: sequencingRef, title: "Sequencing" },
|
|
53
|
+
],
|
|
54
|
+
title: "Methods",
|
|
55
|
+
},
|
|
56
|
+
{ elementRef: resultsRef, title: "Results" },
|
|
57
|
+
]}
|
|
58
|
+
width="200px"
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default App;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Sections in a scrolling panel
|
|
68
|
+
|
|
69
|
+
Three sections with refs wired to the items. Clicking scrolls the panel, and scrolling the panel moves the highlight. Bringing a section into view scrolls every scroll container around it, so this example holds the docs page still while the panel moves; a page that scrolls its own sections wants the opposite.
|
|
70
|
+
|
|
71
|
+
**Example: JumpToSections**
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// Each item points at a section through a ref, and the component keeps the
|
|
75
|
+
// highlight on whichever section is showing. Clicking an item scrolls its
|
|
76
|
+
// section into view, which here means scrolling the panel on the right.
|
|
77
|
+
//
|
|
78
|
+
// Give the sections ids: the tabs point their aria-controls at them, and
|
|
79
|
+
// without ids they reference elements that do not exist.
|
|
80
|
+
//
|
|
81
|
+
// "Into view" means every scroll container around the section, so this page
|
|
82
|
+
// scrolls along with the panel. holdPageStill pins it for the length of the
|
|
83
|
+
// animation, which a page that scrolls its own sections would not want.
|
|
84
|
+
|
|
85
|
+
import {
|
|
86
|
+
NavigationJumpTo,
|
|
87
|
+
fontBodyS,
|
|
88
|
+
fontHeaderM,
|
|
89
|
+
getSemanticColors,
|
|
90
|
+
type CommonThemeProps,
|
|
91
|
+
} from "@czi-sds/components";
|
|
92
|
+
import styled from "@emotion/styled";
|
|
93
|
+
import { useRef } from "react";
|
|
94
|
+
|
|
95
|
+
const Layout = styled.div`
|
|
96
|
+
display: flex;
|
|
97
|
+
gap: 16px;
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
const ScrollArea = styled.div<CommonThemeProps>`
|
|
101
|
+
${(props) => {
|
|
102
|
+
const semanticColors = getSemanticColors(props);
|
|
103
|
+
|
|
104
|
+
return `
|
|
105
|
+
border: 1px solid ${semanticColors?.base?.divider};
|
|
106
|
+
border-radius: 4px;
|
|
107
|
+
flex: 1;
|
|
108
|
+
height: 280px;
|
|
109
|
+
overflow-y: auto;
|
|
110
|
+
padding: 16px;
|
|
111
|
+
`;
|
|
112
|
+
}}
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
const Section = styled.section<CommonThemeProps>`
|
|
116
|
+
${fontBodyS}
|
|
117
|
+
|
|
118
|
+
${(props) => {
|
|
119
|
+
const semanticColors = getSemanticColors(props);
|
|
120
|
+
|
|
121
|
+
return `
|
|
122
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
123
|
+
min-height: 320px;
|
|
124
|
+
|
|
125
|
+
h3 {
|
|
126
|
+
${fontHeaderM(props)}
|
|
127
|
+
margin: 0 0 8px 0;
|
|
128
|
+
}
|
|
129
|
+
`;
|
|
130
|
+
}}
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
function holdPageStill() {
|
|
134
|
+
const { scrollX, scrollY } = window;
|
|
135
|
+
const until = performance.now() + 500;
|
|
136
|
+
|
|
137
|
+
const pin = () => {
|
|
138
|
+
window.scrollTo({ behavior: "instant", left: scrollX, top: scrollY });
|
|
139
|
+
if (performance.now() < until) requestAnimationFrame(pin);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
requestAnimationFrame(pin);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function App() {
|
|
146
|
+
const overviewRef = useRef<HTMLElement | null>(null);
|
|
147
|
+
const methodsRef = useRef<HTMLElement | null>(null);
|
|
148
|
+
const resultsRef = useRef<HTMLElement | null>(null);
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<div className="app">
|
|
152
|
+
<Layout>
|
|
153
|
+
<NavigationJumpTo
|
|
154
|
+
items={[
|
|
155
|
+
{ elementRef: overviewRef, title: "Overview" },
|
|
156
|
+
{ elementRef: methodsRef, title: "Methods" },
|
|
157
|
+
{ elementRef: resultsRef, title: "Results" },
|
|
158
|
+
]}
|
|
159
|
+
onChange={(_value, _event, type) => {
|
|
160
|
+
if (type === "click") holdPageStill();
|
|
161
|
+
}}
|
|
162
|
+
width="160px"
|
|
163
|
+
/>
|
|
164
|
+
|
|
165
|
+
<ScrollArea>
|
|
166
|
+
<Section id="overview" ref={overviewRef}>
|
|
167
|
+
<h3>Overview</h3>
|
|
168
|
+
<p>What the study set out to measure and why.</p>
|
|
169
|
+
</Section>
|
|
170
|
+
<Section id="methods" ref={methodsRef}>
|
|
171
|
+
<h3>Methods</h3>
|
|
172
|
+
<p>How the samples were collected and processed.</p>
|
|
173
|
+
</Section>
|
|
174
|
+
<Section id="results" ref={resultsRef}>
|
|
175
|
+
<h3>Results</h3>
|
|
176
|
+
<p>What came out of the analysis.</p>
|
|
177
|
+
</Section>
|
|
178
|
+
</ScrollArea>
|
|
179
|
+
</Layout>
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default App;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Sub-items and onChange
|
|
188
|
+
|
|
189
|
+
Sub-items render indented under their parent, and the readout shows the index that `onChange` reports along with what triggered it. Click an item, then scroll the panel by hand to see the other kind of change arrive. The sub-sections are siblings of Methods rather than children, because a parent section wrapping them would keep the highlight for itself.
|
|
190
|
+
|
|
191
|
+
**Example: JumpToSubItems**
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
// subItems nest one level and render indented under their parent. onChange
|
|
195
|
+
// reports the index of the highlighted item, counting sub-items in the order
|
|
196
|
+
// they render, along with whether a click or a scroll caused the change: click
|
|
197
|
+
// an item, then scroll the panel by hand to see the other kind arrive.
|
|
198
|
+
//
|
|
199
|
+
// The sub-sections are siblings of Methods rather than children of it. Nested
|
|
200
|
+
// inside, Methods would be in view whenever they were, and since the highlight
|
|
201
|
+
// goes to the first item in view, the sub-items would never take it on scroll.
|
|
202
|
+
//
|
|
203
|
+
// Bringing a section into view scrolls every container around it, this page
|
|
204
|
+
// included, so holdPageStill pins the page while the panel moves. A page that
|
|
205
|
+
// scrolls its own sections wants none of that.
|
|
206
|
+
|
|
207
|
+
import {
|
|
208
|
+
NavigationJumpTo,
|
|
209
|
+
fontBodyS,
|
|
210
|
+
fontBodyXs,
|
|
211
|
+
fontHeaderM,
|
|
212
|
+
fontHeaderS,
|
|
213
|
+
getSemanticColors,
|
|
214
|
+
getSpaces,
|
|
215
|
+
type CommonThemeProps,
|
|
216
|
+
} from "@czi-sds/components";
|
|
217
|
+
import styled from "@emotion/styled";
|
|
218
|
+
import { useRef, useState } from "react";
|
|
219
|
+
|
|
220
|
+
const Layout = styled.div`
|
|
221
|
+
display: flex;
|
|
222
|
+
gap: 16px;
|
|
223
|
+
`;
|
|
224
|
+
|
|
225
|
+
const ScrollArea = styled.div<CommonThemeProps>`
|
|
226
|
+
${(props) => {
|
|
227
|
+
const semanticColors = getSemanticColors(props);
|
|
228
|
+
|
|
229
|
+
return `
|
|
230
|
+
border: 1px solid ${semanticColors?.base?.divider};
|
|
231
|
+
border-radius: 4px;
|
|
232
|
+
flex: 1;
|
|
233
|
+
height: 280px;
|
|
234
|
+
overflow-y: auto;
|
|
235
|
+
padding: 16px;
|
|
236
|
+
`;
|
|
237
|
+
}}
|
|
238
|
+
`;
|
|
239
|
+
|
|
240
|
+
const Section = styled.section<CommonThemeProps>`
|
|
241
|
+
${fontBodyS}
|
|
242
|
+
|
|
243
|
+
${(props) => {
|
|
244
|
+
const semanticColors = getSemanticColors(props);
|
|
245
|
+
|
|
246
|
+
return `
|
|
247
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
248
|
+
min-height: 320px;
|
|
249
|
+
|
|
250
|
+
h3 {
|
|
251
|
+
${fontHeaderM(props)}
|
|
252
|
+
margin: 0 0 8px 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
h4 {
|
|
256
|
+
${fontHeaderS(props)}
|
|
257
|
+
margin: 0 0 8px 0;
|
|
258
|
+
}
|
|
259
|
+
`;
|
|
260
|
+
}}
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
const Sidebar = styled.p<CommonThemeProps>`
|
|
264
|
+
${(props) => {
|
|
265
|
+
const spaces = getSpaces(props);
|
|
266
|
+
|
|
267
|
+
return `
|
|
268
|
+
display: flex;
|
|
269
|
+
flex-direction: column;
|
|
270
|
+
gap: ${spaces?.xl}px;
|
|
271
|
+
`;
|
|
272
|
+
}}
|
|
273
|
+
`;
|
|
274
|
+
|
|
275
|
+
const Readout = styled.p<CommonThemeProps>`
|
|
276
|
+
${fontBodyXs}
|
|
277
|
+
|
|
278
|
+
${(props) => {
|
|
279
|
+
const semanticColors = getSemanticColors(props);
|
|
280
|
+
|
|
281
|
+
return `
|
|
282
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
283
|
+
`;
|
|
284
|
+
}}
|
|
285
|
+
`;
|
|
286
|
+
|
|
287
|
+
function holdPageStill() {
|
|
288
|
+
const { scrollX, scrollY } = window;
|
|
289
|
+
const until = performance.now() + 500;
|
|
290
|
+
|
|
291
|
+
const pin = () => {
|
|
292
|
+
window.scrollTo({ behavior: "instant", left: scrollX, top: scrollY });
|
|
293
|
+
if (performance.now() < until) requestAnimationFrame(pin);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
requestAnimationFrame(pin);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function App() {
|
|
300
|
+
const [lastChange, setLastChange] = useState("none yet");
|
|
301
|
+
const methodsRef = useRef<HTMLElement | null>(null);
|
|
302
|
+
const samplePrepRef = useRef<HTMLElement | null>(null);
|
|
303
|
+
const sequencingRef = useRef<HTMLElement | null>(null);
|
|
304
|
+
const resultsRef = useRef<HTMLElement | null>(null);
|
|
305
|
+
const discussionRef = useRef<HTMLElement | null>(null);
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<div className="app">
|
|
309
|
+
<Layout>
|
|
310
|
+
<Sidebar>
|
|
311
|
+
<NavigationJumpTo
|
|
312
|
+
items={[
|
|
313
|
+
{
|
|
314
|
+
elementRef: methodsRef,
|
|
315
|
+
subItems: [
|
|
316
|
+
{ elementRef: samplePrepRef, title: "Sample prep" },
|
|
317
|
+
{ elementRef: sequencingRef, title: "Sequencing" },
|
|
318
|
+
],
|
|
319
|
+
title: "Methods",
|
|
320
|
+
},
|
|
321
|
+
{ elementRef: resultsRef, title: "Results" },
|
|
322
|
+
{ elementRef: discussionRef, title: "Discussion" },
|
|
323
|
+
]}
|
|
324
|
+
onChange={(value, _event, type) => {
|
|
325
|
+
setLastChange(`index ${value}, from ${type}`);
|
|
326
|
+
if (type === "click") holdPageStill();
|
|
327
|
+
}}
|
|
328
|
+
width="160px"
|
|
329
|
+
/>
|
|
330
|
+
|
|
331
|
+
<Readout>
|
|
332
|
+
Last change: <br />
|
|
333
|
+
{lastChange}
|
|
334
|
+
</Readout>
|
|
335
|
+
</Sidebar>
|
|
336
|
+
|
|
337
|
+
<ScrollArea>
|
|
338
|
+
<Section id="methods" ref={methodsRef}>
|
|
339
|
+
<h3>Methods</h3>
|
|
340
|
+
<p>How the samples were collected and processed.</p>
|
|
341
|
+
</Section>
|
|
342
|
+
<Section id="sample-prep" ref={samplePrepRef}>
|
|
343
|
+
<h4>Sample prep</h4>
|
|
344
|
+
<p>Dissociation, staining, and quality gates.</p>
|
|
345
|
+
</Section>
|
|
346
|
+
<Section id="sequencing" ref={sequencingRef}>
|
|
347
|
+
<h4>Sequencing</h4>
|
|
348
|
+
<p>Library construction and read depth per sample.</p>
|
|
349
|
+
</Section>
|
|
350
|
+
<Section id="results" ref={resultsRef}>
|
|
351
|
+
<h3>Results</h3>
|
|
352
|
+
<p>What came out of the analysis.</p>
|
|
353
|
+
</Section>
|
|
354
|
+
<Section id="discussion" ref={discussionRef}>
|
|
355
|
+
<h3>Discussion</h3>
|
|
356
|
+
<p>What the numbers do and do not support.</p>
|
|
357
|
+
</Section>
|
|
358
|
+
</ScrollArea>
|
|
359
|
+
</Layout>
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export default App;
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## SDS vs MUI
|
|
368
|
+
|
|
369
|
+
NavigationJumpTo is built on MUI's [Tabs](https://mui.com/material-ui/react-tabs/), but it is not a tab strip: the tabs are page sections, and which one is highlighted follows the scroll position rather than a value you hold. It differs from Tabs in these ways:
|
|
370
|
+
|
|
371
|
+
- `value`: the component owns it. An IntersectionObserver watches the sections and highlights the first one in view, so there is no `value` prop to set.
|
|
372
|
+
|
|
373
|
+
- `onChange`: MUI's signature is replaced. It reports the index that became active, the event, and whether a click or a scroll caused it, and it fires once per change rather than on every scroll frame.
|
|
374
|
+
|
|
375
|
+
- `orientation`: `"vertical"` is set for you, and the design only covers vertical. Passing `"horizontal"` is possible, since your props are applied last, but nothing is styled for it.
|
|
376
|
+
|
|
377
|
+
- **Colors are fixed.** The indicator is a 2px rule in the accent active color and the label colors come from the theme. There is no `indicatorColor` or `textColor` to set.
|
|
378
|
+
|
|
379
|
+
- **No icons.** Items are a title and a ref; there is nowhere to put one.
|
|
380
|
+
|
|
381
|
+
- **Layout is baked in.** The component is sticky 24px from the top of its scroll container, draws a 1px rule down its left edge, and reserves 16px below and 12px to its right. Width is the only dimension exposed.
|
|
382
|
+
|
|
383
|
+
## Behavior notes
|
|
384
|
+
|
|
385
|
+
- Each item carries a ref to its section, and the highlight follows the first section in view, in the order the items are listed. An earlier section still showing by a few pixels therefore keeps the highlight, which is the problem `offsetTop` solves. While a click's smooth scroll is running the scroll-driven update is suspended, so the indicator does not race through the sections on the way.
|
|
386
|
+
|
|
387
|
+
- Clicking an item scrolls its section into view. With `offsetTop` left at `0` that is a plain `scrollIntoView`, which works inside a scrolling container as well as on the page. Note that it scrolls every container around the section, so a section in a panel moves the panel and the page holding it.
|
|
388
|
+
|
|
389
|
+
- Setting `offsetTop` changes the mechanism: the component scrolls the window and finds the section by id. Sections therefore need `id` attributes, and a section without one simply will not scroll. It also assumes the page itself scrolls, so pair it with page-level scrolling rather than a scrolling panel.
|
|
390
|
+
|
|
391
|
+
- Sub-items nest exactly one level through `subItems` and render indented under their parent. They are ordinary items as far as indexing goes: a parent with two sub-items occupies indices 0, 1, and 2.
|
|
392
|
+
|
|
393
|
+
- Each tab points `aria-controls` at its section's id, falling back to a generated `navigation-panel-N` when the section has no id. Give your sections ids so the reference resolves, or the tabs point at elements that do not exist.
|
|
394
|
+
|
|
395
|
+
- The strip labels itself `navigation-jump-to`. Pass an `aria-label` of your own when a page has more than one.
|
|
396
|
+
|
|
397
|
+
## Props
|
|
398
|
+
|
|
399
|
+
Everything below is SDS's own. Other [Tabs props](https://mui.com/material-ui/api/tabs/) pass through to the underlying element, and because they are applied after the component's own, they can override them.
|
|
400
|
+
|
|
401
|
+
| Name | Type | Default | Description |
|
|
402
|
+
| ----------- | ------------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
403
|
+
| `items` | `Item[]` | - (required) | The sections to list. See the shape below. |
|
|
404
|
+
| `offsetTop` | `number` | `0` | How many pixels above a section the highlight should switch to it, which is what you want when a sticky header covers the top of the page. Any non-zero value also switches clicks to window scrolling by element id. |
|
|
405
|
+
| `onChange` | `function` | - | `(value: number, event?: React.SyntheticEvent, type?: "click" \| "scroll") => void`. Called when the highlighted item changes, with its index and what caused the change. For a scroll the event is synthesized, not a real DOM event. |
|
|
406
|
+
| `width` | `CSSProperties["width"]` | `"100%"` | The width of the strip and of every item in it. Long titles wrap rather than truncate. |
|
|
407
|
+
|
|
408
|
+
## Item
|
|
409
|
+
|
|
410
|
+
| Name | Type | Default | Description |
|
|
411
|
+
| ------------ | --------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------- |
|
|
412
|
+
| `title` | `string` | - | The item's label. It is also kebab-cased into the tab's id, so keep titles distinct. |
|
|
413
|
+
| `elementRef` | `MutableRefObject<HTMLElement \| null>` | - | A ref on the section this item points at. The component observes and scrolls whatever the ref holds. |
|
|
414
|
+
| `subItems` | `{ title, elementRef }[]` | - | Indented children, one level only. Sub-items cannot nest sub-items of their own. |
|