@stealthscale/component-navigation 0.1.0 → 0.3.1
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 +416 -48
- package/dist/cyclonedx/bom.json +1 -1
- package/dist/index.d.ts +900 -38
- package/dist/index.js +2250 -32
- package/dist/recipe-BN0p1O6u.js +1548 -0
- package/dist/theme.js +8 -2
- package/package.json +41 -23
- package/dist/recipe-TfPpeGkM.js +0 -150
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @stealthscale/component-navigation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
by extending the recipe. The preset under `./theme` registers the recipes with an
|
|
6
|
-
compiler.
|
|
3
|
+
React components for moving between pages and between sections of a page: `Link`, `Breadcrumb`,
|
|
4
|
+
`NavList`, `NavigationMenu`, `Pagination` and `Toc`. Each component renders through a recipe, so a
|
|
5
|
+
theme restyles it by extending the recipe. The preset under `./theme` registers the recipes with an
|
|
6
|
+
application's compiler.
|
|
7
7
|
|
|
8
|
-
Every value a theme can change
|
|
9
|
-
|
|
8
|
+
Every value a theme can change is a recipe axis, and a caller sets it as a prop. A caller changes
|
|
9
|
+
the rendered element with `as`.
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
@@ -14,19 +14,21 @@ prop and writes no style. A caller changes the element a component draws with `a
|
|
|
14
14
|
pnpm add @stealthscale/component-navigation
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
The package peers on `react
|
|
18
|
-
|
|
17
|
+
The package peers on `react`, `@stealthscale/theme`, `@stealthscale/hooks`,
|
|
18
|
+
`@stealthscale/component-actions`, `@stealthscale/component-disclosure` and
|
|
19
|
+
`@stealthscale/component-primitives`. Add the preset under `./theme` to the presets the
|
|
20
|
+
application's compiler installs.
|
|
19
21
|
|
|
20
22
|
## Link
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
Renders an anchor in the theme's link ink. `fg.link`, the visited ink, the cursor and the focus ring
|
|
25
|
+
come from the theme's `link()` fragment, so a theme sets them once for every link.
|
|
24
26
|
|
|
25
27
|
```tsx
|
|
26
28
|
import { Link } from "@stealthscale/component-navigation";
|
|
27
29
|
|
|
28
30
|
<Link href="/invoices">Invoices</Link>;
|
|
29
|
-
<Link href="/terms" variant="
|
|
31
|
+
<Link href="/terms" variant="plain">
|
|
30
32
|
Terms
|
|
31
33
|
</Link>;
|
|
32
34
|
<Link as={RouterLink} to="/invoices">
|
|
@@ -34,23 +36,34 @@ import { Link } from "@stealthscale/component-navigation";
|
|
|
34
36
|
</Link>;
|
|
35
37
|
```
|
|
36
38
|
|
|
37
|
-
| Axis | Values
|
|
38
|
-
| --------- |
|
|
39
|
-
| `variant` | `plain`, `underline`
|
|
39
|
+
| Axis | Values | Default |
|
|
40
|
+
| --------- | ------------------------------------------------------------------ | ----------- |
|
|
41
|
+
| `variant` | `plain`, `underline` | `underline` |
|
|
42
|
+
| `palette` | `primary`, `secondary`, `accent`, `neutral`, and the four statuses | `fg.link` |
|
|
43
|
+
| `inherit` | `true` | off |
|
|
40
44
|
|
|
41
|
-
Both looks underline
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
Both looks underline on hover, and `underline` also underlines at rest. Keep the default for a link
|
|
46
|
+
in running text. Without an underline the link differs from the text only by ink, and the link ink
|
|
47
|
+
measured 1.64:1 against body text on the ink theme. Use `plain` where the context marks the link: a
|
|
48
|
+
card title, a navigation row, a brand name.
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
`palette` sets the ink from the palette's `fg` role and the focus ring from its `focusRing` role.
|
|
51
|
+
Without a palette the link reads `fg.link`. The recipe lists every palette in `staticCss`, so a
|
|
52
|
+
value set through `LinkPropsProvider` has a rule. It lists `inherit` and both variants as well. A
|
|
53
|
+
router link that `createLink(Link)` makes has another name, so an application's compiler does not
|
|
54
|
+
read the values passed to it.
|
|
55
|
+
|
|
56
|
+
`inherit` makes the link take the text color of its parent, visited state included. The hover
|
|
57
|
+
underline and the focus ring still apply. A `palette` on the same link takes precedence.
|
|
58
|
+
|
|
59
|
+
The element is `a` and needs an `href`. A browser gives an anchor without an `href` no focus and no
|
|
60
|
+
Enter key, so use a button for a control that runs an action. Pass a router's link component through
|
|
61
|
+
`as` to keep its routing and take the recipe's classes.
|
|
49
62
|
|
|
50
63
|
## Breadcrumb
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
Renders an ordered list of links from the site root to the current page. Compose it as
|
|
66
|
+
`Breadcrumb.Root` around a `Breadcrumb.List` of items.
|
|
54
67
|
|
|
55
68
|
```tsx
|
|
56
69
|
import { Breadcrumb } from "@stealthscale/component-navigation";
|
|
@@ -77,31 +90,386 @@ import { Breadcrumb } from "@stealthscale/component-navigation";
|
|
|
77
90
|
| `size` | `xs`, `sm`, `md`, `lg`, `xl` | `md` |
|
|
78
91
|
| `variant` | `plain`, `underline` | `plain` |
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
`
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
| Part | Element | What it renders |
|
|
94
|
+
| ------------- | ------- | -------------------------------------------------- |
|
|
95
|
+
| `Root` | `nav` | The landmark, which receives the variants |
|
|
96
|
+
| `List` | `ol` | The crumbs in order, with `role="list"` |
|
|
97
|
+
| `Item` | `li` | One crumb |
|
|
98
|
+
| `Link` | `a` | The link of a crumb above the current page |
|
|
99
|
+
| `CurrentLink` | `span` | The current page, with `aria-current="page"` |
|
|
100
|
+
| `Separator` | `li` | The `aria-hidden` mark between two crumbs |
|
|
101
|
+
| `Ellipsis` | `li` | The labelled row that replaces the crumbs left out |
|
|
102
|
+
|
|
103
|
+
`size` sets the body text style on the root and the gap on the list, and every part inherits the
|
|
104
|
+
text size. The links are muted and darken on hover. The current page renders in the default ink. The
|
|
105
|
+
recipe has no `palette` axis, because a trail has no color of its own.
|
|
106
|
+
|
|
107
|
+
The root's `aria-label` defaults to `Breadcrumb`, because a page with a site navigation and a
|
|
108
|
+
breadcrumb has two navigation landmarks. Pass a translated label through `aria-label`.
|
|
109
|
+
|
|
110
|
+
The list sets `role="list"`, because Safari drops the list role from a list with `list-style: none`.
|
|
111
|
+
The separator is a list row between two items, with `aria-hidden` and `role="presentation"`, so a
|
|
112
|
+
screen reader counts only the crumbs. The recipe rotates the separator by 180 degrees in a
|
|
113
|
+
right-to-left document.
|
|
114
|
+
|
|
115
|
+
For a long trail, keep the first crumb and the current page and render `Breadcrumb.Ellipsis` in
|
|
116
|
+
place of the crumbs between them. Give it an `aria-label` with the number of crumbs it replaces,
|
|
117
|
+
such as `4 more steps`, so a screen reader announces the full depth.
|
|
118
|
+
|
|
119
|
+
## NavList
|
|
120
|
+
|
|
121
|
+
Renders a list of links a page is reached from: rows in a column, and branches that expand a nested
|
|
122
|
+
list.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { NavList } from "@stealthscale/component-navigation";
|
|
126
|
+
|
|
127
|
+
<nav aria-label="Main">
|
|
128
|
+
<NavList.Root iconic={collapsed} palette="primary">
|
|
129
|
+
<NavList.Item>
|
|
130
|
+
<NavList.Link aria-current="page" href="/">
|
|
131
|
+
Overview
|
|
132
|
+
</NavList.Link>
|
|
133
|
+
<NavList.Badge>3</NavList.Badge>
|
|
134
|
+
</NavList.Item>
|
|
135
|
+
<NavList.Branch defaultOpen>
|
|
136
|
+
<NavList.Trigger>
|
|
137
|
+
Settings
|
|
138
|
+
<NavList.Indicator>
|
|
139
|
+
<ChevronIcon />
|
|
140
|
+
</NavList.Indicator>
|
|
141
|
+
</NavList.Trigger>
|
|
142
|
+
<NavList.Content>
|
|
143
|
+
<NavList.Item>
|
|
144
|
+
<NavList.Link href="/settings/team">Team</NavList.Link>
|
|
145
|
+
</NavList.Item>
|
|
146
|
+
</NavList.Content>
|
|
147
|
+
</NavList.Branch>
|
|
148
|
+
</NavList.Root>
|
|
149
|
+
</nav>;
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
| Axis | Values | Default |
|
|
153
|
+
| ----------- | ------------------------------------------------------------------ | --------- |
|
|
154
|
+
| `size` | `sm`, `md`, `lg` | `md` |
|
|
155
|
+
| `variant` | `list`, `dock` | `list` |
|
|
156
|
+
| `highlight` | `tint`, `fill`, `bar` | `tint` |
|
|
157
|
+
| `palette` | `primary`, `secondary`, `accent`, `neutral`, and the four statuses | inherited |
|
|
158
|
+
| `radius` | `l1`, `l2`, `l3`, `full` | `l2` |
|
|
159
|
+
| `guide` | `solid`, `dashed`, `dotted`, `none` | `solid` |
|
|
160
|
+
| `iconic` | `true` | off |
|
|
161
|
+
| `reveal` | `always`, `hover` | `always` |
|
|
162
|
+
| `effect` | `glow` | none |
|
|
163
|
+
|
|
164
|
+
| Part | Element | What it renders |
|
|
165
|
+
| --------------- | -------- | -------------------------------------------------- |
|
|
166
|
+
| `Root` | `ul` | The list, which receives the variants |
|
|
167
|
+
| `Item` | `li` | One row |
|
|
168
|
+
| `Link` | `a` | A row's link |
|
|
169
|
+
| `Mark` | `span` | A leading mark that is not an icon, an avatar |
|
|
170
|
+
| `Action` | `button` | A control at the end of a row |
|
|
171
|
+
| `Badge` | `span` | A count at the end of a row |
|
|
172
|
+
| `Branch` | `li` | A row that expands, and its state |
|
|
173
|
+
| `Trigger` | `button` | The row that expands and collapses |
|
|
174
|
+
| `Indicator` | `span` | The icon that rotates as a branch opens |
|
|
175
|
+
| `Content` | `ul` | The nested list of a branch |
|
|
176
|
+
| `Skeleton` | `li` | A placeholder row while the list loads |
|
|
177
|
+
| `PropsProvider` | none | The default `iconic` and `size` of the lists below |
|
|
178
|
+
|
|
179
|
+
Set `aria-current="page"` on the link to the current page. A screen reader announces the attribute
|
|
180
|
+
and `highlight` styles it, so the two cannot disagree. A row is `fg.muted`, and the current row is
|
|
181
|
+
`fg` and semibold. A row's label, inline inset and gap are two sizes below the row's size: 12.6px,
|
|
182
|
+
8px and 4px at `md`. The rows of an `md` list are 4px apart and those of an `lg` list 6px. An `sm`
|
|
183
|
+
list sets its 24px rows with no gap between them, for a dense list such as a catalogue's.
|
|
184
|
+
|
|
185
|
+
The list does not set a landmark, because a page renders more than one list. Render a `nav` with an
|
|
186
|
+
`aria-label` around the list. Do not pass `as="nav"` to the root: the rows are `li` elements, and a
|
|
187
|
+
`nav` holding them directly is not read as a list.
|
|
188
|
+
|
|
189
|
+
`palette` sets the palette the highlight, the hover fill and the branch rows read. Without it the
|
|
190
|
+
list inherits the palette of the surrounding element. `effect="glow"` adds a glow around the current
|
|
191
|
+
row.
|
|
192
|
+
|
|
193
|
+
`NavList.Branch` takes `open`, `defaultOpen`, `onOpenChange` and `id`. Pass `open` to keep the
|
|
194
|
+
branch that contains the current page open across navigations. The machine sets `aria-expanded` and
|
|
195
|
+
`aria-controls` on the trigger, so do not set them yourself.
|
|
196
|
+
|
|
197
|
+
`NavList.Action` is a `button` with `type="button"`. Pass the icon as its child and an `aria-label`
|
|
198
|
+
that includes the row, such as `Rename Invoices`. A screen reader has no other text that ties the
|
|
199
|
+
control to its row.
|
|
200
|
+
|
|
201
|
+
Set `iconic` for a list collapsed to a rail. Each row becomes a square that contains its icon. The
|
|
202
|
+
counts, the controls, the indicators and the nested rows are hidden. The text stays in the
|
|
203
|
+
accessibility tree, so a screen reader still reads the name of every row. The rail centres its
|
|
204
|
+
squares, so render it in a container as wide as the rail. Pass `iconic` from the component that
|
|
205
|
+
collapses. The list does not measure anything itself.
|
|
206
|
+
|
|
207
|
+
A row without an icon leads with `NavList.Mark`, such as an avatar with the row's initials. The mark
|
|
208
|
+
is `aria-hidden`, and an iconic list keeps it visible in place of the icon.
|
|
209
|
+
|
|
210
|
+
`NavList.PropsProvider` sets `iconic` and `size` once for every list below it, and a list's own
|
|
211
|
+
props apply over it. The screen package's sidebar renders one. On an iconic list, a link with
|
|
212
|
+
`tooltip` shows those words in a tooltip beside its icon on hover and on focus. The tooltip is
|
|
213
|
+
portalled to the document, because a scrolling column clips what overflows it.
|
|
214
|
+
|
|
215
|
+
Inside a filter scope from `@stealthscale/hooks`, such as a sidebar's search, an `Item` or a
|
|
216
|
+
`Branch` whose words do not contain the query is hidden, and a branch is open while the scope has a
|
|
217
|
+
query. While the query is active, a press on a branch's trigger leaves the branch open. A cleared
|
|
218
|
+
query returns each branch to the state it had before the query, so the branch of the current page is
|
|
219
|
+
open again.
|
|
220
|
+
|
|
221
|
+
`reveal="hover"` hides each control until its row is hovered. The control on the current row stays
|
|
222
|
+
visible. The others also appear while any element in their row has focus and under a coarse pointer,
|
|
223
|
+
so a keyboard and a touch screen both reach them.
|
|
224
|
+
|
|
225
|
+
Set `variant="dock"` for a few links across the foot of a screen. Each link takes an equal share of
|
|
226
|
+
the row and shows its icon over its label. The bottom padding includes the safe area a device
|
|
227
|
+
reserves for a home indicator. Do not put a branch in a dock, because it has no room to open.
|
|
228
|
+
|
|
229
|
+
The list sizes an `svg` that is a direct child of a link or a trigger. In a list, the icon takes the
|
|
230
|
+
icon size one smaller than the row's size. In a dock, it takes `icon.lg`. Pass the icon without a
|
|
231
|
+
size.
|
|
232
|
+
|
|
233
|
+
Nested rows use the same `Item` and `Link`. `Content` sets `fg.subtle`, and the nested rows inherit
|
|
234
|
+
it. A line runs down the start of a nested list. When the trigger leads with an icon, the line is
|
|
235
|
+
aligned with the icon's centre. `guide` sets the line's style, and `none` removes it.
|
|
236
|
+
|
|
237
|
+
Set `aria-busy` on the root while `NavList.Skeleton` rows render in place of the loading rows, and
|
|
238
|
+
put the feedback package's `Skeleton` inside each one.
|
|
239
|
+
|
|
240
|
+
## NavigationMenu
|
|
241
|
+
|
|
242
|
+
Renders a site's navigation landmark: a bar of links and of buttons that open panels of links.
|
|
243
|
+
Compose it as `NavigationMenu.Root` around a `NavigationMenu.List` of items. An item contains a
|
|
244
|
+
trigger and its panel, or a link.
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
import { NavigationMenu } from "@stealthscale/component-navigation";
|
|
248
|
+
|
|
249
|
+
<NavigationMenu.Root aria-label="Site">
|
|
250
|
+
<NavigationMenu.List>
|
|
251
|
+
<NavigationMenu.Item value="products">
|
|
252
|
+
<NavigationMenu.Trigger>
|
|
253
|
+
Products
|
|
254
|
+
<ChevronDownIcon />
|
|
255
|
+
</NavigationMenu.Trigger>
|
|
256
|
+
<NavigationMenu.Content>
|
|
257
|
+
<NavigationMenu.Link href="/payments">
|
|
258
|
+
<CreditCardIcon />
|
|
259
|
+
<Strong weight="medium">Payments</Strong>
|
|
260
|
+
<Span tone="muted">Accept cards and bank debits in one checkout.</Span>
|
|
261
|
+
</NavigationMenu.Link>
|
|
262
|
+
</NavigationMenu.Content>
|
|
263
|
+
</NavigationMenu.Item>
|
|
264
|
+
<NavigationMenu.Item value="pricing">
|
|
265
|
+
<NavigationMenu.Link current href="/pricing">
|
|
266
|
+
Pricing
|
|
267
|
+
</NavigationMenu.Link>
|
|
268
|
+
</NavigationMenu.Item>
|
|
269
|
+
<NavigationMenu.Indicator />
|
|
270
|
+
</NavigationMenu.List>
|
|
271
|
+
<NavigationMenu.ViewportPositioner align="start">
|
|
272
|
+
<NavigationMenu.Viewport />
|
|
273
|
+
</NavigationMenu.ViewportPositioner>
|
|
274
|
+
</NavigationMenu.Root>;
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
| Axis | Values | Default |
|
|
278
|
+
| --------- | ------------------------------------------------------------------ | --------- |
|
|
279
|
+
| `size` | `sm`, `md`, `lg` | `md` |
|
|
280
|
+
| `palette` | `primary`, `secondary`, `accent`, `neutral`, and the four statuses | `primary` |
|
|
281
|
+
|
|
282
|
+
| Part | Element | What it renders |
|
|
283
|
+
| -------------------- | -------- | ------------------------------------------------------- |
|
|
284
|
+
| `Root` | `nav` | The landmark, which starts the machine |
|
|
285
|
+
| `List` | `ul` | The bar of items |
|
|
286
|
+
| `Item` | `li` | A trigger and its panel, or a link |
|
|
287
|
+
| `Trigger` | `button` | The button that opens its item's panel |
|
|
288
|
+
| `Content` | `div` | The panel of an item, `hidden` while the item is closed |
|
|
289
|
+
| `Link` | `a` | A link in the bar or in a panel |
|
|
290
|
+
| `Indicator` | `li` | The `aria-hidden` bar under the open trigger |
|
|
291
|
+
| `ViewportPositioner` | `div` | The box that places the viewport under the bar |
|
|
292
|
+
| `Viewport` | `div` | The surface every panel shows in |
|
|
293
|
+
|
|
294
|
+
Each trigger follows the WAI-ARIA disclosure navigation pattern: a `button` with `aria-expanded` and
|
|
295
|
+
`aria-controls`, and no `menu` role. A mouse over a trigger opens its panel after `openDelay`, 200ms
|
|
296
|
+
by default, and a press toggles it. A mouse that leaves the trigger and the panel closes it after
|
|
297
|
+
`closeDelay`, 300ms by default. The arrow keys move along the bar, Home and End move to its ends,
|
|
298
|
+
and ArrowDown moves into an open panel, or ArrowRight in a vertical menu. The arrow keys move
|
|
299
|
+
between a panel's links. Escape and a press outside close the panel and return focus to its trigger.
|
|
300
|
+
|
|
301
|
+
`NavigationMenu.Root` takes the machine's options: `value` or `defaultValue`, `onValueChange`,
|
|
302
|
+
`openDelay`, `closeDelay`, `orientation`, `dir`, `disableHoverTrigger`, `disableClickTrigger` and
|
|
303
|
+
`disablePointerLeaveClose`. Name the landmark with `aria-label`. The machine's `translations` are
|
|
304
|
+
not accepted. `disablePointerLeaveClose` keeps a panel open when the pointer leaves the panel or the
|
|
305
|
+
viewport. The root sets the machine's value to an item open at mount after the first commit, because
|
|
306
|
+
the machine measures a trigger and tracks Escape and presses outside only when its value changes.
|
|
307
|
+
`onValueChange` receives each change the menu makes, and never the value the menu mounts with.
|
|
308
|
+
|
|
309
|
+
Without a viewport, each panel opens one gap under its trigger. A panel is as wide as its content,
|
|
310
|
+
at most `sizes.2xl` and 20px narrower than the window. With `ViewportPositioner` and `Viewport`,
|
|
311
|
+
every panel shows inside the viewport, which moves to the open trigger and resizes to the panel. The
|
|
312
|
+
panel that closes fades over the panel that opens. `align` on the positioner lines the viewport up
|
|
313
|
+
with the open trigger: `start` joins their start edges, `center` their centres and `end` their end
|
|
314
|
+
edges. The default is `center`. The machine keeps the viewport 10px inside the window. Render the
|
|
315
|
+
viewport on the first render, because the machine looks for it once, as it starts.
|
|
316
|
+
|
|
317
|
+
A closed panel is in the document with `hidden`, so a crawler reads its links. While the menu
|
|
318
|
+
renders a viewport, the machine renders a visually hidden proxy after each open trigger, and an
|
|
319
|
+
element whose `aria-owns` references the panel. The proxy is focusable and `aria-hidden`. Tab on the
|
|
320
|
+
trigger focuses the proxy, which moves focus to the panel's first link, and Tab on the panel's last
|
|
321
|
+
link moves focus past the proxy to the next item. axe reports `aria-hidden-focus` for the proxy
|
|
322
|
+
while a panel is open.
|
|
323
|
+
|
|
324
|
+
`NavigationMenu.Link` takes `current`, which sets `aria-current="page"`, and `closeOnClick`, true by
|
|
325
|
+
default. `onSelect` runs on a press before the menu closes, and a call to `preventDefault` on its
|
|
326
|
+
event keeps the menu open. A press with the meta key keeps the menu open, because the link opens in
|
|
327
|
+
another tab. Pass a router's link through `as`.
|
|
328
|
+
|
|
329
|
+
A panel link places a leading icon in a column of its own and every other child in the column beside
|
|
330
|
+
it, so a title and a description start at one edge. Pass `Strong` and `Span tone="muted"` from the
|
|
331
|
+
typography package for the two. The recipe sizes the icon from the icon scale at the menu's size. A
|
|
332
|
+
trigger's trailing icon is one size smaller and turns over while the panel is open.
|
|
333
|
+
|
|
334
|
+
`NavigationMenu.Indicator` is a bar along the open trigger's bottom edge. In a list with an
|
|
335
|
+
indicator the items are not positioned, because the machine measures a trigger against its
|
|
336
|
+
positioned ancestor and the indicator reads that place against the list. A panel in place then opens
|
|
337
|
+
under the trigger's measured place, and a panel that closes while another opens hides at once.
|
|
338
|
+
`palette` colors the indicator and a link to the current page. Under forced colors the indicator
|
|
339
|
+
paints `CanvasText` and an open trigger fills with `Highlight`.
|
|
340
|
+
|
|
341
|
+
The bar wraps onto a second row when its items do not fit. The recipe offers no `Arrow` part,
|
|
342
|
+
because the indicator is a bar, and no item indicator, because the trigger turns its own trailing
|
|
343
|
+
icon.
|
|
344
|
+
|
|
345
|
+
## Pagination
|
|
346
|
+
|
|
347
|
+
Renders a navigation landmark of page buttons between the buttons that move a page back and forward.
|
|
348
|
+
Compose it as `Pagination.Root` around the triggers and `Pagination.Items`, or around the triggers
|
|
349
|
+
and `Pagination.PageText` for a row without page buttons.
|
|
350
|
+
|
|
351
|
+
```tsx
|
|
352
|
+
import { Pagination } from "@stealthscale/component-navigation";
|
|
353
|
+
|
|
354
|
+
<Pagination.Root count={240} onPageChange={({ page }) => setPage(page)} page={page} pageSize={10}>
|
|
355
|
+
<Pagination.PrevTrigger>
|
|
356
|
+
<ChevronLeftIcon />
|
|
357
|
+
</Pagination.PrevTrigger>
|
|
358
|
+
<Pagination.Items />
|
|
359
|
+
<Pagination.NextTrigger>
|
|
360
|
+
<ChevronRightIcon />
|
|
361
|
+
</Pagination.NextTrigger>
|
|
362
|
+
</Pagination.Root>;
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
| Axis | Values | Default |
|
|
366
|
+
| ------ | ---------------- | ------- |
|
|
367
|
+
| `size` | `sm`, `md`, `lg` | `md` |
|
|
368
|
+
|
|
369
|
+
| Part | Element | What it renders |
|
|
370
|
+
| -------------- | --------------- | --------------------------------------------------------- |
|
|
371
|
+
| `Root` | `nav` | The landmark, which starts the machine |
|
|
372
|
+
| `Items` | none | A page per page shown, a mark per run left out, a summary |
|
|
373
|
+
| `Item` | `button` or `a` | One page |
|
|
374
|
+
| `Ellipsis` | `span` | The `aria-hidden` mark for a run of pages left out |
|
|
375
|
+
| `PrevTrigger` | `button` or `a` | The button that moves one page back |
|
|
376
|
+
| `NextTrigger` | `button` or `a` | The button that moves one page forward |
|
|
377
|
+
| `FirstTrigger` | `button` or `a` | The button that moves to the first page |
|
|
378
|
+
| `LastTrigger` | `button` or `a` | The button that moves to the last page |
|
|
379
|
+
| `PageText` | `output` | The current page in words |
|
|
380
|
+
|
|
381
|
+
Every page and trigger is the actions package's square `Button`. The root passes `size`, `variant`
|
|
382
|
+
and `palette` to every button inside it. `variant` takes any look of the button and defaults to
|
|
383
|
+
`ghost`, and `palette` takes any palette. The current page takes `aria-current="page"`, which every
|
|
384
|
+
look of the button marks as on.
|
|
385
|
+
|
|
386
|
+
`Pagination.Root` takes the machine's options: `count`, `page` or `defaultPage`, `pageSize` or
|
|
387
|
+
`defaultPageSize`, `siblingCount` and `boundaryCount` (both 1 by default), `onPageChange`,
|
|
388
|
+
`onPageSizeChange`, `type` and `getPageUrl`. Its `aria-label` defaults to `Pagination`. The
|
|
389
|
+
machine's `translations` are not accepted, because every word is a prop with an English default. A
|
|
390
|
+
page is named `Page N` unless `Items` takes a `label` function, and each trigger takes `label` in
|
|
391
|
+
place of `Previous page`, `Next page`, `First page` or `Last page`. Pass each trigger's glyph as its
|
|
392
|
+
child.
|
|
393
|
+
|
|
394
|
+
A trigger at an end sets `aria-disabled` in place of `disabled`, so a press that moves to the last
|
|
395
|
+
page leaves focus on the trigger. The machine ignores a press on a trigger at an end.
|
|
396
|
+
|
|
397
|
+
While its children overflow its row at their natural width, the root sets `data-crowded`. The recipe
|
|
398
|
+
then hides the pages and the marks and shows a summary of the current page between the triggers,
|
|
399
|
+
such as `Page 12 of 24`. `summary` on `Items` sets its format. The summary is an `output`, so a
|
|
400
|
+
screen reader reads the new page after a press while the summary shows. The root takes no `ref`,
|
|
401
|
+
because it attaches its own.
|
|
402
|
+
|
|
403
|
+
`PageText` shows the current page in the format `format` names: `compact` renders `Page 12 of 24`,
|
|
404
|
+
`short` renders `12 / 24`, and `long` renders the range of items, `111–120 of 240`. A count of zero
|
|
405
|
+
renders `Page 0 of 0`, `0 / 0` and `0–0 of 0`. A function receives `count`, `page`, `pageRange` and
|
|
406
|
+
`totalPages` and returns the words in any language. `summary` takes the same values.
|
|
407
|
+
|
|
408
|
+
With `type="link"`, every page and trigger is a link to the address `getPageUrl` returns, so a
|
|
409
|
+
reader can open a page in a new tab. A press follows the link and leaves the page unchanged: pass
|
|
410
|
+
`page` from the address, as an application reads it from its router. A trigger at an end has no
|
|
411
|
+
address, so it takes the link role and a tab stop and keeps focus.
|
|
412
|
+
|
|
413
|
+
## Toc
|
|
414
|
+
|
|
415
|
+
Renders a list of links to the headings on a page and marks the headings in view. Compose it as
|
|
416
|
+
`Toc.Root` around a `Toc.Title` and a `Toc.List` whose first child is `Toc.Indicator`.
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
import { Toc } from "@stealthscale/component-navigation";
|
|
420
|
+
|
|
421
|
+
const items = [
|
|
422
|
+
{ depth: 2, value: "install" },
|
|
423
|
+
{ depth: 3, value: "peers" },
|
|
424
|
+
{ depth: 2, value: "usage" },
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
<Toc.Root items={items} placement="aside" rootMargin="0px">
|
|
428
|
+
<Toc.Title>On this page</Toc.Title>
|
|
429
|
+
<Toc.List>
|
|
430
|
+
<Toc.Indicator />
|
|
431
|
+
{items.map((item) => (
|
|
432
|
+
<Toc.Item item={item} key={item.value}>
|
|
433
|
+
<Toc.Link href={`#${item.value}`} item={item}>
|
|
434
|
+
{titles[item.value]}
|
|
435
|
+
</Toc.Link>
|
|
436
|
+
</Toc.Item>
|
|
437
|
+
))}
|
|
438
|
+
</Toc.List>
|
|
439
|
+
</Toc.Root>;
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
| Axis | Values | Default |
|
|
443
|
+
| ----------- | ------------------------------------------------------------------ | --------- |
|
|
444
|
+
| `size` | `sm`, `md`, `lg` | `md` |
|
|
445
|
+
| `placement` | `inline`, `aside` | `inline` |
|
|
446
|
+
| `palette` | `primary`, `secondary`, `accent`, `neutral`, and the four statuses | `primary` |
|
|
447
|
+
|
|
448
|
+
| Part | Element | What it renders |
|
|
449
|
+
| ----------- | ------- | ---------------------------------------------------- |
|
|
450
|
+
| `Root` | `nav` | The landmark, named by the title |
|
|
451
|
+
| `Title` | `div` | The text the landmark's `aria-labelledby` references |
|
|
452
|
+
| `List` | `ul` | One row per heading |
|
|
453
|
+
| `Item` | `li` | One row, indented by its heading's depth |
|
|
454
|
+
| `Link` | `a` | The link to one heading |
|
|
455
|
+
| `Indicator` | `li` | The bar next to the rows whose heading is in view |
|
|
456
|
+
|
|
457
|
+
Each item names a heading by `value`, the element ID of the heading, and `depth`, the heading level.
|
|
458
|
+
The root observes those elements with an `IntersectionObserver` and sets `aria-current="location"`
|
|
459
|
+
on the links of the headings in view. A click scrolls the page to the heading. When the page scrolls
|
|
460
|
+
inside an element, pass `scrollEl` so the root observes and scrolls that element.
|
|
461
|
+
|
|
462
|
+
`Toc.Root` takes the machine options next to `items`: `rootMargin` and `threshold` for the observer
|
|
463
|
+
band, `autoScroll` to keep the active row visible in a scrolling list, `scrollBehavior`,
|
|
464
|
+
`onActiveChange`, and `activeIds` or `defaultActiveIds`. The machine's default band excludes the
|
|
465
|
+
bottom of the viewport, so a short last section is never marked. Pass `rootMargin="0px"` for a page
|
|
466
|
+
of sections.
|
|
467
|
+
|
|
468
|
+
`placement="aside"` makes the root sticky below the application shell's pinned bars and caps it at
|
|
469
|
+
the viewport height. A longer list scrolls in the primitives package's scroll area inside the root,
|
|
470
|
+
whose viewport takes no tab stop. The root is at least 11rem wide and never wider than its
|
|
471
|
+
container. `palette` sets the indicator and the focus ring. Under forced colors the indicator paints
|
|
472
|
+
`CanvasText`.
|
|
105
473
|
|
|
106
474
|
## Licence
|
|
107
475
|
|