rift-ds 1.15.0 → 1.16.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 +3 -3
- package/components/Checkbox/Checkbox.css +4 -2
- package/components/Drawer/Drawer.d.ts +6 -0
- package/components/Drawer/Drawer.js +11 -1
- package/components/MegaMenu/MegaMenu.css +271 -0
- package/components/MegaMenu/MegaMenu.d.ts +72 -0
- package/components/MegaMenu/MegaMenu.js +71 -0
- package/components/Nav/Nav.d.ts +7 -2
- package/components/ProgressiveBlur/ProgressiveBlur.css +102 -0
- package/components/ProgressiveBlur/ProgressiveBlur.d.ts +30 -0
- package/components/ProgressiveBlur/ProgressiveBlur.js +27 -0
- package/components/PromptSuggestions/PromptSuggestions.css +1 -1
- package/components/QuestionCard/QuestionCard.css +3 -2
- package/components/QuestionCard/QuestionCard.js +2 -8
- package/components/RadioButton/RadioButton.css +5 -3
- package/components/SiteHeader/SiteHeader.css +305 -0
- package/components/SiteHeader/SiteHeader.d.ts +117 -0
- package/components/SiteHeader/SiteHeader.js +329 -0
- package/components/registry.json +25 -1
- package/components/registry.json.d.ts +25 -1
- package/components/registry.json.js +1 -1
- package/index.d.ts +3 -0
- package/index.js +6 -0
- package/package.json +1 -1
- package/tokens/registry.json +1 -0
- package/tokens/registry.json.d.ts +1 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +1 -0
- package/tokens/tokens-light.css +1 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import "./ProgressiveBlur.css";
|
|
4
|
+
function ProgressiveBlur({
|
|
5
|
+
side = "top",
|
|
6
|
+
tinted = false,
|
|
7
|
+
className = "",
|
|
8
|
+
ref,
|
|
9
|
+
...rest
|
|
10
|
+
}) {
|
|
11
|
+
const baseClass = "ds-progressive-blur";
|
|
12
|
+
const classes = [
|
|
13
|
+
baseClass,
|
|
14
|
+
`${baseClass}--${side}`,
|
|
15
|
+
tinted && `${baseClass}--tinted`,
|
|
16
|
+
className
|
|
17
|
+
].filter(Boolean).join(" ");
|
|
18
|
+
return /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", ...rest, ref, className: classes, children: [
|
|
19
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__layer` }),
|
|
20
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__layer` }),
|
|
21
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__layer` }),
|
|
22
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__layer` })
|
|
23
|
+
] });
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
ProgressiveBlur
|
|
27
|
+
};
|
|
@@ -508,7 +508,7 @@
|
|
|
508
508
|
.ds-prompt-suggestions--kept .ds-prompt-suggestions__tile,
|
|
509
509
|
.ds-prompt-suggestions--kept .ds-prompt-suggestions__card {
|
|
510
510
|
border-style: dashed;
|
|
511
|
-
border-color: var(--color-
|
|
511
|
+
border-color: var(--color-bg-container-border);
|
|
512
512
|
/* A record carries no fill, like QuestionCard's rows once answered: the
|
|
513
513
|
outline alone says which way the conversation went. */
|
|
514
514
|
background-color: transparent;
|
|
@@ -113,7 +113,8 @@
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
/* An open row is drawn dashed
|
|
116
|
+
/* An open row is drawn dashed, in the suggestion chip's hairline colour:
|
|
117
|
+
an answer on offer, not yet given. A pick
|
|
117
118
|
turns it solid, in the strongest ink, with the control's indicator on at
|
|
118
119
|
its end, so a chosen row reads as decided at a glance and stays readable
|
|
119
120
|
after the moment has passed. The row is a control, so it sits on Input's
|
|
@@ -128,7 +129,7 @@
|
|
|
128
129
|
min-width: 0;
|
|
129
130
|
margin: 0;
|
|
130
131
|
padding: var(--padding-300) var(--padding-400);
|
|
131
|
-
border: var(--border-025) dashed var(--color-
|
|
132
|
+
border: var(--border-025) dashed var(--color-bg-container-border);
|
|
132
133
|
border-radius: var(--radius-300);
|
|
133
134
|
background-color: var(--color-input-bg-primary);
|
|
134
135
|
color: var(--color-text-primary);
|
|
@@ -92,14 +92,8 @@ const QuestionCard = React.forwardRef(
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
const next = { values: [option.value], other: "", otherOpen: false };
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
submit({ ...drafts, [current.id]: next });
|
|
98
|
-
} else if (step < questions.length - 1) {
|
|
99
|
-
advance(next);
|
|
100
|
-
} else {
|
|
101
|
-
writeDraft(current, next);
|
|
102
|
-
}
|
|
95
|
+
writeDraft(current, next);
|
|
96
|
+
if (!isSet) submit({ ...drafts, [current.id]: next });
|
|
103
97
|
};
|
|
104
98
|
const openOther = () => {
|
|
105
99
|
if (!live) return;
|
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
width: 24px;
|
|
27
27
|
height: 24px;
|
|
28
28
|
box-sizing: border-box;
|
|
29
|
-
border: var(--border-050) solid var(--color-input-border
|
|
30
|
-
|
|
29
|
+
border: var(--border-050) solid var(--color-input-indicator-border);
|
|
30
|
+
/* Always a circle, whatever the theme's radius: a radio's shape is its
|
|
31
|
+
meaning, so it never follows --radius-pill into a square. */
|
|
32
|
+
border-radius: 50%;
|
|
31
33
|
background-color: var(--color-input-bg-primary);
|
|
32
34
|
display: inline-flex;
|
|
33
35
|
align-items: center;
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
.ds-radio__dot {
|
|
44
46
|
width: 12px;
|
|
45
47
|
height: 12px;
|
|
46
|
-
border-radius:
|
|
48
|
+
border-radius: 50%;
|
|
47
49
|
background-color: var(--color-action-primary-bg);
|
|
48
50
|
transform: scale(0);
|
|
49
51
|
transition: transform var(--motion-duration-base) var(--motion-ease-standard);
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
SITE HEADER
|
|
3
|
+
The site's top bar: brand, section pills with
|
|
4
|
+
mega menus, actions, and a drawer once folded
|
|
5
|
+
============================================ */
|
|
6
|
+
|
|
7
|
+
/* Base */
|
|
8
|
+
.ds-site-header {
|
|
9
|
+
/* The bar's height. An override hook: the blur's reach and a page's own
|
|
10
|
+
anchor offsets read it. */
|
|
11
|
+
--ds-site-header-height: calc(var(--control-height-default) + var(--padding-1000));
|
|
12
|
+
/* How far the blur dissolves below the bar. */
|
|
13
|
+
--ds-site-header-blur-reach: var(--gap-1500);
|
|
14
|
+
position: relative;
|
|
15
|
+
/* Lifts the open panel over the page's flow when the header is not sticky. */
|
|
16
|
+
z-index: 1;
|
|
17
|
+
width: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Sticky: rides up with whatever sits above it, then holds at the top. */
|
|
21
|
+
.ds-site-header--sticky {
|
|
22
|
+
position: sticky;
|
|
23
|
+
top: 0;
|
|
24
|
+
z-index: var(--layer-sticky);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* The blur stack, viewport-fixed (a sticky box is not a containing block
|
|
28
|
+
for it) so it spans the full width whatever the host's gutters. It sits
|
|
29
|
+
inside the header's stacking context, behind the bar's controls. Never a
|
|
30
|
+
filter on the header itself: that would make the header a backdrop root
|
|
31
|
+
and the mega menu's own glass would sample only the header's subtree. */
|
|
32
|
+
.ds-site-header__blur.ds-progressive-blur {
|
|
33
|
+
position: fixed;
|
|
34
|
+
inset: 0 0 auto;
|
|
35
|
+
height: calc(var(--ds-site-header-height) + var(--ds-site-header-blur-reach));
|
|
36
|
+
z-index: -1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* The bar: brand, pills, then the end cluster pushed to the far side. */
|
|
40
|
+
.ds-site-header__bar {
|
|
41
|
+
position: relative;
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: var(--gap-400);
|
|
45
|
+
height: var(--ds-site-header-height);
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
/* The folded bar keeps its pills out of flow at full width so they stay
|
|
48
|
+
measurable; clipping the inline axis keeps them from widening the page
|
|
49
|
+
into a sideways scroll. The margin leaves room for a focus ring at the
|
|
50
|
+
bar's edge, and the block axis stays open for the menu panel. */
|
|
51
|
+
overflow-x: clip;
|
|
52
|
+
overflow-clip-margin: var(--gap-200);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.ds-site-header__brand {
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
flex-shrink: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* The pills sit beside the brand rather than centred on the page:
|
|
62
|
+
--gap-1000 from the brand (the bar's own gap plus this margin), and the
|
|
63
|
+
first pill's inline padding sets the rest of the visual gap. */
|
|
64
|
+
.ds-site-header__nav {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: var(--gap-100);
|
|
68
|
+
margin-inline-start: calc(var(--gap-1000) - var(--gap-400));
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.ds-site-header__end {
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: var(--gap-400);
|
|
76
|
+
margin-inline-start: auto;
|
|
77
|
+
flex-shrink: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ds-site-header__actions,
|
|
81
|
+
.ds-site-header__wide-actions {
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: var(--gap-400);
|
|
85
|
+
flex-shrink: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ds-site-header__section {
|
|
89
|
+
position: relative;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Pills: a pill-radius link at the compact control height, so a caret's
|
|
93
|
+
icon box never makes one pill taller than its neighbours. */
|
|
94
|
+
.ds-site-header__pill {
|
|
95
|
+
display: inline-flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
gap: var(--gap-050);
|
|
98
|
+
height: var(--control-height-compact);
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
padding-inline: var(--padding-400);
|
|
101
|
+
border-radius: var(--radius-pill);
|
|
102
|
+
background-color: transparent;
|
|
103
|
+
color: var(--color-text-primary);
|
|
104
|
+
font-family: var(--font-paragraph-sm-emphasis-family);
|
|
105
|
+
font-size: var(--font-paragraph-sm-emphasis-size);
|
|
106
|
+
font-weight: var(--font-paragraph-sm-emphasis-weight);
|
|
107
|
+
letter-spacing: var(--font-paragraph-sm-emphasis-letter-spacing);
|
|
108
|
+
line-height: 1;
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
text-decoration: none;
|
|
111
|
+
transition:
|
|
112
|
+
background-color var(--motion-duration-fast) var(--motion-ease-standard),
|
|
113
|
+
color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Hover is the tertiary family's wash, as on a tertiary Button, a NavList
|
|
117
|
+
row and the MegaMenu's rows: a container fill can match the page floor
|
|
118
|
+
in a light theme and vanish. */
|
|
119
|
+
.ds-site-header__pill:hover {
|
|
120
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.ds-site-header__pill:focus-visible {
|
|
124
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
125
|
+
outline-offset: 2px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* An open menu holds the hover fill and nothing more: opening a menu is
|
|
129
|
+
not a selection. Declared before --current so the current section keeps
|
|
130
|
+
its colour while its menu is open. */
|
|
131
|
+
.ds-site-header__pill--open {
|
|
132
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* The current section is the selected item of an exclusive set, so it
|
|
136
|
+
takes the action selection pair, SegmentedControl's active segment; a
|
|
137
|
+
neutral fill would read as the hover state over the glassy blur. */
|
|
138
|
+
.ds-site-header__pill--current {
|
|
139
|
+
background-color: var(--color-action-primary-bg);
|
|
140
|
+
color: var(--color-action-primary-text);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.ds-site-header__pill--current:hover {
|
|
144
|
+
background-color: var(--color-action-primary-bg-hover);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.ds-site-header__caret {
|
|
148
|
+
--icon-size: var(--icon-size-500);
|
|
149
|
+
line-height: 1;
|
|
150
|
+
color: var(--color-text-secondary);
|
|
151
|
+
transition: rotate var(--motion-duration-base) var(--motion-ease-standard);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.ds-site-header__pill--current .ds-site-header__caret {
|
|
155
|
+
color: inherit;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.ds-site-header__pill--open .ds-site-header__caret {
|
|
159
|
+
rotate: 180deg;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* The menu button only exists once the bar folds. */
|
|
163
|
+
.ds-site-header__menu-button {
|
|
164
|
+
display: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* While the drawer is open its close button stands in this exact spot, on
|
|
168
|
+
the top layer; the button underneath would only show through the scrim. */
|
|
169
|
+
.ds-site-header__menu-button--hidden {
|
|
170
|
+
visibility: hidden;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* The mega menu hangs under the bar across its full width. It is not
|
|
174
|
+
anchored to a control (useAnchoredPosition's job): it belongs to the bar
|
|
175
|
+
and spans it, so it rides the bar's own box.
|
|
176
|
+
The wrapper keeps the height of the last section shown after the menu
|
|
177
|
+
closes, so it never takes a press itself: a transparent box that size
|
|
178
|
+
would sit over the page and swallow its clicks. Only the menu does, and a
|
|
179
|
+
closed menu is hidden, which takes it out of hit testing too. */
|
|
180
|
+
.ds-site-header__panel {
|
|
181
|
+
position: absolute;
|
|
182
|
+
inset-block-start: calc(100% + var(--gap-200));
|
|
183
|
+
inset-inline: 0;
|
|
184
|
+
pointer-events: none;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.ds-site-header__panel > .ds-mega-menu {
|
|
188
|
+
pointer-events: auto;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Folded: the pills and the wide actions leave the row but stay measurable
|
|
192
|
+
(hidden, out of flow, inert), and the menu button takes their place. */
|
|
193
|
+
.ds-site-header--folded .ds-site-header__nav,
|
|
194
|
+
.ds-site-header--folded .ds-site-header__wide-actions {
|
|
195
|
+
position: absolute;
|
|
196
|
+
visibility: hidden;
|
|
197
|
+
pointer-events: none;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ds-site-header--folded .ds-site-header__menu-button {
|
|
201
|
+
display: inline-flex;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.ds-site-header--folded .ds-site-header__panel {
|
|
205
|
+
display: none;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* Before the bar has measured itself (the first frame, which a
|
|
209
|
+
server-rendered page shows before its script runs), it folds by CSS below
|
|
210
|
+
the `initialFold` step, so a phone never sees the pills spill past the
|
|
211
|
+
actions. The first measurement replaces this in the same commit as
|
|
212
|
+
hydration. */
|
|
213
|
+
@media (max-width: 959px) {
|
|
214
|
+
.ds-site-header--prefold-tablet .ds-site-header__nav,
|
|
215
|
+
.ds-site-header--prefold-tablet .ds-site-header__wide-actions {
|
|
216
|
+
position: absolute;
|
|
217
|
+
visibility: hidden;
|
|
218
|
+
pointer-events: none;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.ds-site-header--prefold-tablet .ds-site-header__menu-button {
|
|
222
|
+
display: inline-flex;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@media (max-width: 1279px) {
|
|
227
|
+
.ds-site-header--prefold-mid .ds-site-header__nav,
|
|
228
|
+
.ds-site-header--prefold-mid .ds-site-header__wide-actions {
|
|
229
|
+
position: absolute;
|
|
230
|
+
visibility: hidden;
|
|
231
|
+
pointer-events: none;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.ds-site-header--prefold-mid .ds-site-header__menu-button {
|
|
235
|
+
display: inline-flex;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* The drawer: its brand row lands on the bar's own brand. SiteHeader
|
|
240
|
+
measures the bar as the drawer opens (its top, which is 0 once stuck and
|
|
241
|
+
the host's gutter at rest, and its inline start) and the panel takes
|
|
242
|
+
those as its padding, with the header row the bar's height, so the
|
|
243
|
+
panel slides in under a brand that does not move. */
|
|
244
|
+
.ds-site-header__drawer .ds-drawer__panel {
|
|
245
|
+
width: min(320px, 80vw);
|
|
246
|
+
padding-block-start: var(--ds-site-header-drawer-top, var(--padding-500));
|
|
247
|
+
padding-inline-start: var(--ds-site-header-drawer-start, var(--padding-500));
|
|
248
|
+
/* The floating glass the command palette wears, so the page reaches
|
|
249
|
+
through the menu frosted rather than behind an opaque sheet. The
|
|
250
|
+
panel lets its close button out (below); its body still scrolls. */
|
|
251
|
+
background-color: var(--color-bg-glass);
|
|
252
|
+
backdrop-filter: blur(24px);
|
|
253
|
+
overflow: visible;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* The drawer's close button swaps with the menu button: it sits on the
|
|
257
|
+
scrim exactly where the menu button was (measured as the drawer opens),
|
|
258
|
+
and fades in once the panel has slid home, so the X never travels with
|
|
259
|
+
it. It stays the drawer's own button, inside its focus trap. */
|
|
260
|
+
.ds-site-header__drawer .ds-drawer__close {
|
|
261
|
+
position: absolute;
|
|
262
|
+
inset-block-start: var(--ds-site-header-close-top, var(--padding-500));
|
|
263
|
+
inset-inline-start: var(--ds-site-header-close-start, auto);
|
|
264
|
+
width: var(--control-height-default);
|
|
265
|
+
height: var(--control-height-default);
|
|
266
|
+
/* ds-allow(color): on-scrim furniture, as Lightbox's: the scrim is dark in both themes, so the glyph is white in both. */
|
|
267
|
+
color: #ffffff;
|
|
268
|
+
opacity: 0;
|
|
269
|
+
transition: opacity var(--motion-duration-fast) var(--motion-ease-standard);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* The glyph at the menu button's own size, so the swap reads as one icon
|
|
273
|
+
turning into another. */
|
|
274
|
+
.ds-site-header__drawer .ds-drawer__close .material-symbols-rounded {
|
|
275
|
+
--icon-size: var(--icon-size-600);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.ds-site-header__drawer .ds-drawer__panel[data-state="open"] .ds-drawer__close {
|
|
279
|
+
opacity: 1;
|
|
280
|
+
transition-delay: var(--motion-duration-slow);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.ds-site-header__drawer .ds-drawer__close:hover {
|
|
284
|
+
/* ds-allow(color): the hover wash on the always-dark scrim, as Lightbox's controls. */
|
|
285
|
+
background-color: rgba(255, 255, 255, 0.15);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* The footer row starts under the brand, not at the panel's far edge. */
|
|
289
|
+
.ds-site-header__drawer .ds-drawer__footer {
|
|
290
|
+
justify-content: flex-start;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.ds-site-header__drawer .ds-drawer__header {
|
|
294
|
+
align-items: center;
|
|
295
|
+
height: var(--ds-site-header-height, auto);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.ds-site-header__drawer-brand {
|
|
299
|
+
display: flex;
|
|
300
|
+
align-items: center;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.ds-site-header__drawer .ds-nav-list {
|
|
304
|
+
width: 100%;
|
|
305
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { LinkComponent } from '../../behaviors/LinkProvider';
|
|
3
|
+
import { MegaMenuItem, MegaMenuShowcase } from '../MegaMenu/MegaMenu';
|
|
4
|
+
/** One top-level section of the site: a pill in the bar, a row in the drawer. */
|
|
5
|
+
export interface SiteHeaderSection {
|
|
6
|
+
/** A stable key for the section. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** The pill's text. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** The section's landing page: where a click on the pill goes. */
|
|
11
|
+
href: string;
|
|
12
|
+
/**
|
|
13
|
+
* A mega menu for the section. Hovering or focusing the pill opens it; its
|
|
14
|
+
* items are also the section's sub-list in the drawer.
|
|
15
|
+
*/
|
|
16
|
+
mega?: {
|
|
17
|
+
/** The panel's link rows. */
|
|
18
|
+
items: MegaMenuItem[];
|
|
19
|
+
/** A featured card beside the rows. */
|
|
20
|
+
showcase?: MegaMenuShowcase;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The section's sub-list in the drawer, when it should differ from its
|
|
24
|
+
* menu's items: a page that needs a wide screen, say, left out of the
|
|
25
|
+
* drawer that only narrow screens see. Defaults to the menu's items.
|
|
26
|
+
*/
|
|
27
|
+
drawerItems?: {
|
|
28
|
+
href: string;
|
|
29
|
+
label: string;
|
|
30
|
+
}[];
|
|
31
|
+
/**
|
|
32
|
+
* Marks the section as the one being viewed. Left unset, the header works
|
|
33
|
+
* it out from `currentHref`: the section's own page, a page under it, or
|
|
34
|
+
* any page its menu lists.
|
|
35
|
+
*/
|
|
36
|
+
current?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** Props owned by SiteHeader itself — everything else falls through to the `<header>`. */
|
|
39
|
+
type SiteHeaderOwnProps = {
|
|
40
|
+
/**
|
|
41
|
+
* The brand at the bar's start, usually a logo linking home. Rendered as
|
|
42
|
+
* given, so it carries its own link.
|
|
43
|
+
*/
|
|
44
|
+
brand?: React.ReactNode;
|
|
45
|
+
/** The site's top-level sections, in order. */
|
|
46
|
+
sections: SiteHeaderSection[];
|
|
47
|
+
/** The path of the page being viewed, used to mark the current section, row and drawer item. */
|
|
48
|
+
currentHref?: string;
|
|
49
|
+
/** Controls at the bar's end that stay at every width, such as a search button. */
|
|
50
|
+
actions?: React.ReactNode;
|
|
51
|
+
/**
|
|
52
|
+
* Controls at the bar's end that show only while the pills fit, such as a
|
|
53
|
+
* theme toggle. Once the bar folds to its menu button they leave it; put
|
|
54
|
+
* their drawer twin in `drawerFooter`.
|
|
55
|
+
*/
|
|
56
|
+
wideActions?: React.ReactNode;
|
|
57
|
+
/** Content for the drawer's footer, such as the controls `wideActions` holds in the bar. */
|
|
58
|
+
drawerFooter?: React.ReactNode;
|
|
59
|
+
/** Whether the folded bar's drawer is open, for a host that owns that state. Pair with `onDrawerOpenChange`. */
|
|
60
|
+
drawerOpen?: boolean;
|
|
61
|
+
/** Whether the drawer starts open, when the header owns its state. */
|
|
62
|
+
defaultDrawerOpen?: boolean;
|
|
63
|
+
/** Called when the drawer opens or closes, controlled or not. */
|
|
64
|
+
onDrawerOpenChange?: (open: boolean) => void;
|
|
65
|
+
/**
|
|
66
|
+
* Where the bar folds before it has measured itself: on the first frame,
|
|
67
|
+
* which a server-rendered page shows before its script runs. Below this
|
|
68
|
+
* breakpoint (`tablet` or the wider `mid`) the bar starts folded by CSS
|
|
69
|
+
* alone, so a phone never sees the pills overflow; the measurement takes
|
|
70
|
+
* over once the script runs. Pick the step nearest where the pills stop
|
|
71
|
+
* fitting, or `never` for a bar that fits at every width.
|
|
72
|
+
*/
|
|
73
|
+
initialFold?: 'tablet' | 'mid' | 'never';
|
|
74
|
+
/**
|
|
75
|
+
* Whether the header sticks to the top of the viewport over a progressive
|
|
76
|
+
* blur. Off, it scrolls away with the page and draws no blur.
|
|
77
|
+
*/
|
|
78
|
+
sticky?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* How far, in pixels, the page scrolls before the page-surface tint fades
|
|
81
|
+
* in over the sticky header's blur.
|
|
82
|
+
*/
|
|
83
|
+
tintAfter?: number;
|
|
84
|
+
/**
|
|
85
|
+
* The component every link renders through, so a router's link (Next.js
|
|
86
|
+
* `Link`) navigates without a reload. Falls back to the nearest
|
|
87
|
+
* `LinkProvider`, then `<a>`.
|
|
88
|
+
*/
|
|
89
|
+
linkComponent?: LinkComponent;
|
|
90
|
+
/** The accessible name of the section links' `<nav>` landmark. */
|
|
91
|
+
navLabel?: string;
|
|
92
|
+
/** The accessible name of the menu button that opens the drawer. */
|
|
93
|
+
drawerOpenLabel?: string;
|
|
94
|
+
/**
|
|
95
|
+
* The drawer's title, naming the dialog. Shown at its top when there is no
|
|
96
|
+
* `brand`; with one, the brand takes its place and the title is read only
|
|
97
|
+
* by assistive tech.
|
|
98
|
+
*/
|
|
99
|
+
drawerTitle?: string;
|
|
100
|
+
/** The accessible name of the drawer's close button. */
|
|
101
|
+
drawerCloseLabel?: string;
|
|
102
|
+
/** Additional CSS classes on the `<header>`. */
|
|
103
|
+
className?: string;
|
|
104
|
+
};
|
|
105
|
+
export interface SiteHeaderProps extends SiteHeaderOwnProps, Omit<React.ComponentPropsWithRef<'header'>, keyof SiteHeaderOwnProps> {
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* A site's top bar: the brand, a pill per section with a mega menu on any
|
|
109
|
+
* section that has one, and actions at the end, sticking to the viewport
|
|
110
|
+
* over a progressive blur whose tint arrives once the page scrolls. When
|
|
111
|
+
* the pills no longer fit, measured on the bar's own width rather than the
|
|
112
|
+
* viewport's, the bar folds to a menu button that opens a drawer of the
|
|
113
|
+
* same sections. The forwarded ref and any unrecognised props target the
|
|
114
|
+
* `<header>`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function SiteHeader({ brand, sections, currentHref, actions, wideActions, drawerFooter, drawerOpen: drawerOpenProp, defaultDrawerOpen, onDrawerOpenChange, initialFold, sticky, tintAfter, linkComponent, navLabel, drawerOpenLabel, drawerTitle, drawerCloseLabel, className, ref, ...rest }: SiteHeaderProps): React.JSX.Element;
|
|
117
|
+
export {};
|