@stealthscale/component-screen 0.0.1 → 0.2.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 +211 -54
- package/dist/cyclonedx/bom.json +1 -1
- package/dist/index.d.ts +327 -444
- package/dist/index.js +2246 -1905
- package/dist/recipe-DRMsitrw.js +283 -0
- package/dist/{recipe-CRJrRAI8.js → recipe-sZAQY6Kq.js} +467 -456
- package/dist/root-DDwipXru.d.ts +327 -0
- package/dist/root-qEh4u7y5.js +1182 -0
- package/dist/shell.d.ts +42 -0
- package/dist/shell.js +166 -0
- package/dist/theme.js +4 -3
- package/package.json +57 -41
package/README.md
CHANGED
|
@@ -5,7 +5,8 @@ page, the sidebar of destinations, the switcher at the head of a sidebar, the to
|
|
|
5
5
|
the action bar over a selection, the splitter between panels a person resizes and the floating panel
|
|
6
6
|
a person moves over the page. Every component takes its styles from a recipe, so a theme restyles
|
|
7
7
|
all nine by extending the recipes. The preset under `./theme` registers the recipes with an
|
|
8
|
-
application's compiler.
|
|
8
|
+
application's compiler. The entry under `./shell` adds the switchers of the locale and the theme,
|
|
9
|
+
which render the switcher's recipe.
|
|
9
10
|
|
|
10
11
|
Every value a theme can change is an axis of a recipe, so a caller sets it as a prop and writes no
|
|
11
12
|
style. A caller changes the element a component renders with `as`.
|
|
@@ -16,10 +17,12 @@ style. A caller changes the element a component renders with `as`.
|
|
|
16
17
|
pnpm add @stealthscale/component-screen
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
The package peers on `react`, `@stealthscale/theme`, `@stealthscale/hooks`,
|
|
20
|
+
The package peers on `react`, `react-dom`, `@stealthscale/theme`, `@stealthscale/hooks`,
|
|
20
21
|
`@stealthscale/provider-viewport` and the component packages it renders: `a11y`, `actions`, `data`,
|
|
21
22
|
`disclosure`, `forms`, `layout`, `modals`, `navigation`, `primitives` and `typography`. An
|
|
22
|
-
application lists the preset under `./theme` among the presets its compiler installs.
|
|
23
|
+
application lists the preset under `./theme` among the presets its compiler installs. The `./shell`
|
|
24
|
+
entry also imports `@stealthscale/provider-locale` and `@stealthscale/provider-shell`. Both peers
|
|
25
|
+
are optional, so an application that imports only the main entry installs neither.
|
|
23
26
|
|
|
24
27
|
## Folding
|
|
25
28
|
|
|
@@ -29,18 +32,25 @@ open sidebar folds on the width the sidebar leaves it, and a caller does not wri
|
|
|
29
32
|
media query. Before the element is laid out, and where the browser does not report a size, the
|
|
30
33
|
viewport's width decides, so a phone never lays out wide first.
|
|
31
34
|
|
|
35
|
+
A toolbar also folds while its controls need more room than its row has. It measures the row while
|
|
36
|
+
no control is folded, and tries the unfolded row again when the row or the window changes width.
|
|
37
|
+
While the folded controls still need more room, the toolbar moves its secondary actions and links
|
|
38
|
+
and its switchers into its menu, the control nearest the row's end first, until the rest fit. The
|
|
39
|
+
row adds up the boxes of its controls, so a touch target larger than its control does not fold it.
|
|
40
|
+
|
|
32
41
|
`Toolbar.Action`, `Page.Action` and `Section.Action` fold by priority. An action states `priority`,
|
|
33
42
|
or the action's props imply one:
|
|
34
43
|
|
|
35
|
-
| Props | Priority | On a narrow row
|
|
36
|
-
| ---------------------------- | ----------- |
|
|
37
|
-
| `primary`, no `icon` | `primary` | Unchanged
|
|
38
|
-
| `icon` | `secondary` | Icon alone,
|
|
39
|
-
| neither `primary` nor `icon` | `tertiary` | Removed from the row, a row in its menu
|
|
44
|
+
| Props | Priority | On a narrow row |
|
|
45
|
+
| ---------------------------- | ----------- | -------------------------------------------------------- |
|
|
46
|
+
| `primary`, no `icon` | `primary` | Unchanged |
|
|
47
|
+
| `icon` | `secondary` | Icon alone. In a toolbar without room, a row in its menu |
|
|
48
|
+
| neither `primary` nor `icon` | `tertiary` | Removed from the row, a row in its menu |
|
|
40
49
|
|
|
41
50
|
- A secondary action keeps its label for screen readers.
|
|
42
51
|
- A tertiary action leaves the row and the tab order. The row renders a menu with one row per folded
|
|
43
|
-
action.
|
|
52
|
+
action. When one of those rows leads with an icon, the menu pads the others to the icon's gutter,
|
|
53
|
+
so the words of every row start at one line.
|
|
44
54
|
- `more` names the menu's trigger, `More actions` by default. `moreIcon` replaces the trigger's
|
|
45
55
|
words with a mark.
|
|
46
56
|
- A folded link keeps its `href` in its menu row. A menu row calls the action's `onClick`.
|
|
@@ -100,7 +110,8 @@ import { ActionBar, Toolbar } from "@stealthscale/component-screen";
|
|
|
100
110
|
toolbar renders its menu of folded actions as its last control, beside the actions. Give the main
|
|
101
111
|
action `priority="primary"` to keep its words in a narrow window.
|
|
102
112
|
- The bar is as wide as its content, at least `sizes.2xl`, 672px, and at most the window inside the
|
|
103
|
-
inset. Its toolbar folds in a narrow window and
|
|
113
|
+
inset. Its toolbar folds in a narrow window, and while its controls need more room than the window
|
|
114
|
+
leaves the bar.
|
|
104
115
|
- The positioner is fixed above the safe area at the `banner` layer and takes no pointer events, so
|
|
105
116
|
the page beside the bar remains pressable. `placement` sets the bar at the start, in the middle or
|
|
106
117
|
at the end of the bottom edge, and start and end follow the writing direction.
|
|
@@ -125,8 +136,8 @@ The bar does not offer these:
|
|
|
125
136
|
|
|
126
137
|
## AppShell
|
|
127
138
|
|
|
128
|
-
Lays out an application: bars across the top and the bottom, a body between them with
|
|
129
|
-
either side of the main region, and a status bar at the foot.
|
|
139
|
+
Lays out an application: bars across the top and the bottom, a body between them with panels on
|
|
140
|
+
either side of the main region or of a pane, and a status bar at the foot.
|
|
130
141
|
|
|
131
142
|
```tsx
|
|
132
143
|
import { AppShell, Sidebar } from "@stealthscale/component-screen";
|
|
@@ -154,10 +165,13 @@ import { AppShell, Sidebar } from "@stealthscale/component-screen";
|
|
|
154
165
|
|
|
155
166
|
| Axis | Values | Default |
|
|
156
167
|
| --------- | ---------------------------- | ------- |
|
|
168
|
+
| `content` | `fluid`, `constrained` | `fluid` |
|
|
157
169
|
| `divided` | `true`, `false` | `true` |
|
|
158
170
|
| `scroll` | `page`, `window` | `page` |
|
|
159
171
|
| `variant` | `floating`, `inset`, `plain` | `plain` |
|
|
160
172
|
|
|
173
|
+
- `content="constrained"` caps the page in the main region at the theme's wide page measure,
|
|
174
|
+
`sizes.page.wide`, and centres it. `fluid` gives the page the whole region.
|
|
161
175
|
- `divided` renders a hairline on the inner edge of each bar and each panel. A sidebar inside a
|
|
162
176
|
panel sets its ground and no line.
|
|
163
177
|
- `scroll="page"` sets the shell to the window's height and scrolls the main region. While a panel
|
|
@@ -183,19 +197,20 @@ The shell has no `palette` and no `effect` axis, because its parts are container
|
|
|
183
197
|
|
|
184
198
|
### Parts
|
|
185
199
|
|
|
186
|
-
| Part | Element
|
|
187
|
-
| ------------------ |
|
|
188
|
-
| `AppShell.Root` | `div`
|
|
189
|
-
| `AppShell.Header` | `header`
|
|
190
|
-
| `AppShell.Body` | `div`
|
|
191
|
-
| `AppShell.Navbar` | `div`
|
|
192
|
-
| `AppShell.Rail` | `button` | none |
|
|
193
|
-
| `AppShell.
|
|
194
|
-
| `AppShell.
|
|
195
|
-
| `AppShell.
|
|
196
|
-
| `AppShell.
|
|
197
|
-
| `AppShell.
|
|
198
|
-
| `AppShell.
|
|
200
|
+
| Part | Element | Landmark |
|
|
201
|
+
| ------------------ | --------------------------------------------------------------------- | --------------- |
|
|
202
|
+
| `AppShell.Root` | `div` | none |
|
|
203
|
+
| `AppShell.Header` | `header` | `banner` |
|
|
204
|
+
| `AppShell.Body` | `div` | none |
|
|
205
|
+
| `AppShell.Navbar` | `div` | none |
|
|
206
|
+
| `AppShell.Rail` | `button`, or a `div` in the `separator` role beside a resizable panel | none |
|
|
207
|
+
| `AppShell.Pane` | `div` | none |
|
|
208
|
+
| `AppShell.Main` | `main` | `main` |
|
|
209
|
+
| `AppShell.Aside` | `aside` | `complementary` |
|
|
210
|
+
| `AppShell.Section` | `div` | none |
|
|
211
|
+
| `AppShell.Footer` | `footer` | `contentinfo` |
|
|
212
|
+
| `AppShell.Status` | `section` | `region` |
|
|
213
|
+
| `AppShell.Trigger` | `button` | none |
|
|
199
214
|
|
|
200
215
|
`AppShell.Navbar` has no landmark, because a sidebar inside it renders its own navigation landmarks.
|
|
201
216
|
Name `AppShell.Aside` with `aria-label` when an application renders more than one.
|
|
@@ -203,8 +218,10 @@ Name `AppShell.Aside` with `aria-label` when an application renders more than on
|
|
|
203
218
|
Render `AppShell.Status` after the footer, for the state of the application: the connection, a sync,
|
|
204
219
|
work that runs in the background. It lays its entries out in a row that wraps, padded by
|
|
205
220
|
`spacing.gap.xs` above and below and `spacing.gap.md` at the sides, with a hairline above it under
|
|
206
|
-
`divided`.
|
|
207
|
-
|
|
221
|
+
`divided`. The bar is a region named by `label`, `Status` by default, so its entries are inside a
|
|
222
|
+
landmark. It is not a live region, so an entry that reports a change renders its own `output`. Under
|
|
223
|
+
`scroll="window"` the bar is at the end of the document. The bar takes no room while every entry in
|
|
224
|
+
it renders nothing.
|
|
208
225
|
|
|
209
226
|
`AppShell.Section` is a padded band in a panel or in the main region. `grows` gives it the room its
|
|
210
227
|
siblings leave, and `scrolls` makes it a scroll area, so the bands around it remain in place. A band
|
|
@@ -218,46 +235,101 @@ height exactly and scrolls its own list.
|
|
|
218
235
|
|
|
219
236
|
`AppShell.Navbar` and `AppShell.Aside` are the same panel on the two sides.
|
|
220
237
|
|
|
221
|
-
| Prop | Values
|
|
222
|
-
| -------------- |
|
|
223
|
-
| `collapse` | `hide`, `icons`
|
|
224
|
-
| `folds` | `over`, `under`
|
|
225
|
-
| `foldsBelow` | a breakpoint, `never`
|
|
226
|
-
| `width` | a CSS length
|
|
227
|
-
| `railWidth` | a CSS length
|
|
228
|
-
| `defaultOpen` | `true`, `false`
|
|
229
|
-
| `open` | `true`, `false`
|
|
230
|
-
| `onOpenChange` | a function
|
|
231
|
-
| `name` | a string
|
|
232
|
-
| `shortcut` | a key
|
|
238
|
+
| Prop | Values | Default |
|
|
239
|
+
| -------------- | ----------------------- | ---------------------------------------------- |
|
|
240
|
+
| `collapse` | `hide`, `icons` | `hide` |
|
|
241
|
+
| `folds` | `over`, `under`, `hide` | `over` |
|
|
242
|
+
| `foldsBelow` | a breakpoint, `never` | `md` on the start side, `lg` on the end side |
|
|
243
|
+
| `width` | a CSS length | `sizes.sidebar` or `sizes.aside` |
|
|
244
|
+
| `railWidth` | a CSS length | `sizes.rail` |
|
|
245
|
+
| `defaultOpen` | `true`, `false` | `true` |
|
|
246
|
+
| `open` | `true`, `false` | uncontrolled |
|
|
247
|
+
| `onOpenChange` | a function | none |
|
|
248
|
+
| `name` | a string | `navbar` on the start side, `aside` on the end |
|
|
249
|
+
| `shortcut` | a key | none |
|
|
250
|
+
| `resizable` | `true`, `false` | `false` |
|
|
251
|
+
| `defaultSize` | pixels | `width` |
|
|
252
|
+
| `size` | pixels | uncontrolled |
|
|
253
|
+
| `onSizeChange` | a function | none |
|
|
254
|
+
| `minSize` | pixels | 192 |
|
|
255
|
+
| `maxSize` | pixels | half the window |
|
|
233
256
|
|
|
234
257
|
- `collapse` sets what closing the panel beside the page leaves: `hide` hides it, and `icons` leaves
|
|
235
258
|
a rail of `railWidth`. A sidebar inside the panel renders as a rail on its own.
|
|
236
259
|
- `folds` sets where the panel goes when the shell is narrower than `foldsBelow`: `over` lays it
|
|
237
|
-
over the page behind a backdrop,
|
|
238
|
-
|
|
260
|
+
over the page behind a backdrop, `under` drops it under the main region as a block that is always
|
|
261
|
+
shown, and `hide` takes it out of the shell until the shell is wide enough again, such as a rail
|
|
262
|
+
of apps whose destinations a bar at the foot of the shell lists on a phone. A panel out of the
|
|
263
|
+
shell is hidden and inert. Its trigger and its rail do not render, and its shortcut does not
|
|
264
|
+
toggle it. `foldsBelow="never"` keeps the panel in the body at every width.
|
|
239
265
|
- `shortcut` toggles the panel with Control or Command held: `shortcut="b"` for ⌘B and Ctrl+B.
|
|
266
|
+
- `resizable` lets a person drag the panel's rail, or focus it and press the arrow keys, to set the
|
|
267
|
+
panel's width. The width a person sets replaces `width`. The panel is at most half the window wide
|
|
268
|
+
unless `maxSize` states another limit, so a width kept from a wide screen fits a narrow one.
|
|
269
|
+
`size` and `onSizeChange` control the width, for an application that keeps it. While a drag runs,
|
|
270
|
+
the root has `data-resizing`: the panels stop their transitions, and the resize cursor shows over
|
|
271
|
+
the whole shell.
|
|
240
272
|
- A panel over the page starts closed, and closes again every time the shell narrows. It is a raised
|
|
241
273
|
surface, `bg.panel` with the `lg` shadow, and keeps its width while it slides closed. An
|
|
242
|
-
application
|
|
274
|
+
application's `open` and `onOpenChange` apply while the panel is beside the page, so a stored
|
|
275
|
+
choice never opens a sheet over a phone's page.
|
|
243
276
|
- While a panel is over the page, the bars, the main region and the other panels are inert, focus
|
|
244
277
|
moves into the panel, and Escape or a press on the backdrop closes it and returns focus to the
|
|
245
278
|
control that opened it. The panel has no dialog role, because the inert page and the returned
|
|
246
279
|
focus give a reader the same behaviour.
|
|
247
280
|
|
|
281
|
+
### Panes
|
|
282
|
+
|
|
283
|
+
`AppShell.Pane` is a column in the body for bars that span the page alone. Render the header, the
|
|
284
|
+
main region and the footer inside it, after the panels at the start and before those at the end, and
|
|
285
|
+
the panels run the full height of the body beside the bars.
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
<AppShell.Root variant="inset">
|
|
289
|
+
<AppShell.Body>
|
|
290
|
+
<AppShell.Navbar folds="hide" name="apps" width="4rem">
|
|
291
|
+
…
|
|
292
|
+
</AppShell.Navbar>
|
|
293
|
+
<AppShell.Navbar name="sections" resizable>
|
|
294
|
+
…
|
|
295
|
+
</AppShell.Navbar>
|
|
296
|
+
<AppShell.Rail panel="sections" resizeLabel="Resize the settings" />
|
|
297
|
+
<AppShell.Pane>
|
|
298
|
+
<AppShell.Header>…</AppShell.Header>
|
|
299
|
+
<AppShell.Main>…</AppShell.Main>
|
|
300
|
+
<AppShell.Footer when="narrow">…</AppShell.Footer>
|
|
301
|
+
</AppShell.Pane>
|
|
302
|
+
</AppShell.Body>
|
|
303
|
+
</AppShell.Root>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
- A header in a pane is outside the main region, so it is the `banner` landmark, and the main
|
|
307
|
+
region's scroll bar starts under it.
|
|
308
|
+
- Under `scroll="window"` a pinned bar in a pane sticks under the root's pinned bars, and the panels
|
|
309
|
+
stick under the root's bars alone. A page's sticky bands stick under both.
|
|
310
|
+
- `variant="inset"` gives the pane the main region's card, so the bars in the pane are on the card.
|
|
311
|
+
|
|
248
312
|
### Triggers, the rail and hooks
|
|
249
313
|
|
|
250
314
|
`AppShell.Trigger` renders the library's button, a neutral ghost unless the caller states another
|
|
251
315
|
look. It finds its panel by the name passed as `panel`, `navbar` by default, and sets
|
|
252
316
|
`aria-controls`, `aria-expanded` and `data-state` from it, with no pressed fill. It does not render
|
|
253
|
-
while its panel is under the page, because
|
|
254
|
-
panel, such as `Navigation`, so a screen reader announces
|
|
317
|
+
while its panel is under the page or out of the shell, because nothing it does can show or hide that
|
|
318
|
+
panel. Name the trigger for the panel, such as `Navigation`, so a screen reader announces
|
|
319
|
+
"Navigation, collapsed, button".
|
|
255
320
|
|
|
256
321
|
`AppShell.Rail` is a 24px strip on the edge between a panel and the page, with a 2px line under the
|
|
257
|
-
pointer and on focus. Render it after the navbar or before an aside
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
322
|
+
pointer and on focus. Render it after the navbar or before an aside. It does not render while its
|
|
323
|
+
panel is over or under the page or out of the shell, and the recipe hides it under a coarse pointer.
|
|
324
|
+
|
|
325
|
+
- Beside a panel that is not resizable the rail is a button named by `openLabel` and `closeLabel`.
|
|
326
|
+
It is not in the tab order, because the trigger and the shortcut open and close the same panel.
|
|
327
|
+
- Beside a resizable panel the rail is the panel's `separator`, named by `resizeLabel`, `Resize` by
|
|
328
|
+
default, with a tab stop, as the WAI-ARIA window splitter pattern requires. Its value is the
|
|
329
|
+
panel's width in pixels. The arrows move it by 10px and by 50px with Shift, Home and End move it
|
|
330
|
+
to `minSize` and `maxSize`, and Enter and a press close and open the panel. A drag starts once the
|
|
331
|
+
pointer has moved 3px, and Escape during a drag returns the width the drag started at. Resizing a
|
|
332
|
+
closed panel opens it at the new width.
|
|
261
333
|
|
|
262
334
|
`useAppShellPanel(name)` reads a panel from an application's own code, for example to close the
|
|
263
335
|
navigation when a destination is pressed:
|
|
@@ -328,6 +400,8 @@ The recipe has no axes.
|
|
|
328
400
|
distance from the pointer to the panel's edge, and `allowOverflow={false}` keeps it inside.
|
|
329
401
|
- A controlled panel remains open until `open` turns false. The trigger, the close trigger and
|
|
330
402
|
Escape call `onOpenChange` with `{ open: false }`.
|
|
403
|
+
- A closing panel plays its exit motion where it is. Each open starts from the position and the size
|
|
404
|
+
of the first open, unless `persistRect` keeps those the panel closed at.
|
|
331
405
|
- The positioner is fixed to the window at the `banner` layer plus the panel's place among the open
|
|
332
406
|
panels: above the page's sticky bands, and under a backdrop, a dialog, a popover, a toast and a
|
|
333
407
|
tooltip. Render it in a portal. A press or focus inside a panel brings it in front, and Escape
|
|
@@ -389,13 +463,13 @@ The panel does not offer these:
|
|
|
389
463
|
the panel remains inside a boundary that does not scroll with the page, such as an app shell's
|
|
390
464
|
main region, and leaves one that does.
|
|
391
465
|
|
|
392
|
-
A menu, a select or a
|
|
393
|
-
panel.
|
|
466
|
+
A menu, a select, a combobox or a date picker opened inside the panel stacks at the `popover` layer,
|
|
467
|
+
over the panel.
|
|
394
468
|
|
|
395
469
|
## Page
|
|
396
470
|
|
|
397
471
|
Lays out a page as a column of bands: a banner, a header, a navigation, a toolbar, a body with an
|
|
398
|
-
optional aside, and a footer.
|
|
472
|
+
optional sidebar and aside, and a footer.
|
|
399
473
|
|
|
400
474
|
```tsx
|
|
401
475
|
import { Page } from "@stealthscale/component-screen";
|
|
@@ -483,6 +557,43 @@ nearest item replaces the trail, with `backIcon` before its words.
|
|
|
483
557
|
the query matches no tab.
|
|
484
558
|
- `Page.Picker` and `Page.Palette` are that button and its panel, for a caller that composes its own
|
|
485
559
|
picker: `<Menu.Trigger as={Page.Picker}>`, and the menu's content rendered as `Page.Palette`.
|
|
560
|
+
- `Page.Tabs` is the slot for the disclosure package's `Tabs.List`, rendered with `as`, where the
|
|
561
|
+
caller's tabs root also contains the panels. In `Page.Nav` its strip starts at the gutter and its
|
|
562
|
+
tabs take the sizes of `Page.TabList`'s, and the band's hairline replaces the strip's own.
|
|
563
|
+
|
|
564
|
+
### Sidebar
|
|
565
|
+
|
|
566
|
+
`Page.Sidebar` renders the page's own destinations at the start of the body, a level below the
|
|
567
|
+
shell's sidebar: the child pages of a route, or the sections of a settings page. Render a `nav`
|
|
568
|
+
named by `aria-label` with a navigation list in it.
|
|
569
|
+
|
|
570
|
+
```tsx
|
|
571
|
+
<Page.Sidebar
|
|
572
|
+
current="Members"
|
|
573
|
+
label="Settings sections"
|
|
574
|
+
pickerIcon={<ChevronDownIcon />}
|
|
575
|
+
resizable
|
|
576
|
+
>
|
|
577
|
+
<nav aria-label="Settings sections">
|
|
578
|
+
<NavList.Root>…</NavList.Root>
|
|
579
|
+
</nav>
|
|
580
|
+
</Page.Sidebar>
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
- From the `md` breakpoint of the page's own width the sidebar is a column beside the body, as wide
|
|
584
|
+
as `sizes.sidebar`. The column starts at the gutter, so the fills of its rows start where the
|
|
585
|
+
title starts.
|
|
586
|
+
- On a narrower page a `Page.Picker` above the body replaces the column, with `current` as its
|
|
587
|
+
words, `label` where `current` is left out, and `pickerIcon` at its end. It opens the column's
|
|
588
|
+
content in a `Page.Palette` named by `label`, and a press on a link in the panel closes it.
|
|
589
|
+
- `sticky` keeps the column in view under the shell's pinned bars and the page's sticky bands, at
|
|
590
|
+
most as tall as the room they leave, and renders its content in the primitives package's scroll
|
|
591
|
+
area.
|
|
592
|
+
- `resizable` renders a `separator` on the column's end edge, named by `resizeLabel`, `Resize` by
|
|
593
|
+
default, with the keys and the drag of a resizable shell panel. `size`, `defaultSize`,
|
|
594
|
+
`onSizeChange`, `minSize` and `maxSize` take pixels, as a panel's do.
|
|
595
|
+
- A page with a sidebar and an aside lays the three out in one row from the `lg` breakpoint, and
|
|
596
|
+
stacks the aside under the body below it.
|
|
486
597
|
|
|
487
598
|
## Section
|
|
488
599
|
|
|
@@ -605,6 +716,8 @@ before the query.
|
|
|
605
716
|
while a query is active. Put it in the scope the search filters, and name what was searched:
|
|
606
717
|
`No pages match`.
|
|
607
718
|
- With children, `Sidebar.Search` contains the caller's field, and the caller filters the rows.
|
|
719
|
+
- The search is a landmark in the `search` role, named like its field, so a search in the sidebar's
|
|
720
|
+
header is inside a landmark. Give each search on a page its own `aria-label`.
|
|
608
721
|
|
|
609
722
|
### In an app shell
|
|
610
723
|
|
|
@@ -729,7 +842,11 @@ import { Switcher } from "@stealthscale/component-screen";
|
|
|
729
842
|
closed to icons it is its mark alone, a 32px square at `md`, with its name in a tooltip, and its
|
|
730
843
|
menu opens beside the rail.
|
|
731
844
|
- Alone and in a toolbar the switcher is one line as wide as its words, at least as tall as a button
|
|
732
|
-
of its size. In a narrow toolbar a switcher with a mark
|
|
845
|
+
of its size. In a narrow toolbar a switcher with a mark is its mark alone, a square as tall as a
|
|
846
|
+
button of its size.
|
|
847
|
+
- A narrow toolbar without room for a switcher with `items` moves it into the toolbar's menu. Its
|
|
848
|
+
row there shows `label` with the current name under it, and `indicator`, and it opens the choices
|
|
849
|
+
in a submenu. A switcher composed from parts keeps its place.
|
|
733
850
|
- The looks and the palettes are the button's. The outline look has the button's light edge.
|
|
734
851
|
- Render the menu's positioner in a `Portal` when the switcher is inside an element that clips, such
|
|
735
852
|
as an app shell panel, so the menu renders over the page.
|
|
@@ -753,6 +870,37 @@ the menu's own:
|
|
|
753
870
|
</Switcher.Root>
|
|
754
871
|
```
|
|
755
872
|
|
|
873
|
+
## LocaleSwitcher and ThemeSwitcher
|
|
874
|
+
|
|
875
|
+
`@stealthscale/component-screen/shell` exports the switchers of what an application's shell
|
|
876
|
+
provides. Each renders `Switcher.Root` with a row per choice on offer, so the switcher's axes and
|
|
877
|
+
the menu's keys apply to it.
|
|
878
|
+
|
|
879
|
+
```tsx
|
|
880
|
+
import { LocaleSwitcher, ThemeSwitcher } from "@stealthscale/component-screen/shell";
|
|
881
|
+
|
|
882
|
+
<Toolbar.End>
|
|
883
|
+
<LocaleSwitcher checkIcon={<CheckIcon />} indicator={<ChevronsUpDownIcon />} />
|
|
884
|
+
<ThemeSwitcher checkIcon={<CheckIcon />} indicator={<ChevronsUpDownIcon />} />
|
|
885
|
+
</Toolbar.End>;
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
- `LocaleSwitcher` reads `useLocale` from `@stealthscale/provider-locale`. Each row shows its
|
|
889
|
+
language's name in that language, `Nederlands` for `nl`, with the language subtag in capitals as
|
|
890
|
+
its mark. A pick sets the locale, so the page is translated, and a language written right to left
|
|
891
|
+
mirrors the page. The control is named `Language` unless `label` names it otherwise.
|
|
892
|
+
- `ThemeSwitcher` reads `useThemeChoice` from `@stealthscale/provider-shell`. Each row shows the
|
|
893
|
+
theme's name with a swatch of its primary colour, which the swatch resolves in its own theme
|
|
894
|
+
through `data-theme`. A pick sets the shell's theme, and the shell keeps the choice under the
|
|
895
|
+
application's name. The control is named `Theme` unless `label` names it otherwise.
|
|
896
|
+
- Each takes every prop of `Switcher.Root` except the choices and the value: `checkIcon`,
|
|
897
|
+
`indicator`, `variant`, `palette`, `size`, `placement`, `positioning`, and `Switcher.Action` rows
|
|
898
|
+
as children.
|
|
899
|
+
- Each renders only where the application offers two choices or more.
|
|
900
|
+
- `useLocale` throws outside a `LocaleProvider`, and outside a `Shell` the theme choice offers no
|
|
901
|
+
theme.
|
|
902
|
+
- The locale switcher does not mark a change in progress, which `useLocale().isPending` reports.
|
|
903
|
+
|
|
756
904
|
## Toolbar
|
|
757
905
|
|
|
758
906
|
Renders a row of controls over a table or a list: a band at each end, a band in the middle, and a
|
|
@@ -763,6 +911,7 @@ import { Toolbar } from "@stealthscale/component-screen";
|
|
|
763
911
|
|
|
764
912
|
<Toolbar.Root aria-label="Invoices" more="More actions" moreIcon={<EllipsisIcon />}>
|
|
765
913
|
<Toolbar.Start>
|
|
914
|
+
<Toolbar.Title>Invoices</Toolbar.Title>
|
|
766
915
|
<Toolbar.Link current href="/invoices/drafts">
|
|
767
916
|
Drafts
|
|
768
917
|
</Toolbar.Link>
|
|
@@ -789,8 +938,16 @@ import { Toolbar } from "@stealthscale/component-screen";
|
|
|
789
938
|
|
|
790
939
|
- The row has `role="toolbar"`, and the arrow keys move focus between its controls, so the row is
|
|
791
940
|
one tab stop. Name it with `aria-label`.
|
|
792
|
-
- The row
|
|
793
|
-
|
|
941
|
+
- The row folds its controls and writes `data-narrow` below `sm` of its own width, and while its
|
|
942
|
+
controls need more room than it has. Its gap is the gap token of its size, and every library
|
|
943
|
+
button in it renders at its size.
|
|
944
|
+
- A folded row that still has no room moves a secondary `Toolbar.Action`, a secondary `Toolbar.Link`
|
|
945
|
+
and a switcher into its menu, the one nearest its end first. `Toolbar.Item` and `Toolbar.Search`
|
|
946
|
+
keep their place, because they have no row in a menu.
|
|
947
|
+
- `Toolbar.Title` renders the row's title, a name or a brand, in semibold with twice the row's gap
|
|
948
|
+
after it, so the control after it reads as the row's first control and not as part of the title. A
|
|
949
|
+
narrow row keeps it with the row's gap alone, because the row folds the controls the wider gap
|
|
950
|
+
sets apart. A link inside it renders as a `Toolbar.Item`, one of the row's controls.
|
|
794
951
|
- `Toolbar.Action` renders the library's button, `ghost`, or `solid` with `primary`, and folds by
|
|
795
952
|
its priority.
|
|
796
953
|
- `Toolbar.Link` renders an `a` in the same look with `href`, `current` and `icon`. `current` sets
|