@uniflowed/ui 0.0.0-alpha.9 → 0.2.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/accordion.js +84 -57
- package/alert-dialog.js +284 -0
- package/alert.js +142 -0
- package/avatar.js +280 -0
- package/breadcrumb.js +138 -0
- package/calendar.js +587 -0
- package/carousel.js +410 -0
- package/checkbox.js +215 -31
- package/collapsible.js +72 -48
- package/color-picker.js +172 -0
- package/combobox.js +216 -39
- package/context-menu.js +215 -0
- package/date-field.js +9 -0
- package/date-picker.js +357 -0
- package/date-range-picker.js +120 -0
- package/dialog.js +243 -178
- package/drag-drop.js +125 -0
- package/drawer.js +504 -0
- package/field.js +260 -43
- package/grid-list.js +8 -0
- package/hover-card.js +52 -52
- package/i18n-provider.js +89 -0
- package/index.js +1177 -31
- package/input-otp.js +218 -0
- package/interactions.js +2327 -0
- package/internal/anchor.js +71 -6
- package/internal/collection.js +562 -0
- package/internal/date-grid.js +260 -0
- package/internal/date-range.js +26 -0
- package/internal/disclosure.js +201 -0
- package/internal/menu-tree.js +228 -0
- package/internal/merge-props.js +85 -1
- package/internal/roving-focus.js +15 -4
- package/internal/segmented-field.js +317 -0
- package/internal/selection.js +171 -0
- package/internal/visually-hidden-style.js +41 -0
- package/list-box.js +13 -0
- package/menu.js +553 -361
- package/menubar.js +295 -0
- package/number-field.js +263 -0
- package/package.json +8 -28
- package/pagination.js +34 -22
- package/popover.js +116 -75
- package/progress.js +21 -16
- package/radio-group.js +81 -75
- package/range-calendar.js +79 -0
- package/resizable.js +155 -9
- package/scroll-area.js +283 -0
- package/select.js +83 -37
- package/separator.js +97 -0
- package/sheet.js +189 -0
- package/sidebar.js +320 -0
- package/skeleton.js +163 -0
- package/slider.js +95 -89
- package/switch.js +42 -34
- package/table.js +100 -71
- package/tabs.js +100 -91
- package/tag-group.js +8 -0
- package/time-field.js +8 -0
- package/toast.js +36 -66
- package/toggle-group.js +53 -49
- package/toggle.js +41 -27
- package/tooltip.js +48 -55
- package/tree.js +8 -0
- package/visually-hidden.js +259 -0
package/index.js
CHANGED
|
@@ -32,6 +32,105 @@
|
|
|
32
32
|
// A library written in TypeScript can document those constraints; it cannot
|
|
33
33
|
// state them.
|
|
34
34
|
//
|
|
35
|
+
// # The copy step copies the look, never the behaviour
|
|
36
|
+
//
|
|
37
|
+
// shadcn's product is not a component. It is `npx shadcn add dialog`, which
|
|
38
|
+
// writes the source into your repository so that you own it and change it —
|
|
39
|
+
// and around that sit `init`, `view`, `search`, `build`, `migrate`, `eject`, an
|
|
40
|
+
// MCP server, a `components.json` and a registry format anybody can publish to.
|
|
41
|
+
// The roadmap first answered that with "typed imports, preset styles, and
|
|
42
|
+
// **no copy step**", and ubugeeei-prod/uf#303 is where the answer was argued.
|
|
43
|
+
// ubugeeei-prod/uf#947 kept half of it and reversed the other half, and the
|
|
44
|
+
// half it kept is this package.
|
|
45
|
+
//
|
|
46
|
+
// **Why this package is still never copied.** A copy is a fork with no
|
|
47
|
+
// upstream, and four things follow. A focus trap fixed here reaches everyone
|
|
48
|
+
// who upgrades and reaches nobody who copied. The composition constraints above
|
|
49
|
+
// are checked *across the boundary*: `Tabs.List` declaring `renders* Tabs.Tab`
|
|
50
|
+
// means something while the library is imported and means nothing once the
|
|
51
|
+
// source has been pasted into an application, because then it is the
|
|
52
|
+
// application's own component and Flow has nothing left to hold it to.
|
|
53
|
+
// `sideEffects: false` and one module per primitive already give a bundler
|
|
54
|
+
// everything a copy would. And the accessibility work stays in one place with
|
|
55
|
+
// one suite over it, rather than in every consumer's repository at the version
|
|
56
|
+
// they took it at.
|
|
57
|
+
//
|
|
58
|
+
// **What `uf ui add` copies instead.** The styled layer. `uf ui add dialog`
|
|
59
|
+
// writes `app/components/ui/dialog.js`, which imports its parts from
|
|
60
|
+
// `@uniflowed/ui` and owns the scrim, the spacing and the tone of the
|
|
61
|
+
// trigger — what a person means when they say "our dialog". None of the four
|
|
62
|
+
// arguments above is reopened by it, because none of them is about the look:
|
|
63
|
+
// the focus trap still arrives by upgrade, and the copied `TabsList` still takes
|
|
64
|
+
// `renders* TabsTab` over a `TabsTab` that renders this package's tab, so the
|
|
65
|
+
// constraint holds in the application's own file. `crates/uf_ui`'s header is
|
|
66
|
+
// the decision in full.
|
|
67
|
+
//
|
|
68
|
+
// **What a caller gets without copying anything.** The reason people copy
|
|
69
|
+
// is to change the markup, so that has to be answered or this is a worse
|
|
70
|
+
// library for the same use. The answer is `render`, which every part that
|
|
71
|
+
// renders an element of its own is growing:
|
|
72
|
+
//
|
|
73
|
+
// <Menu.Item render={(props) => <a href="/settings" {...props} />}>
|
|
74
|
+
// Settings
|
|
75
|
+
// </Menu.Item>
|
|
76
|
+
//
|
|
77
|
+
// The part computes every attribute, every id, every composed handler and every
|
|
78
|
+
// ref exactly as it would have, and hands them to the caller to put on their
|
|
79
|
+
// own element; `children` is among them, so a caller who spreads and
|
|
80
|
+
// self-closes still gets what was written between the tags. What it does *not*
|
|
81
|
+
// hand over is anything true of the element rather than of the part —
|
|
82
|
+
// `type="button"` stays on the `<button>` branch.
|
|
83
|
+
//
|
|
84
|
+
// It is deliberately not Radix's `asChild`. Cloning a child hides which props
|
|
85
|
+
// arrived and in what order; a function is handed the object, so a caller can
|
|
86
|
+
// read it, order it themselves, and drop one on purpose. And the part is still
|
|
87
|
+
// the part: `Menu.Body`'s `renders*` still rejects a `<div>` where a
|
|
88
|
+
// `Menu.Item` belongs, because a `Menu.Item` rendered as an `<a>` is a
|
|
89
|
+
// `Menu.Item`. That is the half a copied source cannot keep, and it is what
|
|
90
|
+
// makes leaving this package uncopied a trade rather than a loss.
|
|
91
|
+
//
|
|
92
|
+
// **Where it is, today.** Every fixed-element part has it in `Accordion`,
|
|
93
|
+
// `Alert`, `AlertDialog`, `Avatar`, `Breadcrumb`, `Collapsible`, `Dialog`,
|
|
94
|
+
// `Drawer`, `Menu`, `ContextMenu`, `Menubar`, `Pagination`, `Popover`, `Sheet`,
|
|
95
|
+
// `Skeleton`, `Slider`, `Table`, `Tabs` and `ToggleGroup`; so do the
|
|
96
|
+
// single-part `Checkbox`, `Progress`, `Separator`, `Switch` and `Toggle`.
|
|
97
|
+
// `Field.Root`, `Field.Label`, `Field.Control`, `Field.Description`,
|
|
98
|
+
// `Field.Status` and `Field.Error` take it too, as do `RadioGroup`,
|
|
99
|
+
// `InputOtp.Separator`, `Tooltip.Trigger`, `Tooltip.Body`,
|
|
100
|
+
// `HoverCard.Trigger`, `HoverCard.Body` and `Sidebar.Item`. A documented
|
|
101
|
+
// escape hatch that is not there is worse than an undocumented one that is, so
|
|
102
|
+
// the state of every part is a table in `packages/ui/ui.test.js` rather than a
|
|
103
|
+
// claim in this paragraph: it names each part as implemented, no-element, or
|
|
104
|
+
// fixed remainder, and the suite holds the lists to the exported files. A part
|
|
105
|
+
// added to this barrel is in none of them and the suite says so.
|
|
106
|
+
//
|
|
107
|
+
// **Are the `internal/` modules ever public?** No, and the consequence is
|
|
108
|
+
// worth stating rather than leaving as an omission. `merge-props.js`,
|
|
109
|
+
// `roving-focus.js`, `controlled-state.js` and the rest each explain in their
|
|
110
|
+
// own header why exporting them would publish a weaker promise than the
|
|
111
|
+
// components make — a consumer who could reach `merge-props.js` could build a
|
|
112
|
+
// part that spreads `rest` last, which is the failure it exists to prevent. So
|
|
113
|
+
// there is no third-party primitive that participates the way these do, and no
|
|
114
|
+
// registry of them: the uf-shaped equivalent of publishing a component is a
|
|
115
|
+
// pull request against this package, where its keyboard map gets the same suite
|
|
116
|
+
// as everything else. That is a real cost of the decision and the right side of
|
|
117
|
+
// it for a library whose value is that the hard parts are correct. What a third
|
|
118
|
+
// party *can* build on is the escape hatch itself, which is the whole public
|
|
119
|
+
// surface it needs: a component of theirs given to `render` receives the props
|
|
120
|
+
// this package would have used, and their own composition sits inside a part
|
|
121
|
+
// that is still checked.
|
|
122
|
+
//
|
|
123
|
+
// **What the CLI adds.** `uf ui add`, `uf ui list` and `uf ui diff`, over a
|
|
124
|
+
// registry of styled components in `registry/ui/` that is embedded into the
|
|
125
|
+
// binary — each one built on parts exported here, and none of them a copy of
|
|
126
|
+
// one. There is still no registry of *primitives*, for the reason the previous
|
|
127
|
+
// paragraph gives, and there is no `components.json`: `uf.config.js` is the one
|
|
128
|
+
// configuration surface by design, so a UI option belongs there rather than in
|
|
129
|
+
// a second file. The affordance `shadcn view` has — "tell me what this
|
|
130
|
+
// component is made of" — is still `uf inspect --json` for the parts, out of
|
|
131
|
+
// `crates/uf_lib/src/ui.rs`, which `cargo test -p uf_lib` holds to this barrel
|
|
132
|
+
// in both directions.
|
|
133
|
+
//
|
|
35
134
|
// # Styling is a default, not a dependency
|
|
36
135
|
//
|
|
37
136
|
// Nothing here imports StyleX, and nothing here has a StyleX-shaped type. A
|
|
@@ -39,6 +138,71 @@
|
|
|
39
138
|
// the same components with exactly the same behaviour; the design-system layer
|
|
40
139
|
// that adds uf's default styles is built *on* these, not into them.
|
|
41
140
|
//
|
|
141
|
+
// # What is not here, and where it went instead
|
|
142
|
+
//
|
|
143
|
+
// About twenty of the catalogue this package is measured against have no
|
|
144
|
+
// behaviour at all. Badge, Card, Button, Input, Textarea, Label and Aspect
|
|
145
|
+
// Ratio are, between them, a class list and a `<div>`. For a library whose
|
|
146
|
+
// product *is* the styles that is coherent — you copy them in and you own
|
|
147
|
+
// them. It is not coherent here: a `Badge` with no styles is a `<span>`, a
|
|
148
|
+
// `Card` with no styles is a `<div>`, and shipping them from a package that
|
|
149
|
+
// ships no styles would make this a library of empty elements. Shipping them
|
|
150
|
+
// from `@uniflowed/stylex` would make *that* a component library, which its
|
|
151
|
+
// own header forbids. So for a long time the answer on record was both and
|
|
152
|
+
// neither, which is ubugeeei-prod/uf#298.
|
|
153
|
+
//
|
|
154
|
+
// The line that holds is not "styled versus headless". It is **whether the
|
|
155
|
+
// thing has a decision in it**:
|
|
156
|
+
//
|
|
157
|
+
// - An ARIA decision, a state machine or a keyboard requirement makes it a
|
|
158
|
+
// component here, even when it renders a single element. `Progress` is one
|
|
159
|
+
// `<div>`, and it belongs, because the conditional that omits
|
|
160
|
+
// `aria-valuenow` when the amount is unknown — rather than sending
|
|
161
|
+
// `aria-valuenow={0}`, which says "nothing has happened" — is the whole
|
|
162
|
+
// component. `Toggle` and `Checkbox` are the same shape for the same reason.
|
|
163
|
+
// - Anything that is only a class list belongs in `@uniflowed/stylex/preset`,
|
|
164
|
+
// which already has the right form: `buttonStyles({ tone, size })`,
|
|
165
|
+
// `cardStyles()`, `textStyles({ size, tone })` and `fieldStyles()` return
|
|
166
|
+
// `{ className }` for a caller to spread onto their own element. That is a
|
|
167
|
+
// better answer than a `<Badge>`, not a lesser one — a component wrapping a
|
|
168
|
+
// `<button>` takes away `type="submit"`, `formAction`, the ref and every
|
|
169
|
+
// attribute nobody thought to forward, and gives back a class name the
|
|
170
|
+
// caller could have written.
|
|
171
|
+
//
|
|
172
|
+
// This is stated rather than left as an omission, because an omission reads as
|
|
173
|
+
// an oversight and the next contributor closes it with a `<Badge>`.
|
|
174
|
+
// `crates/uf_lib/src/ui.rs` carries the same decision as data: those entries
|
|
175
|
+
// are `Declined` with the preset functions that replace them named on each,
|
|
176
|
+
// and `cargo test -p uf_lib` fails if a name there stops existing.
|
|
177
|
+
//
|
|
178
|
+
// Five of that twenty are on the other side of the line and now ship — Alert,
|
|
179
|
+
// Avatar, Breadcrumb, Separator and Skeleton — each for one specific reason,
|
|
180
|
+
// and each of them one or two elements:
|
|
181
|
+
//
|
|
182
|
+
// - **Alert** is the one whose usual shape is arguably wrong to copy.
|
|
183
|
+
// `role="alert"` is a live region, an element already in the document when
|
|
184
|
+
// the page loads announces on insertion or not at all, and a permanently
|
|
185
|
+
// rendered "your trial ends soon" box carrying that role is either an
|
|
186
|
+
// interruption on every page load or silence. So the role is behind `live`,
|
|
187
|
+
// and a static callout does not get one. `field.js` already makes that call
|
|
188
|
+
// correctly for `Field.Error`.
|
|
189
|
+
// - **Avatar** is a three-state machine — loading, loaded, failed — with the
|
|
190
|
+
// fallback held back so a cached image does not flash somebody's initials,
|
|
191
|
+
// and with `alt=""` by default, because an avatar beside a name that puts
|
|
192
|
+
// the name in `alt` makes every screen reader say it twice.
|
|
193
|
+
// - **Breadcrumb** is `Pagination`'s shape one door along: a named `<nav>`, one
|
|
194
|
+
// `aria-current="page"`, and separators hidden so the trail is not read as
|
|
195
|
+
// "Home slash Settings slash Billing".
|
|
196
|
+
// - **Separator** is two lines with one decision in them, and it is the
|
|
197
|
+
// decision `Progress` is: `role="separator"` with an `aria-orientation` for a
|
|
198
|
+
// boundary a reader should be told about, `aria-hidden` for a rule that is
|
|
199
|
+
// only a rule.
|
|
200
|
+
// - **Skeleton** is the one that silently makes a page worse. A screen of
|
|
201
|
+
// skeletons is a screen of empty boxes, so the boxes are `aria-hidden`, the
|
|
202
|
+
// region they stand in is `aria-busy`, and a live region that was empty for
|
|
203
|
+
// one commit says "Loading" — which is #289's rule met at the moment it bites
|
|
204
|
+
// hardest, because a skeleton screen is busy on its very first render.
|
|
205
|
+
//
|
|
42
206
|
// # What these components promise React
|
|
43
207
|
//
|
|
44
208
|
// Nothing here mutates during a render, reads a ref during a render, or depends
|
|
@@ -57,20 +221,9 @@
|
|
|
57
221
|
//
|
|
58
222
|
// One component does use it, and it is the case the API is actually for.
|
|
59
223
|
// `toast("Saved")` is called from an event handler or a `catch`, so the queue
|
|
60
|
-
// of notifications lives outside React —
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// store; the DOM is not.
|
|
64
|
-
//
|
|
65
|
-
// That store should be an atom in `@uniflowed/state`, and was one. It is
|
|
66
|
-
// written out by hand in `toast.js` because `@uniflowed/ui` is published to
|
|
67
|
-
// npm and `@uniflowed/state` is not: its name has never been bound, and
|
|
68
|
-
// binding it takes a person with an `npm login` session and a 2FA prompt —
|
|
69
|
-
// ubugeeei-prod/uf#210. A published package whose dependency is missing
|
|
70
|
-
// installs as nothing, `ETARGET` on the first thing a user types, so this
|
|
71
|
-
// package cannot declare that dependency until the name exists. The queue
|
|
72
|
-
// moves back to `@uniflowed/state` when #210 binds it. The local store is the
|
|
73
|
-
// shippable design, not the better one.
|
|
224
|
+
// of notifications lives outside React — an atom in `@uniflowed/state`, read
|
|
225
|
+
// through `useSyncExternalStore` with the cached immutable snapshots and the
|
|
226
|
+
// consistent server snapshot that requires. A queue is a store; the DOM is not.
|
|
74
227
|
//
|
|
75
228
|
// # Server and client
|
|
76
229
|
//
|
|
@@ -85,10 +238,29 @@
|
|
|
85
238
|
// One root module per primitive, each with its own `exports` subpath, each
|
|
86
239
|
// named after the thing it implements:
|
|
87
240
|
//
|
|
88
|
-
// - `dialog.js` — the focus trap, focus restore, scroll lock and inert page
|
|
89
|
-
//
|
|
90
|
-
// - `
|
|
91
|
-
//
|
|
241
|
+
// - `dialog.js` — the focus trap, focus restore, scroll lock and inert page,
|
|
242
|
+
// and the three props the four components below it differ from it by.
|
|
243
|
+
// - `alert-dialog.js`, `sheet.js`, `drawer.js` and `sidebar.js` — the four
|
|
244
|
+
// built on that one. An alert dialog is modal and cannot be dismissed by
|
|
245
|
+
// pressing beside it; a sheet is a dialog with an edge; a drawer is a sheet
|
|
246
|
+
// with a gesture, and therefore with WCAG 2.5.7's keyboard equivalent of it;
|
|
247
|
+
// a sidebar is most often neither modal nor a dialog, and becomes both on a
|
|
248
|
+
// narrow viewport.
|
|
249
|
+
// - `carousel.js`, `scroll-area.js` and `input-otp.js` — the three that replace
|
|
250
|
+
// something the browser already does, and so the three that have to be better
|
|
251
|
+
// than what they replaced. Each module's header says what it gives that the
|
|
252
|
+
// plain element does not; if it ever stops being true, the component should
|
|
253
|
+
// be deleted rather than fixed.
|
|
254
|
+
// - `menu.js`, `context-menu.js` and `menubar.js` — the arrow keys, typeahead,
|
|
255
|
+
// submenus and `Escape` stacking, and the two components that are that
|
|
256
|
+
// behaviour opened differently. A context menu is opened by the right button,
|
|
257
|
+
// by `Shift+F10` and by a long press, and opens at a *point*; a menubar is a
|
|
258
|
+
// row of them with one tab stop and arrows that walk between the open menus.
|
|
259
|
+
// shadcn's fourth menu, Dropdown Menu, is `menu.js` under another name and is
|
|
260
|
+
// deliberately not a second export.
|
|
261
|
+
// - `combobox.js` — `aria-activedescendant` over a filtered list, the count a
|
|
262
|
+
// screen reader is told, and the option groups that make a command palette a
|
|
263
|
+
// composition rather than a seventh module.
|
|
92
264
|
// - `select.js` — the other half of the combobox pattern: the select-only one,
|
|
93
265
|
// with typeahead, option groups and a value a form can submit.
|
|
94
266
|
// - `tabs.js` — the roving `tabindex`, and automatic versus manual activation.
|
|
@@ -119,13 +291,25 @@
|
|
|
119
291
|
// a dialog that is not modal, a tooltip describes its trigger and may never
|
|
120
292
|
// take focus, a hover card is neither and holds links — and because a flag
|
|
121
293
|
// selecting between them would be one flag every behaviour had to read.
|
|
294
|
+
// - `alert.js`, `avatar.js`, `breadcrumb.js`, `separator.js` and `skeleton.js`
|
|
295
|
+
// — the five above that look like a class list and are not. One or two
|
|
296
|
+
// elements each, and one conditional each: whether a callout announces
|
|
297
|
+
// itself, which of three states an image is in, whether the last crumb is a
|
|
298
|
+
// link, whether a rule is in the accessibility tree, and whether anybody is
|
|
299
|
+
// told the page is loading.
|
|
300
|
+
// - `interactions.js` — the press, the hover, the focus ring, the long press,
|
|
301
|
+
// the drag and the key, and the one module here that exports hooks rather
|
|
302
|
+
// than parts. A press that is released outside does not press, a hover is
|
|
303
|
+
// never a finger's, a ring is drawn for the keyboard and not for the pointer,
|
|
304
|
+
// and a screen reader's click is a press; its header says what each rule
|
|
305
|
+
// prevents and where it differs from React Aria's.
|
|
122
306
|
//
|
|
123
|
-
// Every name below is exported from one of those,
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
307
|
+
// Every name below is exported from one of those, and this file is the only way
|
|
308
|
+
// to import any of them: `package.json` exports it alone. The split is by
|
|
309
|
+
// primitive because that is the unit a reader looks for, the unit a bundler
|
|
310
|
+
// drops, and the unit the WAI-ARIA practices are written in.
|
|
127
311
|
//
|
|
128
|
-
// `internal/` holds
|
|
312
|
+
// `internal/` holds ten modules and nothing else, each a rule the primitives
|
|
129
313
|
// must apply identically and a consumer must not be able to apply differently:
|
|
130
314
|
// `merge-props.js` (the caller's props go on first, the component's semantics
|
|
131
315
|
// last), `controlled-state.js` (what "controlled" means here),
|
|
@@ -138,8 +322,10 @@
|
|
|
138
322
|
// fit where it was asked to go), `focus.js` (which elements a reader can reach,
|
|
139
323
|
// which a focus trap and a popover want opposite things from), and
|
|
140
324
|
// `hover-intent.js` (what WCAG requires of content shown on hover or focus,
|
|
141
|
-
// which is three clauses and one mechanism)
|
|
142
|
-
//
|
|
325
|
+
// which is three clauses and one mechanism), and `menu-tree.js` (the chain of
|
|
326
|
+
// open menus the three menu components share, and what `Escape` and choosing an
|
|
327
|
+
// item are defined in terms of). Each says in its own header why it is
|
|
328
|
+
// unreachable rather than exported. There is no `internal/props.js`-shaped
|
|
143
329
|
// bag of helpers: a module that cannot say what it is about does not belong in
|
|
144
330
|
// this package.
|
|
145
331
|
|
|
@@ -150,10 +336,50 @@ import {
|
|
|
150
336
|
AccordionRoot,
|
|
151
337
|
AccordionTrigger,
|
|
152
338
|
} from "./accordion.js";
|
|
339
|
+
import { AlertDescription, AlertRoot, AlertTitle } from "./alert.js";
|
|
340
|
+
import {
|
|
341
|
+
AlertDialogAction,
|
|
342
|
+
AlertDialogBody,
|
|
343
|
+
AlertDialogCancel,
|
|
344
|
+
AlertDialogDescription,
|
|
345
|
+
AlertDialogFooter,
|
|
346
|
+
AlertDialogHeader,
|
|
347
|
+
AlertDialogOverlay,
|
|
348
|
+
AlertDialogRoot,
|
|
349
|
+
AlertDialogTitle,
|
|
350
|
+
AlertDialogTrigger,
|
|
351
|
+
} from "./alert-dialog.js";
|
|
352
|
+
import { AvatarFallback, AvatarImage, AvatarRoot } from "./avatar.js";
|
|
353
|
+
import {
|
|
354
|
+
BreadcrumbItem,
|
|
355
|
+
BreadcrumbLink,
|
|
356
|
+
BreadcrumbList,
|
|
357
|
+
BreadcrumbPage,
|
|
358
|
+
BreadcrumbRoot,
|
|
359
|
+
BreadcrumbSeparator,
|
|
360
|
+
} from "./breadcrumb.js";
|
|
361
|
+
import {
|
|
362
|
+
CarouselContent,
|
|
363
|
+
CarouselItem,
|
|
364
|
+
CarouselNext,
|
|
365
|
+
CarouselPause,
|
|
366
|
+
CarouselPrevious,
|
|
367
|
+
CarouselRoot,
|
|
368
|
+
} from "./carousel.js";
|
|
369
|
+
import {
|
|
370
|
+
CalendarDay,
|
|
371
|
+
CalendarMonth,
|
|
372
|
+
CalendarNext,
|
|
373
|
+
CalendarPrevious,
|
|
374
|
+
CalendarRoot,
|
|
375
|
+
} from "./calendar.js";
|
|
153
376
|
import { Checkbox } from "./checkbox.js";
|
|
377
|
+
import { ContextMenuRoot, ContextMenuTrigger } from "./context-menu.js";
|
|
154
378
|
import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from "./collapsible.js";
|
|
155
379
|
import {
|
|
156
380
|
ComboboxEmpty,
|
|
381
|
+
ComboboxGroup,
|
|
382
|
+
ComboboxGroupLabel,
|
|
157
383
|
ComboboxInput,
|
|
158
384
|
ComboboxLabel,
|
|
159
385
|
ComboboxList,
|
|
@@ -161,6 +387,12 @@ import {
|
|
|
161
387
|
ComboboxRoot,
|
|
162
388
|
ComboboxStatus,
|
|
163
389
|
} from "./combobox.js";
|
|
390
|
+
import {
|
|
391
|
+
DatePickerCalendar,
|
|
392
|
+
DatePickerInput,
|
|
393
|
+
DatePickerRoot,
|
|
394
|
+
DatePickerTrigger,
|
|
395
|
+
} from "./date-picker.js";
|
|
164
396
|
import {
|
|
165
397
|
DialogBody,
|
|
166
398
|
DialogClose,
|
|
@@ -172,19 +404,43 @@ import {
|
|
|
172
404
|
DialogTitle,
|
|
173
405
|
DialogTrigger,
|
|
174
406
|
} from "./dialog.js";
|
|
175
|
-
import {
|
|
407
|
+
import {
|
|
408
|
+
DrawerBody,
|
|
409
|
+
DrawerClose,
|
|
410
|
+
DrawerDescription,
|
|
411
|
+
DrawerFooter,
|
|
412
|
+
DrawerHandle,
|
|
413
|
+
DrawerHeader,
|
|
414
|
+
DrawerOverlay,
|
|
415
|
+
DrawerRoot,
|
|
416
|
+
DrawerTitle,
|
|
417
|
+
DrawerTrigger,
|
|
418
|
+
} from "./drawer.js";
|
|
419
|
+
import {
|
|
420
|
+
FieldControl,
|
|
421
|
+
FieldDescription,
|
|
422
|
+
FieldError,
|
|
423
|
+
FieldLabel,
|
|
424
|
+
FieldRoot,
|
|
425
|
+
FieldStatus,
|
|
426
|
+
} from "./field.js";
|
|
176
427
|
import { HoverCardBody, HoverCardRoot, HoverCardTrigger } from "./hover-card.js";
|
|
428
|
+
import { InputOtpGroup, InputOtpRoot, InputOtpSeparator, InputOtpSlot } from "./input-otp.js";
|
|
177
429
|
import {
|
|
178
430
|
MenuBody,
|
|
431
|
+
MenuCheckboxItem,
|
|
179
432
|
MenuGroup,
|
|
180
433
|
MenuItem,
|
|
181
434
|
MenuLabel,
|
|
435
|
+
MenuRadioGroup,
|
|
436
|
+
MenuRadioItem,
|
|
182
437
|
MenuRoot,
|
|
183
438
|
MenuSeparator,
|
|
184
439
|
MenuSub,
|
|
185
440
|
MenuSubTrigger,
|
|
186
441
|
MenuTrigger,
|
|
187
442
|
} from "./menu.js";
|
|
443
|
+
import { MenubarMenu, MenubarRoot, MenubarTrigger } from "./menubar.js";
|
|
188
444
|
import {
|
|
189
445
|
NavigationMenuBody,
|
|
190
446
|
NavigationMenuItem,
|
|
@@ -204,6 +460,7 @@ import { PopoverBody, PopoverRoot, PopoverTrigger } from "./popover.js";
|
|
|
204
460
|
import { Progress } from "./progress.js";
|
|
205
461
|
import { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot } from "./radio-group.js";
|
|
206
462
|
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "./resizable.js";
|
|
463
|
+
import { ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaViewport } from "./scroll-area.js";
|
|
207
464
|
import {
|
|
208
465
|
SelectGroup,
|
|
209
466
|
SelectGroupLabel,
|
|
@@ -215,6 +472,27 @@ import {
|
|
|
215
472
|
SelectTrigger,
|
|
216
473
|
SelectValue,
|
|
217
474
|
} from "./select.js";
|
|
475
|
+
import { Separator } from "./separator.js";
|
|
476
|
+
import {
|
|
477
|
+
SheetBody,
|
|
478
|
+
SheetClose,
|
|
479
|
+
SheetDescription,
|
|
480
|
+
SheetFooter,
|
|
481
|
+
SheetHeader,
|
|
482
|
+
SheetOverlay,
|
|
483
|
+
SheetRoot,
|
|
484
|
+
SheetTitle,
|
|
485
|
+
SheetTrigger,
|
|
486
|
+
} from "./sheet.js";
|
|
487
|
+
import {
|
|
488
|
+
SidebarBody,
|
|
489
|
+
SidebarFooter,
|
|
490
|
+
SidebarHeader,
|
|
491
|
+
SidebarItem,
|
|
492
|
+
SidebarRoot,
|
|
493
|
+
SidebarTrigger,
|
|
494
|
+
} from "./sidebar.js";
|
|
495
|
+
import { SkeletonBox, SkeletonRoot } from "./skeleton.js";
|
|
218
496
|
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from "./slider.js";
|
|
219
497
|
import { Switch } from "./switch.js";
|
|
220
498
|
import {
|
|
@@ -247,16 +525,240 @@ import { ToggleGroupItem, ToggleGroupRoot } from "./toggle-group.js";
|
|
|
247
525
|
import { TooltipBody, TooltipProvider, TooltipRoot, TooltipTrigger } from "./tooltip.js";
|
|
248
526
|
|
|
249
527
|
export type { AccordionType } from "./accordion.js";
|
|
528
|
+
export type { AvatarStatus } from "./avatar.js";
|
|
529
|
+
// A date, however a caller had one to hand: a `PlainDate` from
|
|
530
|
+
// `@uniflowed/temporal`, or the ISO 8601 string a form field or a URL carries.
|
|
531
|
+
export type { DateValue } from "./calendar.js";
|
|
250
532
|
export type { ActivationMode } from "./tabs.js";
|
|
533
|
+
// What a modal announces itself as, for a caller who holds one in a variable.
|
|
534
|
+
// Two members, not a string: see `dialog.js`.
|
|
535
|
+
export type { DialogRole } from "./dialog.js";
|
|
536
|
+
// Which edge of the viewport a sheet or a drawer is attached to. `Sidebar` has
|
|
537
|
+
// its own two-member union, because a sidebar is never on the top or bottom.
|
|
538
|
+
export type { Edge } from "./sheet.js";
|
|
539
|
+
export type { FieldSource } from "./field.js";
|
|
540
|
+
export type { InputOtpKind } from "./input-otp.js";
|
|
541
|
+
export type { MenuSelect } from "./menu.js";
|
|
542
|
+
// Which way a carousel, a scroll area or a separator runs.
|
|
543
|
+
export type { Orientation } from "./separator.js";
|
|
544
|
+
export type { SidebarSide } from "./sidebar.js";
|
|
251
545
|
// Where an anchored overlay opens, for a caller who holds one in a variable or
|
|
252
546
|
// a prop of their own. Unions rather than strings, so `side="botom"` is a type
|
|
253
547
|
// error at the call rather than an overlay that quietly opens somewhere else.
|
|
254
|
-
|
|
548
|
+
// `LogicalSide` is the same four plus `inline-start` and `inline-end`, which
|
|
549
|
+
// are the ones that mean "the way the reader reads" - what a submenu opens
|
|
550
|
+
// onto, and the left of the page in Arabic.
|
|
551
|
+
export type { Align, LogicalSide, Side } from "./popover.js";
|
|
255
552
|
export type { Sort } from "./table.js";
|
|
256
553
|
export type { Notification, ToastChanges, ToastOptions, Urgency } from "./toast.js";
|
|
257
554
|
export type { ToggleGroupType } from "./toggle-group.js";
|
|
258
555
|
|
|
259
|
-
|
|
556
|
+
/**
|
|
557
|
+
* Every part, under the name its module gives it.
|
|
558
|
+
*
|
|
559
|
+
* This file is the only entry point the package has — `package.json` exports
|
|
560
|
+
* `.` and nothing else — so a name a module exports and this list leaves out is
|
|
561
|
+
* a name nobody can import. The namespaces below are the same parts spelled for
|
|
562
|
+
* composing a page (`Dialog.Root`); these are for the wrapper, the preset and
|
|
563
|
+
* the test that want one part (`import { DialogRoot } from "@uniflowed/ui"`).
|
|
564
|
+
* `sideEffects: false` lets a bundler keep the module a name comes from and drop
|
|
565
|
+
* the rest, and `uf_rsc` names that module, not the package, as the client
|
|
566
|
+
* boundary of a Server Component that imports the name.
|
|
567
|
+
*/
|
|
568
|
+
export {
|
|
569
|
+
AccordionContent,
|
|
570
|
+
AccordionHeader,
|
|
571
|
+
AccordionItem,
|
|
572
|
+
AccordionRoot,
|
|
573
|
+
AccordionTrigger,
|
|
574
|
+
AlertDescription,
|
|
575
|
+
AlertDialogAction,
|
|
576
|
+
AlertDialogBody,
|
|
577
|
+
AlertDialogCancel,
|
|
578
|
+
AlertDialogDescription,
|
|
579
|
+
AlertDialogFooter,
|
|
580
|
+
AlertDialogHeader,
|
|
581
|
+
AlertDialogOverlay,
|
|
582
|
+
AlertDialogRoot,
|
|
583
|
+
AlertDialogTitle,
|
|
584
|
+
AlertDialogTrigger,
|
|
585
|
+
AlertRoot,
|
|
586
|
+
AlertTitle,
|
|
587
|
+
AvatarFallback,
|
|
588
|
+
AvatarImage,
|
|
589
|
+
AvatarRoot,
|
|
590
|
+
BreadcrumbItem,
|
|
591
|
+
BreadcrumbLink,
|
|
592
|
+
BreadcrumbList,
|
|
593
|
+
BreadcrumbPage,
|
|
594
|
+
BreadcrumbRoot,
|
|
595
|
+
BreadcrumbSeparator,
|
|
596
|
+
CalendarDay,
|
|
597
|
+
CalendarMonth,
|
|
598
|
+
CalendarNext,
|
|
599
|
+
CalendarPrevious,
|
|
600
|
+
CalendarRoot,
|
|
601
|
+
CarouselContent,
|
|
602
|
+
CarouselItem,
|
|
603
|
+
CarouselNext,
|
|
604
|
+
CarouselPause,
|
|
605
|
+
CarouselPrevious,
|
|
606
|
+
CarouselRoot,
|
|
607
|
+
CollapsibleContent,
|
|
608
|
+
CollapsibleRoot,
|
|
609
|
+
CollapsibleTrigger,
|
|
610
|
+
ComboboxEmpty,
|
|
611
|
+
ComboboxGroup,
|
|
612
|
+
ComboboxGroupLabel,
|
|
613
|
+
ComboboxInput,
|
|
614
|
+
ComboboxLabel,
|
|
615
|
+
ComboboxList,
|
|
616
|
+
ComboboxOption,
|
|
617
|
+
ComboboxRoot,
|
|
618
|
+
ComboboxStatus,
|
|
619
|
+
ContextMenuRoot,
|
|
620
|
+
ContextMenuTrigger,
|
|
621
|
+
DatePickerCalendar,
|
|
622
|
+
DatePickerInput,
|
|
623
|
+
DatePickerRoot,
|
|
624
|
+
DatePickerTrigger,
|
|
625
|
+
DialogBody,
|
|
626
|
+
DialogClose,
|
|
627
|
+
DialogDescription,
|
|
628
|
+
DialogFooter,
|
|
629
|
+
DialogHeader,
|
|
630
|
+
DialogOverlay,
|
|
631
|
+
DialogRoot,
|
|
632
|
+
DialogTitle,
|
|
633
|
+
DialogTrigger,
|
|
634
|
+
DrawerBody,
|
|
635
|
+
DrawerClose,
|
|
636
|
+
DrawerDescription,
|
|
637
|
+
DrawerFooter,
|
|
638
|
+
DrawerHandle,
|
|
639
|
+
DrawerHeader,
|
|
640
|
+
DrawerOverlay,
|
|
641
|
+
DrawerRoot,
|
|
642
|
+
DrawerTitle,
|
|
643
|
+
DrawerTrigger,
|
|
644
|
+
FieldControl,
|
|
645
|
+
FieldDescription,
|
|
646
|
+
FieldError,
|
|
647
|
+
FieldLabel,
|
|
648
|
+
FieldRoot,
|
|
649
|
+
FieldStatus,
|
|
650
|
+
HoverCardBody,
|
|
651
|
+
HoverCardRoot,
|
|
652
|
+
HoverCardTrigger,
|
|
653
|
+
InputOtpGroup,
|
|
654
|
+
InputOtpRoot,
|
|
655
|
+
InputOtpSeparator,
|
|
656
|
+
InputOtpSlot,
|
|
657
|
+
MenuBody,
|
|
658
|
+
MenuCheckboxItem,
|
|
659
|
+
MenuGroup,
|
|
660
|
+
MenuItem,
|
|
661
|
+
MenuLabel,
|
|
662
|
+
MenuRadioGroup,
|
|
663
|
+
MenuRadioItem,
|
|
664
|
+
MenuRoot,
|
|
665
|
+
MenuSeparator,
|
|
666
|
+
MenuSub,
|
|
667
|
+
MenuSubTrigger,
|
|
668
|
+
MenuTrigger,
|
|
669
|
+
MenubarMenu,
|
|
670
|
+
MenubarRoot,
|
|
671
|
+
MenubarTrigger,
|
|
672
|
+
NavigationMenuBody,
|
|
673
|
+
NavigationMenuItem,
|
|
674
|
+
NavigationMenuLink,
|
|
675
|
+
NavigationMenuList,
|
|
676
|
+
NavigationMenuRoot,
|
|
677
|
+
NavigationMenuTrigger,
|
|
678
|
+
PaginationContent,
|
|
679
|
+
PaginationItem,
|
|
680
|
+
PaginationNext,
|
|
681
|
+
PaginationPrevious,
|
|
682
|
+
PaginationRoot,
|
|
683
|
+
PopoverBody,
|
|
684
|
+
PopoverRoot,
|
|
685
|
+
PopoverTrigger,
|
|
686
|
+
RadioGroupIndicator,
|
|
687
|
+
RadioGroupItem,
|
|
688
|
+
RadioGroupRoot,
|
|
689
|
+
ResizableHandle,
|
|
690
|
+
ResizablePanel,
|
|
691
|
+
ResizablePanelGroup,
|
|
692
|
+
ScrollAreaRoot,
|
|
693
|
+
ScrollAreaScrollbar,
|
|
694
|
+
ScrollAreaViewport,
|
|
695
|
+
SelectGroup,
|
|
696
|
+
SelectGroupLabel,
|
|
697
|
+
SelectLabel,
|
|
698
|
+
SelectList,
|
|
699
|
+
SelectOption,
|
|
700
|
+
SelectRoot,
|
|
701
|
+
SelectSeparator,
|
|
702
|
+
SelectTrigger,
|
|
703
|
+
SelectValue,
|
|
704
|
+
SheetBody,
|
|
705
|
+
SheetClose,
|
|
706
|
+
SheetDescription,
|
|
707
|
+
SheetFooter,
|
|
708
|
+
SheetHeader,
|
|
709
|
+
SheetOverlay,
|
|
710
|
+
SheetRoot,
|
|
711
|
+
SheetTitle,
|
|
712
|
+
SheetTrigger,
|
|
713
|
+
SidebarBody,
|
|
714
|
+
SidebarFooter,
|
|
715
|
+
SidebarHeader,
|
|
716
|
+
SidebarItem,
|
|
717
|
+
SidebarRoot,
|
|
718
|
+
SidebarTrigger,
|
|
719
|
+
SkeletonBox,
|
|
720
|
+
SkeletonRoot,
|
|
721
|
+
SliderRange,
|
|
722
|
+
SliderRoot,
|
|
723
|
+
SliderThumb,
|
|
724
|
+
SliderTrack,
|
|
725
|
+
TableBody,
|
|
726
|
+
TableCaption,
|
|
727
|
+
TableCell,
|
|
728
|
+
TableHead,
|
|
729
|
+
TableHeader,
|
|
730
|
+
TableRoot,
|
|
731
|
+
TableRow,
|
|
732
|
+
TableRowHeader,
|
|
733
|
+
TableRowSelect,
|
|
734
|
+
TableSelectAll,
|
|
735
|
+
TabsList,
|
|
736
|
+
TabsPanel,
|
|
737
|
+
TabsRoot,
|
|
738
|
+
TabsTab,
|
|
739
|
+
ToastAction,
|
|
740
|
+
ToastClose,
|
|
741
|
+
ToastDescription,
|
|
742
|
+
ToastRegion,
|
|
743
|
+
ToastRoot,
|
|
744
|
+
ToastTitle,
|
|
745
|
+
ToggleGroupItem,
|
|
746
|
+
ToggleGroupRoot,
|
|
747
|
+
TooltipBody,
|
|
748
|
+
TooltipProvider,
|
|
749
|
+
TooltipRoot,
|
|
750
|
+
TooltipTrigger,
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* The five that are one component rather than a namespace of parts.
|
|
755
|
+
*
|
|
756
|
+
* Each takes `render`, so the control or line a design system already has — a
|
|
757
|
+
* `<div>` with a knob drawn in it, somebody's `<Pressable>`, a presentational
|
|
758
|
+
* meter shell — keeps the role, state, keys and attributes while being their
|
|
759
|
+
* element. See the module headers and the table in `packages/ui/ui.test.js`.
|
|
760
|
+
*/
|
|
761
|
+
export { Checkbox, Progress, Separator, Switch, Toggle };
|
|
260
762
|
|
|
261
763
|
/**
|
|
262
764
|
* Queueing a notification, from anywhere.
|
|
@@ -271,6 +773,75 @@ export { Checkbox, Progress, Switch, Toggle };
|
|
|
271
773
|
*/
|
|
272
774
|
export { dismissAllToasts, dismissToast, toast, updateToast };
|
|
273
775
|
|
|
776
|
+
/**
|
|
777
|
+
* The interactions every part here is made of, for a control of the caller's own.
|
|
778
|
+
*
|
|
779
|
+
* `usePress` is a press from a pointer, a key or assistive technology, with one
|
|
780
|
+
* set of events; `useHover` is a mouse's and a pen's and never a finger's;
|
|
781
|
+
* `useFocusRing` draws a ring for keyboard focus and not for pointer focus;
|
|
782
|
+
* `useLongPress`, `useMove` and `useKeyboard` are the rest; `mergeProps` puts
|
|
783
|
+
* several of them on one element. `interactions.js` says what each one gets
|
|
784
|
+
* right, and where it differs from React Aria's, at length.
|
|
785
|
+
*
|
|
786
|
+
* const { isPressed, pressProps } = usePress({ onPress: save });
|
|
787
|
+
* const { focusProps, isFocusVisible } = useFocusRing();
|
|
788
|
+
* <div {...mergeProps(pressProps, focusProps)} role="button" tabIndex={0} />
|
|
789
|
+
*
|
|
790
|
+
* Hooks rather than parts, and the one module here that exports them: its rules
|
|
791
|
+
* are about input devices rather than about markup this package builds, so a
|
|
792
|
+
* control written with them is the same control a part is rather than a weaker
|
|
793
|
+
* copy of one.
|
|
794
|
+
*/
|
|
795
|
+
export {
|
|
796
|
+
getInteractionModality,
|
|
797
|
+
mergeProps,
|
|
798
|
+
useFocusRing,
|
|
799
|
+
useFocusVisible,
|
|
800
|
+
useHover,
|
|
801
|
+
useInteractionModality,
|
|
802
|
+
useInteractOutside,
|
|
803
|
+
useKeyboard,
|
|
804
|
+
useLongPress,
|
|
805
|
+
useMove,
|
|
806
|
+
usePress,
|
|
807
|
+
} from "./interactions.js";
|
|
808
|
+
export type {
|
|
809
|
+
FocusRingOptions,
|
|
810
|
+
FocusRingProps,
|
|
811
|
+
FocusRingResult,
|
|
812
|
+
FocusVisibleResult,
|
|
813
|
+
HoverEvent,
|
|
814
|
+
HoverOptions,
|
|
815
|
+
HoverProps,
|
|
816
|
+
HoverResult,
|
|
817
|
+
InteractionEvent,
|
|
818
|
+
InteractionProps,
|
|
819
|
+
InteractOutsideOptions,
|
|
820
|
+
InteractOutsideRef,
|
|
821
|
+
KeyboardInteraction,
|
|
822
|
+
KeyboardOptions,
|
|
823
|
+
KeyboardProps,
|
|
824
|
+
KeyboardResult,
|
|
825
|
+
LongPressEvent,
|
|
826
|
+
LongPressOptions,
|
|
827
|
+
LongPressProps,
|
|
828
|
+
LongPressResult,
|
|
829
|
+
Modality,
|
|
830
|
+
MoveEndEvent,
|
|
831
|
+
MoveMoveEvent,
|
|
832
|
+
MoveOptions,
|
|
833
|
+
MovePointerType,
|
|
834
|
+
MoveProps,
|
|
835
|
+
MoveResult,
|
|
836
|
+
MoveStartEvent,
|
|
837
|
+
PhysicalPointer,
|
|
838
|
+
PointerType,
|
|
839
|
+
PressEvent,
|
|
840
|
+
PressOptions,
|
|
841
|
+
PressProps,
|
|
842
|
+
PressResult,
|
|
843
|
+
} from "./interactions.js";
|
|
844
|
+
|
|
274
845
|
/**
|
|
275
846
|
* An accessible form field.
|
|
276
847
|
*
|
|
@@ -278,14 +849,31 @@ export { dismissAllToasts, dismissToast, toast, updateToast };
|
|
|
278
849
|
* <Field.Label>Email</Field.Label>
|
|
279
850
|
* <Field.Control render={(props) => <input type="email" {...props} />} />
|
|
280
851
|
* <Field.Description>We will not share it.</Field.Description>
|
|
852
|
+
* <Field.Status>{saving ? "Saving…" : ""}</Field.Status>
|
|
281
853
|
* <Field.Error>{error}</Field.Error>
|
|
282
854
|
* </Field.Root>
|
|
855
|
+
*
|
|
856
|
+
* Inside a form, `field` replaces the hand-written `invalid`: the form says
|
|
857
|
+
* whether the field is wrong and what the message is, and the field composes
|
|
858
|
+
* every `aria-*` from that in one place. It also marks the control busy during
|
|
859
|
+
* submit, validation, or async default loading. `@uniflowed/form`'s
|
|
860
|
+
* `useFieldSource` is what produces one, and `field.js`'s header says why the
|
|
861
|
+
* hook lives there rather than here.
|
|
862
|
+
*
|
|
863
|
+
* const email = useFieldSource(form, "email", { required: "We need one" });
|
|
864
|
+
* <Field.Root field={email}>…<Field.Error /></Field.Root>
|
|
865
|
+
*
|
|
866
|
+
* `group` is for a set with no single control to point a `<label for>` at — a
|
|
867
|
+
* radio group, a checkbox group, three selects making a date. The root becomes
|
|
868
|
+
* `role="group"` named by the label, and the description and the error describe
|
|
869
|
+
* the set.
|
|
283
870
|
*/
|
|
284
871
|
export const Field = {
|
|
285
872
|
Root: FieldRoot,
|
|
286
873
|
Label: FieldLabel,
|
|
287
874
|
Control: FieldControl,
|
|
288
875
|
Description: FieldDescription,
|
|
876
|
+
Status: FieldStatus,
|
|
289
877
|
Error: FieldError,
|
|
290
878
|
};
|
|
291
879
|
|
|
@@ -303,6 +891,11 @@ export const Field = {
|
|
|
303
891
|
* <Tabs.Panel value="one">…</Tabs.Panel>
|
|
304
892
|
* <Tabs.Panel value="two">…</Tabs.Panel>
|
|
305
893
|
* </Tabs.Root>
|
|
894
|
+
*
|
|
895
|
+
* Every part takes `render`, so a tab that is also a route — `<Tabs.Tab
|
|
896
|
+
* render={(props) => <a href="#billing" {...props} />}>` — is still a tab, with
|
|
897
|
+
* the roving tab stop and the `aria-controls` a tab has. `Tabs.List`'s
|
|
898
|
+
* `renders* Tabs.Tab` is unaffected, because it is the *part* it constrains.
|
|
306
899
|
*/
|
|
307
900
|
export const Tabs = {
|
|
308
901
|
Root: TabsRoot,
|
|
@@ -435,6 +1028,12 @@ export const ToggleGroup = {
|
|
|
435
1028
|
* </Dialog.Footer>
|
|
436
1029
|
* </Dialog.Body>
|
|
437
1030
|
* </Dialog.Root>
|
|
1031
|
+
*
|
|
1032
|
+
* Every part takes `render`. `Dialog.Title` is an `<h2>` by default and the
|
|
1033
|
+
* level is a fact about the page around it rather than about the dialog, so
|
|
1034
|
+
* `render={(props) => <h3 {...props} />}` is how a caller says which — without
|
|
1035
|
+
* losing the id `aria-labelledby` points at. `AlertDialog`, `Sheet` and
|
|
1036
|
+
* `Drawer` are made of these parts and pass `render` straight through.
|
|
438
1037
|
*/
|
|
439
1038
|
export const Dialog = {
|
|
440
1039
|
Root: DialogRoot,
|
|
@@ -448,6 +1047,199 @@ export const Dialog = {
|
|
|
448
1047
|
Close: DialogClose,
|
|
449
1048
|
};
|
|
450
1049
|
|
|
1050
|
+
/**
|
|
1051
|
+
* The confirmation: modal, announced as an alert, and not dismissible by a
|
|
1052
|
+
* press beside it.
|
|
1053
|
+
*
|
|
1054
|
+
* Focus lands on `Cancel` rather than on the first thing in the dialog, and the
|
|
1055
|
+
* description is required — `role="alertdialog"` exists to announce one, so an
|
|
1056
|
+
* alert dialog without it interrupts the reader to say nothing.
|
|
1057
|
+
*
|
|
1058
|
+
* <AlertDialog.Root>
|
|
1059
|
+
* <AlertDialog.Trigger>Delete</AlertDialog.Trigger>
|
|
1060
|
+
* <AlertDialog.Overlay />
|
|
1061
|
+
* <AlertDialog.Body>
|
|
1062
|
+
* <AlertDialog.Header>
|
|
1063
|
+
* <AlertDialog.Title>Delete this project?</AlertDialog.Title>
|
|
1064
|
+
* <AlertDialog.Description>This cannot be undone.</AlertDialog.Description>
|
|
1065
|
+
* </AlertDialog.Header>
|
|
1066
|
+
* <AlertDialog.Footer>
|
|
1067
|
+
* <AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
|
|
1068
|
+
* <AlertDialog.Action onClick={remove}>Delete</AlertDialog.Action>
|
|
1069
|
+
* </AlertDialog.Footer>
|
|
1070
|
+
* </AlertDialog.Body>
|
|
1071
|
+
* </AlertDialog.Root>
|
|
1072
|
+
*/
|
|
1073
|
+
export const AlertDialog = {
|
|
1074
|
+
Root: AlertDialogRoot,
|
|
1075
|
+
Trigger: AlertDialogTrigger,
|
|
1076
|
+
Overlay: AlertDialogOverlay,
|
|
1077
|
+
Body: AlertDialogBody,
|
|
1078
|
+
Header: AlertDialogHeader,
|
|
1079
|
+
Footer: AlertDialogFooter,
|
|
1080
|
+
Title: AlertDialogTitle,
|
|
1081
|
+
Description: AlertDialogDescription,
|
|
1082
|
+
Action: AlertDialogAction,
|
|
1083
|
+
Cancel: AlertDialogCancel,
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* A modal dialog attached to an edge of the viewport.
|
|
1088
|
+
*
|
|
1089
|
+
* `side` is a union rather than a class name, and every part reports it as
|
|
1090
|
+
* `data-side` — the same attribute `Popover.Body` writes, so one stylesheet
|
|
1091
|
+
* rule covers every overlay in this package.
|
|
1092
|
+
*
|
|
1093
|
+
* <Sheet.Root side="left">
|
|
1094
|
+
* <Sheet.Trigger>Filters</Sheet.Trigger>
|
|
1095
|
+
* <Sheet.Overlay />
|
|
1096
|
+
* <Sheet.Body>
|
|
1097
|
+
* <Sheet.Title>Filters</Sheet.Title>
|
|
1098
|
+
* <Sheet.Close>Done</Sheet.Close>
|
|
1099
|
+
* </Sheet.Body>
|
|
1100
|
+
* </Sheet.Root>
|
|
1101
|
+
*/
|
|
1102
|
+
export const Sheet = {
|
|
1103
|
+
Root: SheetRoot,
|
|
1104
|
+
Trigger: SheetTrigger,
|
|
1105
|
+
Overlay: SheetOverlay,
|
|
1106
|
+
Body: SheetBody,
|
|
1107
|
+
Header: SheetHeader,
|
|
1108
|
+
Footer: SheetFooter,
|
|
1109
|
+
Title: SheetTitle,
|
|
1110
|
+
Description: SheetDescription,
|
|
1111
|
+
Close: SheetClose,
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* The sheet you can drag away, with the keyboard that can do everything the
|
|
1116
|
+
* drag can.
|
|
1117
|
+
*
|
|
1118
|
+
* `Drawer.Handle` is a `role="slider"` over the snap points: the arrow keys
|
|
1119
|
+
* move between them, `Home` and `End` go to the ends, and the closing key at
|
|
1120
|
+
* the smallest snap point closes it. WCAG 2.5.7 also wants a single-pointer
|
|
1121
|
+
* alternative, so a drawer with a handle and no `Drawer.Close` raises.
|
|
1122
|
+
*
|
|
1123
|
+
* <Drawer.Root side="bottom" snapPoints={[0.4, 1]}>
|
|
1124
|
+
* <Drawer.Trigger>Details</Drawer.Trigger>
|
|
1125
|
+
* <Drawer.Overlay />
|
|
1126
|
+
* <Drawer.Body>
|
|
1127
|
+
* <Drawer.Handle label="Resize the details" />
|
|
1128
|
+
* <Drawer.Title>Details</Drawer.Title>
|
|
1129
|
+
* <Drawer.Close>Close</Drawer.Close>
|
|
1130
|
+
* </Drawer.Body>
|
|
1131
|
+
* </Drawer.Root>
|
|
1132
|
+
*/
|
|
1133
|
+
export const Drawer = {
|
|
1134
|
+
Root: DrawerRoot,
|
|
1135
|
+
Trigger: DrawerTrigger,
|
|
1136
|
+
Overlay: DrawerOverlay,
|
|
1137
|
+
Body: DrawerBody,
|
|
1138
|
+
Handle: DrawerHandle,
|
|
1139
|
+
Header: DrawerHeader,
|
|
1140
|
+
Footer: DrawerFooter,
|
|
1141
|
+
Title: DrawerTitle,
|
|
1142
|
+
Description: DrawerDescription,
|
|
1143
|
+
Close: DrawerClose,
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Navigation beside the page, which becomes a modal sheet on a narrow one.
|
|
1148
|
+
*
|
|
1149
|
+
* `Sidebar.Item` takes a `label` and keeps it as the button's accessible name
|
|
1150
|
+
* the moment the sidebar collapses to icons — which is the whole reason a
|
|
1151
|
+
* collapsing sidebar is a component rather than a class.
|
|
1152
|
+
*
|
|
1153
|
+
* <Sidebar.Root defaultOpen={fromCookie}>
|
|
1154
|
+
* <Sidebar.Trigger>Menu</Sidebar.Trigger>
|
|
1155
|
+
* <Sidebar.Body label="Main">
|
|
1156
|
+
* <Sidebar.Item label="Settings">
|
|
1157
|
+
* <Gear /> Settings
|
|
1158
|
+
* </Sidebar.Item>
|
|
1159
|
+
* </Sidebar.Body>
|
|
1160
|
+
* </Sidebar.Root>
|
|
1161
|
+
*/
|
|
1162
|
+
export const Sidebar = {
|
|
1163
|
+
Root: SidebarRoot,
|
|
1164
|
+
Trigger: SidebarTrigger,
|
|
1165
|
+
Header: SidebarHeader,
|
|
1166
|
+
Body: SidebarBody,
|
|
1167
|
+
Footer: SidebarFooter,
|
|
1168
|
+
Item: SidebarItem,
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Slides, one at a time, that a reader can stop and cannot fall into.
|
|
1173
|
+
*
|
|
1174
|
+
* `Carousel.Pause` is WCAG 2.2.2's mechanism and must be the first focusable
|
|
1175
|
+
* thing inside the carousel; the slides that are not showing are `inert`, so
|
|
1176
|
+
* `Tab` cannot reach a link nobody can see.
|
|
1177
|
+
*
|
|
1178
|
+
* <Carousel.Root autoplay={5000} count={3} label="Featured">
|
|
1179
|
+
* <Carousel.Pause />
|
|
1180
|
+
* <Carousel.Content>
|
|
1181
|
+
* <Carousel.Item index={0}>…</Carousel.Item>
|
|
1182
|
+
* <Carousel.Item index={1}>…</Carousel.Item>
|
|
1183
|
+
* <Carousel.Item index={2}>…</Carousel.Item>
|
|
1184
|
+
* </Carousel.Content>
|
|
1185
|
+
* <Carousel.Previous />
|
|
1186
|
+
* <Carousel.Next />
|
|
1187
|
+
* </Carousel.Root>
|
|
1188
|
+
*/
|
|
1189
|
+
export const Carousel = {
|
|
1190
|
+
Root: CarouselRoot,
|
|
1191
|
+
Content: CarouselContent,
|
|
1192
|
+
Item: CarouselItem,
|
|
1193
|
+
Pause: CarouselPause,
|
|
1194
|
+
Previous: CarouselPrevious,
|
|
1195
|
+
Next: CarouselNext,
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* An overflow container a keyboard can actually scroll.
|
|
1200
|
+
*
|
|
1201
|
+
* `role="region"`, a name and `tabindex="0"`, because a scroll container is not
|
|
1202
|
+
* focusable in every browser and one that is not is one a keyboard reader can
|
|
1203
|
+
* see the top of and nothing else.
|
|
1204
|
+
*
|
|
1205
|
+
* <ScrollArea.Root label="Release notes">
|
|
1206
|
+
* <ScrollArea.Viewport>…</ScrollArea.Viewport>
|
|
1207
|
+
* <ScrollArea.Scrollbar orientation="vertical" />
|
|
1208
|
+
* </ScrollArea.Root>
|
|
1209
|
+
*/
|
|
1210
|
+
export const ScrollArea = {
|
|
1211
|
+
Root: ScrollAreaRoot,
|
|
1212
|
+
Viewport: ScrollAreaViewport,
|
|
1213
|
+
Scrollbar: ScrollAreaScrollbar,
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* A one-time code: six boxes drawn over one real `<input>`.
|
|
1218
|
+
*
|
|
1219
|
+
* One input, so `autocomplete="one-time-code"` works, a paste fills every box,
|
|
1220
|
+
* and a form submits one value under one name.
|
|
1221
|
+
*
|
|
1222
|
+
* <InputOtp.Root label="One-time code" length={6} name="code">
|
|
1223
|
+
* <InputOtp.Group>
|
|
1224
|
+
* <InputOtp.Slot index={0} />
|
|
1225
|
+
* <InputOtp.Slot index={1} />
|
|
1226
|
+
* <InputOtp.Slot index={2} />
|
|
1227
|
+
* </InputOtp.Group>
|
|
1228
|
+
* <InputOtp.Separator>-</InputOtp.Separator>
|
|
1229
|
+
* <InputOtp.Group>
|
|
1230
|
+
* <InputOtp.Slot index={3} />
|
|
1231
|
+
* <InputOtp.Slot index={4} />
|
|
1232
|
+
* <InputOtp.Slot index={5} />
|
|
1233
|
+
* </InputOtp.Group>
|
|
1234
|
+
* </InputOtp.Root>
|
|
1235
|
+
*/
|
|
1236
|
+
export const InputOtp = {
|
|
1237
|
+
Root: InputOtpRoot,
|
|
1238
|
+
Group: InputOtpGroup,
|
|
1239
|
+
Slot: InputOtpSlot,
|
|
1240
|
+
Separator: InputOtpSeparator,
|
|
1241
|
+
};
|
|
1242
|
+
|
|
451
1243
|
/**
|
|
452
1244
|
* A menu, with the keyboard map every native menu has had for thirty years.
|
|
453
1245
|
*
|
|
@@ -467,12 +1259,94 @@ export const Dialog = {
|
|
|
467
1259
|
* </Menu.Sub>
|
|
468
1260
|
* </Menu.Body>
|
|
469
1261
|
* </Menu.Root>
|
|
1262
|
+
*
|
|
1263
|
+
* Every part takes `render`, which is what makes a menu of links possible — and
|
|
1264
|
+
* a menu of links is the most ordinary menu there is:
|
|
1265
|
+
*
|
|
1266
|
+
* <Menu.Item render={(props) => <a href="/settings" {...props} />}>
|
|
1267
|
+
* Settings
|
|
1268
|
+
* </Menu.Item>
|
|
1269
|
+
*
|
|
1270
|
+
* The `<a>` keeps the middle click, the context menu and the status bar; the
|
|
1271
|
+
* item keeps the role, the id, the roving tab stop and the press that closes
|
|
1272
|
+
* the tree. See the module header for why that is the answer to "no copy step".
|
|
470
1273
|
*/
|
|
471
1274
|
export const Menu = {
|
|
472
1275
|
Root: MenuRoot,
|
|
473
1276
|
Trigger: MenuTrigger,
|
|
474
1277
|
Body: MenuBody,
|
|
475
1278
|
Item: MenuItem,
|
|
1279
|
+
CheckboxItem: MenuCheckboxItem,
|
|
1280
|
+
RadioGroup: MenuRadioGroup,
|
|
1281
|
+
RadioItem: MenuRadioItem,
|
|
1282
|
+
Separator: MenuSeparator,
|
|
1283
|
+
Group: MenuGroup,
|
|
1284
|
+
Label: MenuLabel,
|
|
1285
|
+
Sub: MenuSub,
|
|
1286
|
+
SubTrigger: MenuSubTrigger,
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* The same menu, opened by the right button — and by the keyboard.
|
|
1291
|
+
*
|
|
1292
|
+
* `Shift+F10`, the `ContextMenu` key and a long press all open it, because a
|
|
1293
|
+
* command reachable only by right-click is reachable only by a pointer, which
|
|
1294
|
+
* is a WCAG 2.1.1 failure. `context-menu.js` says why the trigger is in the tab
|
|
1295
|
+
* order and when to take it out again.
|
|
1296
|
+
*
|
|
1297
|
+
* The body needs an `aria-label`: its trigger is a table row or a canvas rather
|
|
1298
|
+
* than a short name, so unlike `Menu.Body` it cannot name itself after one.
|
|
1299
|
+
*
|
|
1300
|
+
* <ContextMenu.Root>
|
|
1301
|
+
* <ContextMenu.Trigger>{row}</ContextMenu.Trigger>
|
|
1302
|
+
* <ContextMenu.Body aria-label="Row actions">
|
|
1303
|
+
* <ContextMenu.Item onSelect={rename}>Rename…</ContextMenu.Item>
|
|
1304
|
+
* <ContextMenu.CheckboxItem defaultChecked>Show hidden</ContextMenu.CheckboxItem>
|
|
1305
|
+
* </ContextMenu.Body>
|
|
1306
|
+
* </ContextMenu.Root>
|
|
1307
|
+
*/
|
|
1308
|
+
export const ContextMenu = {
|
|
1309
|
+
Root: ContextMenuRoot,
|
|
1310
|
+
Trigger: ContextMenuTrigger,
|
|
1311
|
+
Body: MenuBody,
|
|
1312
|
+
Item: MenuItem,
|
|
1313
|
+
CheckboxItem: MenuCheckboxItem,
|
|
1314
|
+
RadioGroup: MenuRadioGroup,
|
|
1315
|
+
RadioItem: MenuRadioItem,
|
|
1316
|
+
Separator: MenuSeparator,
|
|
1317
|
+
Group: MenuGroup,
|
|
1318
|
+
Label: MenuLabel,
|
|
1319
|
+
Sub: MenuSub,
|
|
1320
|
+
SubTrigger: MenuSubTrigger,
|
|
1321
|
+
};
|
|
1322
|
+
|
|
1323
|
+
/**
|
|
1324
|
+
* A row of menus that behaves as one control: File, Edit, View.
|
|
1325
|
+
*
|
|
1326
|
+
* One tab stop for the whole bar, arrows between the menus, and — the part that
|
|
1327
|
+
* is always missing — arrows *while a menu is open* that close it and open the
|
|
1328
|
+
* next one, so a reader walks File → Edit → View without pressing Escape.
|
|
1329
|
+
*
|
|
1330
|
+
* <Menubar.Root aria-label="Main">
|
|
1331
|
+
* <Menubar.Menu value="file">
|
|
1332
|
+
* <Menubar.Trigger>File</Menubar.Trigger>
|
|
1333
|
+
* <Menubar.Body>
|
|
1334
|
+
* <Menubar.Item onSelect={open}>Open…</Menubar.Item>
|
|
1335
|
+
* </Menubar.Body>
|
|
1336
|
+
* </Menubar.Menu>
|
|
1337
|
+
* </Menubar.Root>
|
|
1338
|
+
*/
|
|
1339
|
+
export const Menubar = {
|
|
1340
|
+
Root: MenubarRoot,
|
|
1341
|
+
Menu: MenubarMenu,
|
|
1342
|
+
Trigger: MenubarTrigger,
|
|
1343
|
+
// `Menu.Body` itself: a bar's menu is a root menu, and `menubar.js`'s header
|
|
1344
|
+
// says why a wrapper with the same defaults would be a second place to drift.
|
|
1345
|
+
Body: MenuBody,
|
|
1346
|
+
Item: MenuItem,
|
|
1347
|
+
CheckboxItem: MenuCheckboxItem,
|
|
1348
|
+
RadioGroup: MenuRadioGroup,
|
|
1349
|
+
RadioItem: MenuRadioItem,
|
|
476
1350
|
Separator: MenuSeparator,
|
|
477
1351
|
Group: MenuGroup,
|
|
478
1352
|
Label: MenuLabel,
|
|
@@ -489,13 +1363,19 @@ export const Menu = {
|
|
|
489
1363
|
* <Combobox.Label>Country</Combobox.Label>
|
|
490
1364
|
* <Combobox.Input />
|
|
491
1365
|
* <Combobox.List>
|
|
492
|
-
*
|
|
493
|
-
* <Combobox.
|
|
494
|
-
*
|
|
1366
|
+
* <Combobox.Group>
|
|
1367
|
+
* <Combobox.GroupLabel>Europe</Combobox.GroupLabel>
|
|
1368
|
+
* {european.map((each) => (
|
|
1369
|
+
* <Combobox.Option key={each} value={each}>{each}</Combobox.Option>
|
|
1370
|
+
* ))}
|
|
1371
|
+
* </Combobox.Group>
|
|
495
1372
|
* </Combobox.List>
|
|
496
1373
|
* <Combobox.Empty>No matches.</Combobox.Empty>
|
|
497
1374
|
* <Combobox.Status />
|
|
498
1375
|
* </Combobox.Root>
|
|
1376
|
+
*
|
|
1377
|
+
* `Combobox.Label` names the field and `Combobox.GroupLabel` names a group of
|
|
1378
|
+
* options, which is why there are two of them.
|
|
499
1379
|
*/
|
|
500
1380
|
export const Combobox = {
|
|
501
1381
|
Root: ComboboxRoot,
|
|
@@ -503,6 +1383,8 @@ export const Combobox = {
|
|
|
503
1383
|
Input: ComboboxInput,
|
|
504
1384
|
List: ComboboxList,
|
|
505
1385
|
Option: ComboboxOption,
|
|
1386
|
+
Group: ComboboxGroup,
|
|
1387
|
+
GroupLabel: ComboboxGroupLabel,
|
|
506
1388
|
Empty: ComboboxEmpty,
|
|
507
1389
|
Status: ComboboxStatus,
|
|
508
1390
|
};
|
|
@@ -571,6 +1453,64 @@ export const Popover = {
|
|
|
571
1453
|
Body: PopoverBody,
|
|
572
1454
|
};
|
|
573
1455
|
|
|
1456
|
+
/**
|
|
1457
|
+
* A month of dates, as one stop in the page's tab order.
|
|
1458
|
+
*
|
|
1459
|
+
* The grid is `role="grid"`, the arrow keys move by a day and by a week, and
|
|
1460
|
+
* `PageUp` and `PageDown` change the month - with `Shift`, the year. Running off
|
|
1461
|
+
* the end of a month shows the next one and lands on its first day, and the
|
|
1462
|
+
* month is announced in a live region when it changes.
|
|
1463
|
+
*
|
|
1464
|
+
* <Calendar.Root defaultValue="2026-10-14" onValueChange={setWhen}>
|
|
1465
|
+
* <Calendar.Previous>Previous month</Calendar.Previous>
|
|
1466
|
+
* <Calendar.Next>Next month</Calendar.Next>
|
|
1467
|
+
* <Calendar.Month />
|
|
1468
|
+
* </Calendar.Root>
|
|
1469
|
+
*
|
|
1470
|
+
* `Calendar.Month` takes a function child when a day needs more than its number
|
|
1471
|
+
* in it - a dot for an appointment, a price for a night - and it is handed the
|
|
1472
|
+
* date and returns a `Calendar.Day`.
|
|
1473
|
+
*
|
|
1474
|
+
* Dates are `@uniflowed/temporal`'s `PlainDate`, or the ISO strings it reads.
|
|
1475
|
+
* `isDateDisabled` marks a day unavailable *without* making it unreachable: it
|
|
1476
|
+
* is `aria-disabled` and the arrow keys still land on it, which is the opposite
|
|
1477
|
+
* of what a disabled menu item does and the only way a reader can find out which
|
|
1478
|
+
* days are unavailable.
|
|
1479
|
+
*/
|
|
1480
|
+
export const Calendar = {
|
|
1481
|
+
Root: CalendarRoot,
|
|
1482
|
+
Previous: CalendarPrevious,
|
|
1483
|
+
Next: CalendarNext,
|
|
1484
|
+
Month: CalendarMonth,
|
|
1485
|
+
Day: CalendarDay,
|
|
1486
|
+
};
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* A field somebody types a date into, and a calendar for the times they would
|
|
1490
|
+
* rather point at one.
|
|
1491
|
+
*
|
|
1492
|
+
* <DatePicker.Root onValueChange={setWhen} value={when}>
|
|
1493
|
+
* <DatePicker.Input aria-label="Arrive on" />
|
|
1494
|
+
* <DatePicker.Trigger>Choose a date</DatePicker.Trigger>
|
|
1495
|
+
* <DatePicker.Calendar>
|
|
1496
|
+
* <Calendar.Previous>Previous month</Calendar.Previous>
|
|
1497
|
+
* <Calendar.Next>Next month</Calendar.Next>
|
|
1498
|
+
* <Calendar.Month />
|
|
1499
|
+
* </DatePicker.Calendar>
|
|
1500
|
+
* </DatePicker.Root>
|
|
1501
|
+
*
|
|
1502
|
+
* The field is the control and the grid is the second way in: `Escape` and a
|
|
1503
|
+
* chosen date both put focus back on the field. `format` and `parse` are ISO
|
|
1504
|
+
* 8601 both ways unless a caller passes their own - `date-picker.js` says why a
|
|
1505
|
+
* locale format is not this package's to guess.
|
|
1506
|
+
*/
|
|
1507
|
+
export const DatePicker = {
|
|
1508
|
+
Root: DatePickerRoot,
|
|
1509
|
+
Input: DatePickerInput,
|
|
1510
|
+
Trigger: DatePickerTrigger,
|
|
1511
|
+
Calendar: DatePickerCalendar,
|
|
1512
|
+
};
|
|
1513
|
+
|
|
574
1514
|
/**
|
|
575
1515
|
* A phrase about a control, on hover and on focus, that WCAG would accept.
|
|
576
1516
|
*
|
|
@@ -744,3 +1684,209 @@ export const Pagination = {
|
|
|
744
1684
|
Previous: PaginationPrevious,
|
|
745
1685
|
Next: PaginationNext,
|
|
746
1686
|
};
|
|
1687
|
+
|
|
1688
|
+
/**
|
|
1689
|
+
* The trail above the page, read as places rather than as punctuation.
|
|
1690
|
+
*
|
|
1691
|
+
* <Breadcrumb.Root>
|
|
1692
|
+
* <Breadcrumb.List>
|
|
1693
|
+
* <Breadcrumb.Item>
|
|
1694
|
+
* <Breadcrumb.Link href="/">Home</Breadcrumb.Link>
|
|
1695
|
+
* </Breadcrumb.Item>
|
|
1696
|
+
* <Breadcrumb.Separator>/</Breadcrumb.Separator>
|
|
1697
|
+
* <Breadcrumb.Item>
|
|
1698
|
+
* <Breadcrumb.Page>Billing</Breadcrumb.Page>
|
|
1699
|
+
* </Breadcrumb.Item>
|
|
1700
|
+
* </Breadcrumb.List>
|
|
1701
|
+
* </Breadcrumb.Root>
|
|
1702
|
+
*
|
|
1703
|
+
* `Pagination`'s shape one door along: a `<nav>` with a name, one
|
|
1704
|
+
* `aria-current="page"`, and the separators out of the accessibility tree so
|
|
1705
|
+
* the trail is not announced as "Home slash Settings slash Billing". The last
|
|
1706
|
+
* crumb is a `Breadcrumb.Page` and not a link, because it is where the reader
|
|
1707
|
+
* already is.
|
|
1708
|
+
*/
|
|
1709
|
+
export const Breadcrumb = {
|
|
1710
|
+
Root: BreadcrumbRoot,
|
|
1711
|
+
List: BreadcrumbList,
|
|
1712
|
+
Item: BreadcrumbItem,
|
|
1713
|
+
Link: BreadcrumbLink,
|
|
1714
|
+
Page: BreadcrumbPage,
|
|
1715
|
+
Separator: BreadcrumbSeparator,
|
|
1716
|
+
};
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* A callout, and the `live` that decides whether anybody is interrupted by it.
|
|
1720
|
+
*
|
|
1721
|
+
* <Alert.Root>
|
|
1722
|
+
* <Alert.Title>Your trial ends on Friday</Alert.Title>
|
|
1723
|
+
* <Alert.Description>Add a card to keep your projects.</Alert.Description>
|
|
1724
|
+
* </Alert.Root>
|
|
1725
|
+
*
|
|
1726
|
+
* {error != null && (
|
|
1727
|
+
* <Alert.Root live>
|
|
1728
|
+
* <Alert.Title>Could not save</Alert.Title>
|
|
1729
|
+
* <Alert.Description>{error}</Alert.Description>
|
|
1730
|
+
* </Alert.Root>
|
|
1731
|
+
* )}
|
|
1732
|
+
*
|
|
1733
|
+
* The first has no role at all: it was there when the page loaded, so a live
|
|
1734
|
+
* region would announce it on every load or never, and neither is what anybody
|
|
1735
|
+
* wanted. The second appeared because something happened, which is what
|
|
1736
|
+
* `role="alert"` is for. `alert.js`'s header says why there is no polite
|
|
1737
|
+
* version of this and why `Toast` is that instead.
|
|
1738
|
+
*/
|
|
1739
|
+
export const Alert = {
|
|
1740
|
+
Root: AlertRoot,
|
|
1741
|
+
Title: AlertTitle,
|
|
1742
|
+
Description: AlertDescription,
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* A picture of a person, and the two states it is not in yet.
|
|
1747
|
+
*
|
|
1748
|
+
* <Avatar.Root>
|
|
1749
|
+
* <Avatar.Image src={person.photo} />
|
|
1750
|
+
* <Avatar.Fallback>{initials(person.name)}</Avatar.Fallback>
|
|
1751
|
+
* </Avatar.Root>
|
|
1752
|
+
*
|
|
1753
|
+
* The fallback is absent while the image is loading and present once it has
|
|
1754
|
+
* failed, held back long enough that a cached image never flashes initials.
|
|
1755
|
+
* `alt` defaults to `""`, because an avatar beside the name it belongs to is
|
|
1756
|
+
* decorative and a component that helpfully puts the name there makes every
|
|
1757
|
+
* screen reader say it twice; pass `alt` where the picture is the only thing
|
|
1758
|
+
* identifying the person.
|
|
1759
|
+
*/
|
|
1760
|
+
export const Avatar = {
|
|
1761
|
+
Root: AvatarRoot,
|
|
1762
|
+
Image: AvatarImage,
|
|
1763
|
+
Fallback: AvatarFallback,
|
|
1764
|
+
};
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* The grey boxes, and the sentence that stops them being an empty page.
|
|
1768
|
+
*
|
|
1769
|
+
* <Skeleton.Root busy={pending}>
|
|
1770
|
+
* {pending ? <Skeleton.Box /> : <Invoices rows={invoices} />}
|
|
1771
|
+
* </Skeleton.Root>
|
|
1772
|
+
*
|
|
1773
|
+
* The boxes are `aria-hidden`, the region is `aria-busy`, and a live region
|
|
1774
|
+
* that was mounted empty for a commit says "Loading" — a skeleton screen is
|
|
1775
|
+
* busy on its first render, so a region rendered with its message already in it
|
|
1776
|
+
* announces nothing at all. Keep the root mounted across the load and toggle
|
|
1777
|
+
* `busy`; unmounting it takes the region away before it can say the wait is
|
|
1778
|
+
* over.
|
|
1779
|
+
*/
|
|
1780
|
+
export const Skeleton = {
|
|
1781
|
+
Root: SkeletonRoot,
|
|
1782
|
+
Box: SkeletonBox,
|
|
1783
|
+
};
|
|
1784
|
+
|
|
1785
|
+
export { I18nProvider, useLocale, useCollator, useFilter } from "./i18n-provider.js";
|
|
1786
|
+
import {
|
|
1787
|
+
NumberFieldRoot,
|
|
1788
|
+
NumberFieldInput,
|
|
1789
|
+
NumberFieldIncrement,
|
|
1790
|
+
NumberFieldDecrement,
|
|
1791
|
+
} from "./number-field.js";
|
|
1792
|
+
export const NumberField = {
|
|
1793
|
+
Root: NumberFieldRoot,
|
|
1794
|
+
Input: NumberFieldInput,
|
|
1795
|
+
Increment: NumberFieldIncrement,
|
|
1796
|
+
Decrement: NumberFieldDecrement,
|
|
1797
|
+
};
|
|
1798
|
+
|
|
1799
|
+
export { ListBox } from "./list-box.js";
|
|
1800
|
+
export { GridList } from "./grid-list.js";
|
|
1801
|
+
export { Tree } from "./tree.js";
|
|
1802
|
+
export { TagGroup } from "./tag-group.js";
|
|
1803
|
+
export type { CollectionItem, CollectionItemState, CollectionProps } from "./list-box.js";
|
|
1804
|
+
|
|
1805
|
+
export type { Drop, DragAndDrop } from "./drag-drop.js";
|
|
1806
|
+
export { useDragAndDrop } from "./drag-drop.js";
|
|
1807
|
+
import {
|
|
1808
|
+
ColorPickerRoot,
|
|
1809
|
+
ColorPickerInput,
|
|
1810
|
+
ColorPickerField,
|
|
1811
|
+
ColorPickerChannel,
|
|
1812
|
+
ColorPickerSwatch,
|
|
1813
|
+
} from "./color-picker.js";
|
|
1814
|
+
export const ColorPicker = {
|
|
1815
|
+
Root: ColorPickerRoot,
|
|
1816
|
+
Input: ColorPickerInput,
|
|
1817
|
+
Field: ColorPickerField,
|
|
1818
|
+
Channel: ColorPickerChannel,
|
|
1819
|
+
Swatch: ColorPickerSwatch,
|
|
1820
|
+
};
|
|
1821
|
+
|
|
1822
|
+
export { DateField } from "./date-field.js";
|
|
1823
|
+
export { TimeField } from "./time-field.js";
|
|
1824
|
+
export type { DateFieldProps } from "./date-field.js";
|
|
1825
|
+
import { RangeCalendarRoot } from "./range-calendar.js";
|
|
1826
|
+
import {
|
|
1827
|
+
DateRangePickerRoot,
|
|
1828
|
+
DateRangePickerStartField,
|
|
1829
|
+
DateRangePickerEndField,
|
|
1830
|
+
DateRangePickerTrigger,
|
|
1831
|
+
DateRangePickerCalendar,
|
|
1832
|
+
} from "./date-range-picker.js";
|
|
1833
|
+
export type { DateRange } from "./range-calendar.js";
|
|
1834
|
+
export const RangeCalendar = {
|
|
1835
|
+
Root: RangeCalendarRoot,
|
|
1836
|
+
Month: CalendarMonth,
|
|
1837
|
+
Day: CalendarDay,
|
|
1838
|
+
Previous: CalendarPrevious,
|
|
1839
|
+
Next: CalendarNext,
|
|
1840
|
+
};
|
|
1841
|
+
export const DateRangePicker = {
|
|
1842
|
+
Root: DateRangePickerRoot,
|
|
1843
|
+
StartField: DateRangePickerStartField,
|
|
1844
|
+
EndField: DateRangePickerEndField,
|
|
1845
|
+
Trigger: DateRangePickerTrigger,
|
|
1846
|
+
Calendar: DateRangePickerCalendar,
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1849
|
+
export {
|
|
1850
|
+
NumberFieldRoot,
|
|
1851
|
+
NumberFieldInput,
|
|
1852
|
+
NumberFieldIncrement,
|
|
1853
|
+
NumberFieldDecrement,
|
|
1854
|
+
parseNumber,
|
|
1855
|
+
} from "./number-field.js";
|
|
1856
|
+
export type { NumberFormatOptions } from "./number-field.js";
|
|
1857
|
+
export {
|
|
1858
|
+
ColorPickerRoot,
|
|
1859
|
+
ColorPickerInput,
|
|
1860
|
+
ColorPickerField,
|
|
1861
|
+
ColorPickerChannel,
|
|
1862
|
+
ColorPickerSwatch,
|
|
1863
|
+
parseColor,
|
|
1864
|
+
} from "./color-picker.js";
|
|
1865
|
+
export { RangeCalendarRoot } from "./range-calendar.js";
|
|
1866
|
+
export {
|
|
1867
|
+
DateRangePickerRoot,
|
|
1868
|
+
DateRangePickerStartField,
|
|
1869
|
+
DateRangePickerEndField,
|
|
1870
|
+
DateRangePickerTrigger,
|
|
1871
|
+
DateRangePickerCalendar,
|
|
1872
|
+
} from "./date-range-picker.js";
|
|
1873
|
+
export type { Locale } from "./i18n-provider.js";
|
|
1874
|
+
export { startsWithLocale } from "./i18n-provider.js";
|
|
1875
|
+
|
|
1876
|
+
/**
|
|
1877
|
+
* For a screen reader and nobody else.
|
|
1878
|
+
*
|
|
1879
|
+
* `VisuallyHidden` is text that stays in the accessibility tree and off the
|
|
1880
|
+
* screen — an icon button's name, a skip link (`focusable`) that appears when a
|
|
1881
|
+
* keyboard reaches it. `announce` says something through one pair of live
|
|
1882
|
+
* regions shared by the whole document, from an event handler, an effect or a
|
|
1883
|
+
* `catch`, without the caller rendering a region of their own:
|
|
1884
|
+
*
|
|
1885
|
+
* announce(`${results.length} results`);
|
|
1886
|
+
* announce("Could not save", { politeness: "assertive" });
|
|
1887
|
+
*
|
|
1888
|
+
* `visually-hidden.js` says why a region rendered with its message is silent,
|
|
1889
|
+
* and why these regions stay readable while a dialog hides the rest of the page.
|
|
1890
|
+
*/
|
|
1891
|
+
export { VisuallyHidden, announce, clearAnnouncements } from "./visually-hidden.js";
|
|
1892
|
+
export type { AnnounceOptions, Politeness } from "./visually-hidden.js";
|