@uniflowed/ui 0.0.0-alpha.2 → 0.0.0-alpha.37

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.
Files changed (58) hide show
  1. package/accordion.js +360 -0
  2. package/alert-dialog.js +282 -0
  3. package/alert.js +142 -0
  4. package/avatar.js +276 -0
  5. package/breadcrumb.js +138 -0
  6. package/calendar.js +550 -0
  7. package/carousel.js +410 -0
  8. package/checkbox.js +264 -0
  9. package/collapsible.js +169 -0
  10. package/combobox.js +728 -0
  11. package/context-menu.js +206 -0
  12. package/date-picker.js +346 -0
  13. package/dialog.js +523 -0
  14. package/drawer.js +490 -0
  15. package/field.js +387 -0
  16. package/hover-card.js +330 -0
  17. package/index.js +1699 -22
  18. package/input-otp.js +218 -0
  19. package/interactions.js +2163 -0
  20. package/internal/anchor.js +565 -0
  21. package/internal/controlled-state.js +65 -0
  22. package/internal/date-grid.js +260 -0
  23. package/internal/disclosure.js +298 -0
  24. package/internal/focus.js +64 -0
  25. package/internal/form-value.js +83 -0
  26. package/internal/hover-intent.js +259 -0
  27. package/internal/menu-tree.js +228 -0
  28. package/internal/merge-props.js +285 -0
  29. package/internal/range.js +147 -0
  30. package/internal/roving-focus.js +430 -0
  31. package/menu.js +823 -0
  32. package/menubar.js +287 -0
  33. package/navigation-menu.js +251 -0
  34. package/package.json +9 -9
  35. package/pagination.js +209 -0
  36. package/popover.js +343 -0
  37. package/progress.js +91 -0
  38. package/radio-group.js +302 -0
  39. package/resizable.js +447 -0
  40. package/scroll-area.js +283 -0
  41. package/select.js +902 -0
  42. package/separator.js +97 -0
  43. package/sheet.js +189 -0
  44. package/sidebar.js +300 -0
  45. package/skeleton.js +159 -0
  46. package/slider.js +405 -0
  47. package/switch.js +81 -0
  48. package/table.js +502 -0
  49. package/tabs.js +289 -0
  50. package/toast.js +592 -0
  51. package/toggle-group.js +283 -0
  52. package/toggle.js +105 -0
  53. package/tooltip.js +400 -0
  54. package/internal/dialog.js +0 -236
  55. package/internal/field.js +0 -161
  56. package/internal/props.js +0 -78
  57. package/internal/switch.js +0 -122
  58. package/internal/tabs.js +0 -270
package/index.js CHANGED
@@ -11,70 +11,888 @@
11
11
  // That part is behaviour, and specifically keyboard and screen-reader
12
12
  // behaviour: a roving `tabindex` so a twelve-tab list does not take twelve Tab
13
13
  // presses to get past, a focus trap that actually cannot be escaped, focus
14
- // restored to whatever opened a dialog, `aria-describedby` pointing only at
15
- // elements that are in the document. None of it is visible in a screenshot and
16
- // all of it is what separates a component from a div that looks like one.
14
+ // restored to whatever opened a dialog, typeahead in a menu, an
15
+ // `aria-activedescendant` that names an option still in the document. None of
16
+ // it is visible in a screenshot and all of it is what separates a component
17
+ // from a `div` that looks like one.
18
+ //
19
+ // Each primitive implements the WAI-ARIA authoring practices pattern for it —
20
+ // the roles, the `aria-*` wiring, the focus management and the whole keyboard
21
+ // map — and each module's header says which interaction it exists to get right
22
+ // and what a naive version breaks.
17
23
  //
18
24
  // # Composition is type-checked
19
25
  //
20
26
  // This is where Flow says something no other type system can. `Tabs.List`
21
27
  // declares `renders* Tabs.Tab`, so a `<button>` in a tab list is a *type
22
28
  // error* — not a review comment, not a runtime warning, not a screen reader
23
- // announcing "button" where the reader expected "tab, 2 of 5". A library
24
- // written in TypeScript can document that constraint; it cannot state it.
29
+ // announcing "button" where the reader expected "tab, 2 of 5". `Menu.Body` and
30
+ // `Combobox.List` state the same constraint about what may appear inside a
31
+ // menu and a listbox, which ARIA also requires and which nothing else checks.
32
+ // A library written in TypeScript can document those constraints; it cannot
33
+ // state them.
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
+ //
134
+ // # Styling is a default, not a dependency
135
+ //
136
+ // Nothing here imports StyleX, and nothing here has a StyleX-shaped type. A
137
+ // consumer styling with plain CSS, CSS Modules or anything else gets exactly
138
+ // the same components with exactly the same behaviour; the design-system layer
139
+ // that adds uf's default styles is built *on* these, not into them.
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
+ //
206
+ // # What these components promise React
207
+ //
208
+ // Nothing here mutates during a render, reads a ref during a render, or depends
209
+ // on a render happening exactly once — so React Compiler's memoization and
210
+ // ordinary `memo` are both safe, and none of it needs an escape hatch. The
211
+ // refs that exist (`triggerRef`, `pendingFocus`, the typeahead buffer) are
212
+ // written only from event handlers and effects, and nothing renders them.
213
+ //
214
+ // Where a component has to learn something the DOM knows — how many options a
215
+ // caller filtered down to, which item the arrow key should move to — it reads
216
+ // the document in an effect or an event handler and, if a render depends on
217
+ // the answer, puts it in state. That is deliberately *not*
218
+ // `useSyncExternalStore`: the DOM is not a store whose value a render may
219
+ // read, and reading layout during a render is the thing that API exists to
220
+ // prevent.
221
+ //
222
+ // One component does use it, and it is the case the API is actually for.
223
+ // `toast("Saved")` is called from an event handler or a `catch`, so the queue
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.
227
+ //
228
+ // # Server and client
229
+ //
230
+ // Every module that manages focus, listens to the document or holds state
231
+ // declares `"use client"`, because each of those needs a browser. That is a
232
+ // property of the components, not of the application: an RSC page may import
233
+ // this package from a Server Component, and only the parts that need the client
234
+ // join the client bundle.
235
+ //
236
+ // # How the package is laid out
237
+ //
238
+ // One root module per primitive, each with its own `exports` subpath, each
239
+ // named after the thing it implements:
240
+ //
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.
264
+ // - `select.js` — the other half of the combobox pattern: the select-only one,
265
+ // with typeahead, option groups and a value a form can submit.
266
+ // - `tabs.js` — the roving `tabindex`, and automatic versus manual activation.
267
+ // - `toast.js` — the live region that was watching before there was anything
268
+ // to announce, and the countdown that stops.
269
+ // - `field.js` — the label, description, error and `aria-invalid` wiring.
270
+ // - `switch.js`, `checkbox.js` and `toggle.js` — the three two-state controls,
271
+ // apart because a reader is told something different by each, and because the
272
+ // third state and the `Enter` key genuinely differ between them.
273
+ // - `radio-group.js` — one answer out of several, and the tab stop an
274
+ // unanswered group would otherwise not have.
275
+ // - `toggle-group.js` — a row of toggle buttons as one control, whose `single`
276
+ // mode is a radio group and is rendered by `radio-group.js` rather than
277
+ // written a second time.
278
+ // - `collapsible.js`, `accordion.js` and `navigation-menu.js` — the disclosure
279
+ // pattern on its own, stacked, and applied to a site's navigation. The third
280
+ // of those exists as much to prevent `role="menu"` from being used for a list
281
+ // of links as to provide anything.
282
+ // - `slider.js`, `resizable.js` and `progress.js` — the three that report a
283
+ // number in a range. A window splitter is a slider wearing a separator's
284
+ // role, which is why it is beside one rather than with the layout.
285
+ // - `table.js` and `pagination.js` — the sort that is announced, the selection
286
+ // that can be mixed, and the rows a page is not showing.
287
+ // - `popover.js`, `tooltip.js` and `hover-card.js` — the three anchored
288
+ // overlays, which are one component seen from three distances: one you
289
+ // click, one you hover, and one you hover and then read. They are three
290
+ // modules because what a reader is told differs in every one — a popover is
291
+ // a dialog that is not modal, a tooltip describes its trigger and may never
292
+ // take focus, a hover card is neither and holds links — and because a flag
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.
306
+ //
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.
311
+ //
312
+ // `internal/` holds ten modules and nothing else, each a rule the primitives
313
+ // must apply identically and a consumer must not be able to apply differently:
314
+ // `merge-props.js` (the caller's props go on first, the component's semantics
315
+ // last), `controlled-state.js` (what "controlled" means here),
316
+ // `roving-focus.js` (how a set of items is found and moved between),
317
+ // `disclosure.js` (how a button says whether a region is showing, and how a
318
+ // closed region stays findable), `form-value.js` (what a `<form>` submits for a
319
+ // control the browser has never heard of), `range.js` (the arithmetic that
320
+ // keeps `aria-valuemin`, `aria-valuemax` and `aria-valuenow` true about each
321
+ // other), `anchor.js` (where an overlay goes, and what it does when it does not
322
+ // fit where it was asked to go), `focus.js` (which elements a reader can reach,
323
+ // which a focus trap and a popover want opposite things from), and
324
+ // `hover-intent.js` (what WCAG requires of content shown on hover or focus,
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
329
+ // bag of helpers: a module that cannot say what it is about does not belong in
330
+ // this package.
25
331
 
332
+ import {
333
+ AccordionContent,
334
+ AccordionHeader,
335
+ AccordionItem,
336
+ AccordionRoot,
337
+ AccordionTrigger,
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";
376
+ import { Checkbox } from "./checkbox.js";
377
+ import { ContextMenuRoot, ContextMenuTrigger } from "./context-menu.js";
378
+ import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from "./collapsible.js";
379
+ import {
380
+ ComboboxEmpty,
381
+ ComboboxGroup,
382
+ ComboboxGroupLabel,
383
+ ComboboxInput,
384
+ ComboboxLabel,
385
+ ComboboxList,
386
+ ComboboxOption,
387
+ ComboboxRoot,
388
+ ComboboxStatus,
389
+ } from "./combobox.js";
390
+ import {
391
+ DatePickerCalendar,
392
+ DatePickerInput,
393
+ DatePickerRoot,
394
+ DatePickerTrigger,
395
+ } from "./date-picker.js";
396
+ import {
397
+ DialogBody,
398
+ DialogClose,
399
+ DialogDescription,
400
+ DialogFooter,
401
+ DialogHeader,
402
+ DialogOverlay,
403
+ DialogRoot,
404
+ DialogTitle,
405
+ DialogTrigger,
406
+ } from "./dialog.js";
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";
26
419
  import {
27
420
  FieldControl,
28
421
  FieldDescription,
29
422
  FieldError,
30
423
  FieldLabel,
31
424
  FieldRoot,
32
- } from "./internal/field.js";
425
+ FieldStatus,
426
+ } from "./field.js";
427
+ import { HoverCardBody, HoverCardRoot, HoverCardTrigger } from "./hover-card.js";
428
+ import { InputOtpGroup, InputOtpRoot, InputOtpSeparator, InputOtpSlot } from "./input-otp.js";
429
+ import {
430
+ MenuBody,
431
+ MenuCheckboxItem,
432
+ MenuGroup,
433
+ MenuItem,
434
+ MenuLabel,
435
+ MenuRadioGroup,
436
+ MenuRadioItem,
437
+ MenuRoot,
438
+ MenuSeparator,
439
+ MenuSub,
440
+ MenuSubTrigger,
441
+ MenuTrigger,
442
+ } from "./menu.js";
443
+ import { MenubarMenu, MenubarRoot, MenubarTrigger } from "./menubar.js";
444
+ import {
445
+ NavigationMenuBody,
446
+ NavigationMenuItem,
447
+ NavigationMenuLink,
448
+ NavigationMenuList,
449
+ NavigationMenuRoot,
450
+ NavigationMenuTrigger,
451
+ } from "./navigation-menu.js";
452
+ import {
453
+ PaginationContent,
454
+ PaginationItem,
455
+ PaginationNext,
456
+ PaginationPrevious,
457
+ PaginationRoot,
458
+ } from "./pagination.js";
459
+ import { PopoverBody, PopoverRoot, PopoverTrigger } from "./popover.js";
460
+ import { Progress } from "./progress.js";
461
+ import { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot } from "./radio-group.js";
462
+ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "./resizable.js";
463
+ import { ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaViewport } from "./scroll-area.js";
464
+ import {
465
+ SelectGroup,
466
+ SelectGroupLabel,
467
+ SelectLabel,
468
+ SelectList,
469
+ SelectOption,
470
+ SelectRoot,
471
+ SelectSeparator,
472
+ SelectTrigger,
473
+ SelectValue,
474
+ } from "./select.js";
475
+ import { Separator } from "./separator.js";
33
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";
496
+ import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from "./slider.js";
497
+ import { Switch } from "./switch.js";
498
+ import {
499
+ TableBody,
500
+ TableCaption,
501
+ TableCell,
502
+ TableHead,
503
+ TableHeader,
504
+ TableRoot,
505
+ TableRow,
506
+ TableRowHeader,
507
+ TableRowSelect,
508
+ TableSelectAll,
509
+ } from "./table.js";
510
+ import { TabsList, TabsPanel, TabsRoot, TabsTab } from "./tabs.js";
511
+ import {
512
+ ToastAction,
513
+ ToastClose,
514
+ ToastDescription,
515
+ ToastRegion,
516
+ ToastRoot,
517
+ ToastTitle,
518
+ dismissAllToasts,
519
+ dismissToast,
520
+ toast,
521
+ updateToast,
522
+ } from "./toast.js";
523
+ import { Toggle } from "./toggle.js";
524
+ import { ToggleGroupItem, ToggleGroupRoot } from "./toggle-group.js";
525
+ import { TooltipBody, TooltipProvider, TooltipRoot, TooltipTrigger } from "./tooltip.js";
526
+
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";
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";
545
+ // Where an anchored overlay opens, for a caller who holds one in a variable or
546
+ // a prop of their own. Unions rather than strings, so `side="botom"` is a type
547
+ // error at the call rather than an overlay that quietly opens somewhere else.
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";
552
+ export type { Sort } from "./table.js";
553
+ export type { Notification, ToastChanges, ToastOptions, Urgency } from "./toast.js";
554
+ export type { ToggleGroupType } from "./toggle-group.js";
555
+
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,
34
626
  DialogClose,
35
- DialogContent,
627
+ DialogDescription,
628
+ DialogFooter,
629
+ DialogHeader,
630
+ DialogOverlay,
36
631
  DialogRoot,
37
632
  DialogTitle,
38
633
  DialogTrigger,
39
- } from "./internal/dialog.js";
40
- import { TabsList, TabsPanel, TabsRoot, TabsTab } from "./internal/tabs.js";
41
- import { Checkbox, Switch } from "./internal/switch.js";
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
+ };
42
752
 
43
- export { Checkbox, Switch };
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 };
44
762
 
45
763
  /**
46
- * An accessible form field.
764
+ * Queueing a notification, from anywhere.
47
765
  *
48
- * `Field.Control` takes a render function rather than rendering an `<input>`,
49
- * because a field wraps a select, a textarea or somebody else's component just
50
- * as often, and each needs the same attributes.
766
+ * Functions rather than a hook, because the places a notification comes from —
767
+ * an event handler, a `catch`, a Server Action's error path — do not have a
768
+ * component to hold state in. `Toast.Region` is what displays them.
769
+ *
770
+ * const id = toast("Uploading…", { duration: null });
771
+ * updateToast(id, { content: "Uploaded", duration: 4000 });
772
+ * toast("Could not save", { urgency: "assertive" });
773
+ */
774
+ export { dismissAllToasts, dismissToast, toast, updateToast };
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
+ useKeyboard,
803
+ useLongPress,
804
+ useMove,
805
+ usePress,
806
+ } from "./interactions.js";
807
+ export type {
808
+ FocusRingOptions,
809
+ FocusRingProps,
810
+ FocusRingResult,
811
+ FocusVisibleResult,
812
+ HoverEvent,
813
+ HoverOptions,
814
+ HoverProps,
815
+ HoverResult,
816
+ InteractionEvent,
817
+ InteractionProps,
818
+ KeyboardInteraction,
819
+ KeyboardOptions,
820
+ KeyboardProps,
821
+ KeyboardResult,
822
+ LongPressEvent,
823
+ LongPressOptions,
824
+ LongPressProps,
825
+ LongPressResult,
826
+ Modality,
827
+ MoveEndEvent,
828
+ MoveMoveEvent,
829
+ MoveOptions,
830
+ MovePointerType,
831
+ MoveProps,
832
+ MoveResult,
833
+ MoveStartEvent,
834
+ PhysicalPointer,
835
+ PointerType,
836
+ PressEvent,
837
+ PressOptions,
838
+ PressProps,
839
+ PressResult,
840
+ } from "./interactions.js";
841
+
842
+ /**
843
+ * An accessible form field.
51
844
  *
52
845
  * <Field.Root invalid={error != null}>
53
846
  * <Field.Label>Email</Field.Label>
54
847
  * <Field.Control render={(props) => <input type="email" {...props} />} />
55
848
  * <Field.Description>We will not share it.</Field.Description>
849
+ * <Field.Status>{saving ? "Saving…" : ""}</Field.Status>
56
850
  * <Field.Error>{error}</Field.Error>
57
851
  * </Field.Root>
852
+ *
853
+ * Inside a form, `field` replaces the hand-written `invalid`: the form says
854
+ * whether the field is wrong and what the message is, and the field composes
855
+ * every `aria-*` from that in one place. It also marks the control busy during
856
+ * submit, validation, or async default loading. `@uniflowed/form`'s
857
+ * `useFieldSource` is what produces one, and `field.js`'s header says why the
858
+ * hook lives there rather than here.
859
+ *
860
+ * const email = useFieldSource(form, "email", { required: "We need one" });
861
+ * <Field.Root field={email}>…<Field.Error /></Field.Root>
862
+ *
863
+ * `group` is for a set with no single control to point a `<label for>` at — a
864
+ * radio group, a checkbox group, three selects making a date. The root becomes
865
+ * `role="group"` named by the label, and the description and the error describe
866
+ * the set.
58
867
  */
59
868
  export const Field = {
60
869
  Root: FieldRoot,
61
870
  Label: FieldLabel,
62
871
  Control: FieldControl,
63
872
  Description: FieldDescription,
873
+ Status: FieldStatus,
64
874
  Error: FieldError,
65
875
  };
66
876
 
67
877
  /**
68
878
  * Tabs, with the arrow-key behaviour the pattern requires.
69
879
  *
880
+ * `activationMode="manual"` moves focus without selecting, for panels that cost
881
+ * something to show.
882
+ *
70
883
  * <Tabs.Root defaultValue="one">
71
- * <Tabs.List>
884
+ * <Tabs.List aria-label="Sections">
72
885
  * <Tabs.Tab value="one">One</Tabs.Tab>
73
886
  * <Tabs.Tab value="two">Two</Tabs.Tab>
74
887
  * </Tabs.List>
75
888
  * <Tabs.Panel value="one">…</Tabs.Panel>
76
889
  * <Tabs.Panel value="two">…</Tabs.Panel>
77
890
  * </Tabs.Root>
891
+ *
892
+ * Every part takes `render`, so a tab that is also a route — `<Tabs.Tab
893
+ * render={(props) => <a href="#billing" {...props} />}>` — is still a tab, with
894
+ * the roving tab stop and the `aria-controls` a tab has. `Tabs.List`'s
895
+ * `renders* Tabs.Tab` is unaffected, because it is the *part* it constrains.
78
896
  */
79
897
  export const Tabs = {
80
898
  Root: TabsRoot,
@@ -83,21 +901,880 @@ export const Tabs = {
83
901
  Panel: TabsPanel,
84
902
  };
85
903
 
904
+ /**
905
+ * A button and the region it shows, with the three attributes that say so.
906
+ *
907
+ * The content stays in the document while it is closed, so the browser's
908
+ * find-in-page can still reach the text in it.
909
+ *
910
+ * <Collapsible.Root>
911
+ * <Collapsible.Trigger>Details</Collapsible.Trigger>
912
+ * <Collapsible.Content>…</Collapsible.Content>
913
+ * </Collapsible.Root>
914
+ */
915
+ export const Collapsible = {
916
+ Root: CollapsibleRoot,
917
+ Trigger: CollapsibleTrigger,
918
+ Content: CollapsibleContent,
919
+ };
920
+
921
+ /**
922
+ * A stack of disclosures that know about each other.
923
+ *
924
+ * `Accordion.Header` takes the heading `level`, because which heading an
925
+ * accordion's sections are depends on where the accordion sits. Each panel is a
926
+ * region named after the header that opens it.
927
+ *
928
+ * <Accordion.Root type="single">
929
+ * <Accordion.Item value="shipping">
930
+ * <Accordion.Header level={3}>
931
+ * <Accordion.Trigger>Shipping</Accordion.Trigger>
932
+ * </Accordion.Header>
933
+ * <Accordion.Content>…</Accordion.Content>
934
+ * </Accordion.Item>
935
+ * </Accordion.Root>
936
+ */
937
+ export const Accordion = {
938
+ Root: AccordionRoot,
939
+ Item: AccordionItem,
940
+ Header: AccordionHeader,
941
+ Trigger: AccordionTrigger,
942
+ Content: AccordionContent,
943
+ };
944
+
945
+ /**
946
+ * Site navigation: a list of links behind buttons, and not a `menu`.
947
+ *
948
+ * <NavigationMenu.Root aria-label="Main">
949
+ * <NavigationMenu.List>
950
+ * <NavigationMenu.Item value="docs">
951
+ * <NavigationMenu.Trigger>Docs</NavigationMenu.Trigger>
952
+ * <NavigationMenu.Body>
953
+ * <NavigationMenu.Link href="/guide">Guide</NavigationMenu.Link>
954
+ * </NavigationMenu.Body>
955
+ * </NavigationMenu.Item>
956
+ * </NavigationMenu.List>
957
+ * </NavigationMenu.Root>
958
+ */
959
+ export const NavigationMenu = {
960
+ Root: NavigationMenuRoot,
961
+ List: NavigationMenuList,
962
+ Item: NavigationMenuItem,
963
+ Trigger: NavigationMenuTrigger,
964
+ Body: NavigationMenuBody,
965
+ Link: NavigationMenuLink,
966
+ };
967
+
968
+ /**
969
+ * One answer out of several, with the arrow keys that check as they move.
970
+ *
971
+ * `Tab` reaches the chosen answer, or the first one while there is none, and
972
+ * leaves the whole group in one press. `name` puts the answer where a form can
973
+ * submit it.
974
+ *
975
+ * <Field.Root>
976
+ * <Field.Label>Plan</Field.Label>
977
+ * <Field.Control
978
+ * render={(props) => (
979
+ * <RadioGroup.Root {...props} defaultValue="free" name="plan">
980
+ * <RadioGroup.Item value="free">
981
+ * Free <RadioGroup.Indicator>●</RadioGroup.Indicator>
982
+ * </RadioGroup.Item>
983
+ * <RadioGroup.Item value="pro">Pro</RadioGroup.Item>
984
+ * </RadioGroup.Root>
985
+ * )}
986
+ * />
987
+ * </Field.Root>
988
+ */
989
+ export const RadioGroup = {
990
+ Root: RadioGroupRoot,
991
+ Item: RadioGroupItem,
992
+ Indicator: RadioGroupIndicator,
993
+ };
994
+
995
+ /**
996
+ * A row of toggle buttons that behaves as one control.
997
+ *
998
+ * `type="multiple"` is a group of toggle buttons, any number of them pressed.
999
+ * `type="single"` is a radio group drawn as segments, and is rendered by
1000
+ * `RadioGroup` rather than written a second time.
1001
+ *
1002
+ * <ToggleGroup.Root aria-label="Formatting" type="multiple">
1003
+ * <ToggleGroup.Item value="bold">B</ToggleGroup.Item>
1004
+ * <ToggleGroup.Item value="italic">I</ToggleGroup.Item>
1005
+ * </ToggleGroup.Root>
1006
+ */
1007
+ export const ToggleGroup = {
1008
+ Root: ToggleGroupRoot,
1009
+ Item: ToggleGroupItem,
1010
+ };
1011
+
86
1012
  /**
87
1013
  * A modal dialog: focus moved in, kept in, and given back.
88
1014
  *
89
1015
  * <Dialog.Root>
90
- * <Dialog.Trigger>Open</Dialog.Trigger>
91
- * <Dialog.Content>
92
- * <Dialog.Title>Are you sure?</Dialog.Title>
93
- * <Dialog.Close>Cancel</Dialog.Close>
94
- * </Dialog.Content>
1016
+ * <Dialog.Trigger>Delete</Dialog.Trigger>
1017
+ * <Dialog.Overlay />
1018
+ * <Dialog.Body>
1019
+ * <Dialog.Header>
1020
+ * <Dialog.Title>Delete this project?</Dialog.Title>
1021
+ * <Dialog.Description>This cannot be undone.</Dialog.Description>
1022
+ * </Dialog.Header>
1023
+ * <Dialog.Footer>
1024
+ * <Dialog.Close>Cancel</Dialog.Close>
1025
+ * </Dialog.Footer>
1026
+ * </Dialog.Body>
95
1027
  * </Dialog.Root>
1028
+ *
1029
+ * Every part takes `render`. `Dialog.Title` is an `<h2>` by default and the
1030
+ * level is a fact about the page around it rather than about the dialog, so
1031
+ * `render={(props) => <h3 {...props} />}` is how a caller says which — without
1032
+ * losing the id `aria-labelledby` points at. `AlertDialog`, `Sheet` and
1033
+ * `Drawer` are made of these parts and pass `render` straight through.
96
1034
  */
97
1035
  export const Dialog = {
98
1036
  Root: DialogRoot,
99
1037
  Trigger: DialogTrigger,
100
- Content: DialogContent,
1038
+ Overlay: DialogOverlay,
1039
+ Body: DialogBody,
1040
+ Header: DialogHeader,
1041
+ Footer: DialogFooter,
101
1042
  Title: DialogTitle,
1043
+ Description: DialogDescription,
102
1044
  Close: DialogClose,
103
1045
  };
1046
+
1047
+ /**
1048
+ * The confirmation: modal, announced as an alert, and not dismissible by a
1049
+ * press beside it.
1050
+ *
1051
+ * Focus lands on `Cancel` rather than on the first thing in the dialog, and the
1052
+ * description is required — `role="alertdialog"` exists to announce one, so an
1053
+ * alert dialog without it interrupts the reader to say nothing.
1054
+ *
1055
+ * <AlertDialog.Root>
1056
+ * <AlertDialog.Trigger>Delete</AlertDialog.Trigger>
1057
+ * <AlertDialog.Overlay />
1058
+ * <AlertDialog.Body>
1059
+ * <AlertDialog.Header>
1060
+ * <AlertDialog.Title>Delete this project?</AlertDialog.Title>
1061
+ * <AlertDialog.Description>This cannot be undone.</AlertDialog.Description>
1062
+ * </AlertDialog.Header>
1063
+ * <AlertDialog.Footer>
1064
+ * <AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
1065
+ * <AlertDialog.Action onClick={remove}>Delete</AlertDialog.Action>
1066
+ * </AlertDialog.Footer>
1067
+ * </AlertDialog.Body>
1068
+ * </AlertDialog.Root>
1069
+ */
1070
+ export const AlertDialog = {
1071
+ Root: AlertDialogRoot,
1072
+ Trigger: AlertDialogTrigger,
1073
+ Overlay: AlertDialogOverlay,
1074
+ Body: AlertDialogBody,
1075
+ Header: AlertDialogHeader,
1076
+ Footer: AlertDialogFooter,
1077
+ Title: AlertDialogTitle,
1078
+ Description: AlertDialogDescription,
1079
+ Action: AlertDialogAction,
1080
+ Cancel: AlertDialogCancel,
1081
+ };
1082
+
1083
+ /**
1084
+ * A modal dialog attached to an edge of the viewport.
1085
+ *
1086
+ * `side` is a union rather than a class name, and every part reports it as
1087
+ * `data-side` — the same attribute `Popover.Body` writes, so one stylesheet
1088
+ * rule covers every overlay in this package.
1089
+ *
1090
+ * <Sheet.Root side="left">
1091
+ * <Sheet.Trigger>Filters</Sheet.Trigger>
1092
+ * <Sheet.Overlay />
1093
+ * <Sheet.Body>
1094
+ * <Sheet.Title>Filters</Sheet.Title>
1095
+ * <Sheet.Close>Done</Sheet.Close>
1096
+ * </Sheet.Body>
1097
+ * </Sheet.Root>
1098
+ */
1099
+ export const Sheet = {
1100
+ Root: SheetRoot,
1101
+ Trigger: SheetTrigger,
1102
+ Overlay: SheetOverlay,
1103
+ Body: SheetBody,
1104
+ Header: SheetHeader,
1105
+ Footer: SheetFooter,
1106
+ Title: SheetTitle,
1107
+ Description: SheetDescription,
1108
+ Close: SheetClose,
1109
+ };
1110
+
1111
+ /**
1112
+ * The sheet you can drag away, with the keyboard that can do everything the
1113
+ * drag can.
1114
+ *
1115
+ * `Drawer.Handle` is a `role="slider"` over the snap points: the arrow keys
1116
+ * move between them, `Home` and `End` go to the ends, and the closing key at
1117
+ * the smallest snap point closes it. WCAG 2.5.7 also wants a single-pointer
1118
+ * alternative, so a drawer with a handle and no `Drawer.Close` raises.
1119
+ *
1120
+ * <Drawer.Root side="bottom" snapPoints={[0.4, 1]}>
1121
+ * <Drawer.Trigger>Details</Drawer.Trigger>
1122
+ * <Drawer.Overlay />
1123
+ * <Drawer.Body>
1124
+ * <Drawer.Handle label="Resize the details" />
1125
+ * <Drawer.Title>Details</Drawer.Title>
1126
+ * <Drawer.Close>Close</Drawer.Close>
1127
+ * </Drawer.Body>
1128
+ * </Drawer.Root>
1129
+ */
1130
+ export const Drawer = {
1131
+ Root: DrawerRoot,
1132
+ Trigger: DrawerTrigger,
1133
+ Overlay: DrawerOverlay,
1134
+ Body: DrawerBody,
1135
+ Handle: DrawerHandle,
1136
+ Header: DrawerHeader,
1137
+ Footer: DrawerFooter,
1138
+ Title: DrawerTitle,
1139
+ Description: DrawerDescription,
1140
+ Close: DrawerClose,
1141
+ };
1142
+
1143
+ /**
1144
+ * Navigation beside the page, which becomes a modal sheet on a narrow one.
1145
+ *
1146
+ * `Sidebar.Item` takes a `label` and keeps it as the button's accessible name
1147
+ * the moment the sidebar collapses to icons — which is the whole reason a
1148
+ * collapsing sidebar is a component rather than a class.
1149
+ *
1150
+ * <Sidebar.Root defaultOpen={fromCookie}>
1151
+ * <Sidebar.Trigger>Menu</Sidebar.Trigger>
1152
+ * <Sidebar.Body label="Main">
1153
+ * <Sidebar.Item label="Settings">
1154
+ * <Gear /> Settings
1155
+ * </Sidebar.Item>
1156
+ * </Sidebar.Body>
1157
+ * </Sidebar.Root>
1158
+ */
1159
+ export const Sidebar = {
1160
+ Root: SidebarRoot,
1161
+ Trigger: SidebarTrigger,
1162
+ Header: SidebarHeader,
1163
+ Body: SidebarBody,
1164
+ Footer: SidebarFooter,
1165
+ Item: SidebarItem,
1166
+ };
1167
+
1168
+ /**
1169
+ * Slides, one at a time, that a reader can stop and cannot fall into.
1170
+ *
1171
+ * `Carousel.Pause` is WCAG 2.2.2's mechanism and must be the first focusable
1172
+ * thing inside the carousel; the slides that are not showing are `inert`, so
1173
+ * `Tab` cannot reach a link nobody can see.
1174
+ *
1175
+ * <Carousel.Root autoplay={5000} count={3} label="Featured">
1176
+ * <Carousel.Pause />
1177
+ * <Carousel.Content>
1178
+ * <Carousel.Item index={0}>…</Carousel.Item>
1179
+ * <Carousel.Item index={1}>…</Carousel.Item>
1180
+ * <Carousel.Item index={2}>…</Carousel.Item>
1181
+ * </Carousel.Content>
1182
+ * <Carousel.Previous />
1183
+ * <Carousel.Next />
1184
+ * </Carousel.Root>
1185
+ */
1186
+ export const Carousel = {
1187
+ Root: CarouselRoot,
1188
+ Content: CarouselContent,
1189
+ Item: CarouselItem,
1190
+ Pause: CarouselPause,
1191
+ Previous: CarouselPrevious,
1192
+ Next: CarouselNext,
1193
+ };
1194
+
1195
+ /**
1196
+ * An overflow container a keyboard can actually scroll.
1197
+ *
1198
+ * `role="region"`, a name and `tabindex="0"`, because a scroll container is not
1199
+ * focusable in every browser and one that is not is one a keyboard reader can
1200
+ * see the top of and nothing else.
1201
+ *
1202
+ * <ScrollArea.Root label="Release notes">
1203
+ * <ScrollArea.Viewport>…</ScrollArea.Viewport>
1204
+ * <ScrollArea.Scrollbar orientation="vertical" />
1205
+ * </ScrollArea.Root>
1206
+ */
1207
+ export const ScrollArea = {
1208
+ Root: ScrollAreaRoot,
1209
+ Viewport: ScrollAreaViewport,
1210
+ Scrollbar: ScrollAreaScrollbar,
1211
+ };
1212
+
1213
+ /**
1214
+ * A one-time code: six boxes drawn over one real `<input>`.
1215
+ *
1216
+ * One input, so `autocomplete="one-time-code"` works, a paste fills every box,
1217
+ * and a form submits one value under one name.
1218
+ *
1219
+ * <InputOtp.Root label="One-time code" length={6} name="code">
1220
+ * <InputOtp.Group>
1221
+ * <InputOtp.Slot index={0} />
1222
+ * <InputOtp.Slot index={1} />
1223
+ * <InputOtp.Slot index={2} />
1224
+ * </InputOtp.Group>
1225
+ * <InputOtp.Separator>-</InputOtp.Separator>
1226
+ * <InputOtp.Group>
1227
+ * <InputOtp.Slot index={3} />
1228
+ * <InputOtp.Slot index={4} />
1229
+ * <InputOtp.Slot index={5} />
1230
+ * </InputOtp.Group>
1231
+ * </InputOtp.Root>
1232
+ */
1233
+ export const InputOtp = {
1234
+ Root: InputOtpRoot,
1235
+ Group: InputOtpGroup,
1236
+ Slot: InputOtpSlot,
1237
+ Separator: InputOtpSeparator,
1238
+ };
1239
+
1240
+ /**
1241
+ * A menu, with the keyboard map every native menu has had for thirty years.
1242
+ *
1243
+ * <Menu.Root>
1244
+ * <Menu.Trigger>File</Menu.Trigger>
1245
+ * <Menu.Body>
1246
+ * <Menu.Group>
1247
+ * <Menu.Label>Recent</Menu.Label>
1248
+ * <Menu.Item onSelect={open}>Open…</Menu.Item>
1249
+ * </Menu.Group>
1250
+ * <Menu.Separator />
1251
+ * <Menu.Sub>
1252
+ * <Menu.SubTrigger>Export</Menu.SubTrigger>
1253
+ * <Menu.Body>
1254
+ * <Menu.Item onSelect={png}>PNG</Menu.Item>
1255
+ * </Menu.Body>
1256
+ * </Menu.Sub>
1257
+ * </Menu.Body>
1258
+ * </Menu.Root>
1259
+ *
1260
+ * Every part takes `render`, which is what makes a menu of links possible — and
1261
+ * a menu of links is the most ordinary menu there is:
1262
+ *
1263
+ * <Menu.Item render={(props) => <a href="/settings" {...props} />}>
1264
+ * Settings
1265
+ * </Menu.Item>
1266
+ *
1267
+ * The `<a>` keeps the middle click, the context menu and the status bar; the
1268
+ * item keeps the role, the id, the roving tab stop and the press that closes
1269
+ * the tree. See the module header for why that is the answer to "no copy step".
1270
+ */
1271
+ export const Menu = {
1272
+ Root: MenuRoot,
1273
+ Trigger: MenuTrigger,
1274
+ Body: MenuBody,
1275
+ Item: MenuItem,
1276
+ CheckboxItem: MenuCheckboxItem,
1277
+ RadioGroup: MenuRadioGroup,
1278
+ RadioItem: MenuRadioItem,
1279
+ Separator: MenuSeparator,
1280
+ Group: MenuGroup,
1281
+ Label: MenuLabel,
1282
+ Sub: MenuSub,
1283
+ SubTrigger: MenuSubTrigger,
1284
+ };
1285
+
1286
+ /**
1287
+ * The same menu, opened by the right button — and by the keyboard.
1288
+ *
1289
+ * `Shift+F10`, the `ContextMenu` key and a long press all open it, because a
1290
+ * command reachable only by right-click is reachable only by a pointer, which
1291
+ * is a WCAG 2.1.1 failure. `context-menu.js` says why the trigger is in the tab
1292
+ * order and when to take it out again.
1293
+ *
1294
+ * The body needs an `aria-label`: its trigger is a table row or a canvas rather
1295
+ * than a short name, so unlike `Menu.Body` it cannot name itself after one.
1296
+ *
1297
+ * <ContextMenu.Root>
1298
+ * <ContextMenu.Trigger>{row}</ContextMenu.Trigger>
1299
+ * <ContextMenu.Body aria-label="Row actions">
1300
+ * <ContextMenu.Item onSelect={rename}>Rename…</ContextMenu.Item>
1301
+ * <ContextMenu.CheckboxItem defaultChecked>Show hidden</ContextMenu.CheckboxItem>
1302
+ * </ContextMenu.Body>
1303
+ * </ContextMenu.Root>
1304
+ */
1305
+ export const ContextMenu = {
1306
+ Root: ContextMenuRoot,
1307
+ Trigger: ContextMenuTrigger,
1308
+ Body: MenuBody,
1309
+ Item: MenuItem,
1310
+ CheckboxItem: MenuCheckboxItem,
1311
+ RadioGroup: MenuRadioGroup,
1312
+ RadioItem: MenuRadioItem,
1313
+ Separator: MenuSeparator,
1314
+ Group: MenuGroup,
1315
+ Label: MenuLabel,
1316
+ Sub: MenuSub,
1317
+ SubTrigger: MenuSubTrigger,
1318
+ };
1319
+
1320
+ /**
1321
+ * A row of menus that behaves as one control: File, Edit, View.
1322
+ *
1323
+ * One tab stop for the whole bar, arrows between the menus, and — the part that
1324
+ * is always missing — arrows *while a menu is open* that close it and open the
1325
+ * next one, so a reader walks File → Edit → View without pressing Escape.
1326
+ *
1327
+ * <Menubar.Root aria-label="Main">
1328
+ * <Menubar.Menu value="file">
1329
+ * <Menubar.Trigger>File</Menubar.Trigger>
1330
+ * <Menubar.Body>
1331
+ * <Menubar.Item onSelect={open}>Open…</Menubar.Item>
1332
+ * </Menubar.Body>
1333
+ * </Menubar.Menu>
1334
+ * </Menubar.Root>
1335
+ */
1336
+ export const Menubar = {
1337
+ Root: MenubarRoot,
1338
+ Menu: MenubarMenu,
1339
+ Trigger: MenubarTrigger,
1340
+ // `Menu.Body` itself: a bar's menu is a root menu, and `menubar.js`'s header
1341
+ // says why a wrapper with the same defaults would be a second place to drift.
1342
+ Body: MenuBody,
1343
+ Item: MenuItem,
1344
+ CheckboxItem: MenuCheckboxItem,
1345
+ RadioGroup: MenuRadioGroup,
1346
+ RadioItem: MenuRadioItem,
1347
+ Separator: MenuSeparator,
1348
+ Group: MenuGroup,
1349
+ Label: MenuLabel,
1350
+ Sub: MenuSub,
1351
+ SubTrigger: MenuSubTrigger,
1352
+ };
1353
+
1354
+ /**
1355
+ * A text field with a list of options, navigated without leaving the field.
1356
+ *
1357
+ * The caller filters; the component keeps the ARIA wiring true while they do.
1358
+ *
1359
+ * <Combobox.Root inputValue={query} onInputValueChange={setQuery}>
1360
+ * <Combobox.Label>Country</Combobox.Label>
1361
+ * <Combobox.Input />
1362
+ * <Combobox.List>
1363
+ * <Combobox.Group>
1364
+ * <Combobox.GroupLabel>Europe</Combobox.GroupLabel>
1365
+ * {european.map((each) => (
1366
+ * <Combobox.Option key={each} value={each}>{each}</Combobox.Option>
1367
+ * ))}
1368
+ * </Combobox.Group>
1369
+ * </Combobox.List>
1370
+ * <Combobox.Empty>No matches.</Combobox.Empty>
1371
+ * <Combobox.Status />
1372
+ * </Combobox.Root>
1373
+ *
1374
+ * `Combobox.Label` names the field and `Combobox.GroupLabel` names a group of
1375
+ * options, which is why there are two of them.
1376
+ */
1377
+ export const Combobox = {
1378
+ Root: ComboboxRoot,
1379
+ Label: ComboboxLabel,
1380
+ Input: ComboboxInput,
1381
+ List: ComboboxList,
1382
+ Option: ComboboxOption,
1383
+ Group: ComboboxGroup,
1384
+ GroupLabel: ComboboxGroupLabel,
1385
+ Empty: ComboboxEmpty,
1386
+ Status: ComboboxStatus,
1387
+ };
1388
+
1389
+ /**
1390
+ * The other half of the combobox pattern: a button, a list, and no typing.
1391
+ *
1392
+ * Use a native `<select>` when a native `<select>` will do — `select.js` says
1393
+ * so first and means it. This is for the popup a `<select>` cannot draw.
1394
+ *
1395
+ * <Select.Root defaultValue="GB" name="country">
1396
+ * <Select.Label>Country</Select.Label>
1397
+ * <Select.Trigger>
1398
+ * <Select.Value placeholder="Choose one" />
1399
+ * </Select.Trigger>
1400
+ * <Select.List>
1401
+ * <Select.Group>
1402
+ * <Select.GroupLabel>Europe</Select.GroupLabel>
1403
+ * <Select.Option value="GB">United Kingdom</Select.Option>
1404
+ * <Select.Option value="FR">France</Select.Option>
1405
+ * </Select.Group>
1406
+ * <Select.Separator />
1407
+ * <Select.Option value="JP">Japan</Select.Option>
1408
+ * </Select.List>
1409
+ * </Select.Root>
1410
+ *
1411
+ * `Select.Label` names the field and `Select.GroupLabel` names a group of
1412
+ * options. shadcn has one `SelectLabel` and it is the second of those; a select
1413
+ * needs both, so they are two parts here.
1414
+ */
1415
+ export const Select = {
1416
+ Root: SelectRoot,
1417
+ Label: SelectLabel,
1418
+ Trigger: SelectTrigger,
1419
+ Value: SelectValue,
1420
+ List: SelectList,
1421
+ Option: SelectOption,
1422
+ Group: SelectGroup,
1423
+ GroupLabel: SelectGroupLabel,
1424
+ Separator: SelectSeparator,
1425
+ };
1426
+
1427
+ /**
1428
+ * A dialog that is not modal, anchored to the button that opened it.
1429
+ *
1430
+ * Focus moves in, `Escape` closes it and gives focus back, and `Tab` *leaves* —
1431
+ * the page behind a popover is still there, still scrollable and still
1432
+ * tabbable, which is every way in which it is not a `Dialog`.
1433
+ *
1434
+ * <Popover.Root>
1435
+ * <Popover.Trigger>Filters</Popover.Trigger>
1436
+ * <Popover.Body align="start" side="bottom" sideOffset={8}>
1437
+ * <label>
1438
+ * Only mine <input type="checkbox" />
1439
+ * </label>
1440
+ * </Popover.Body>
1441
+ * </Popover.Root>
1442
+ *
1443
+ * `Popover.Body` reports where it ended up as `data-side` and `data-align`, and
1444
+ * writes the trigger's width and the room it had as custom properties, so a
1445
+ * stylesheet can point an arrow and cap a height without measuring anything.
1446
+ */
1447
+ export const Popover = {
1448
+ Root: PopoverRoot,
1449
+ Trigger: PopoverTrigger,
1450
+ Body: PopoverBody,
1451
+ };
1452
+
1453
+ /**
1454
+ * A month of dates, as one stop in the page's tab order.
1455
+ *
1456
+ * The grid is `role="grid"`, the arrow keys move by a day and by a week, and
1457
+ * `PageUp` and `PageDown` change the month - with `Shift`, the year. Running off
1458
+ * the end of a month shows the next one and lands on its first day, and the
1459
+ * month is announced in a live region when it changes.
1460
+ *
1461
+ * <Calendar.Root defaultValue="2026-10-14" onValueChange={setWhen}>
1462
+ * <Calendar.Previous>Previous month</Calendar.Previous>
1463
+ * <Calendar.Next>Next month</Calendar.Next>
1464
+ * <Calendar.Month />
1465
+ * </Calendar.Root>
1466
+ *
1467
+ * `Calendar.Month` takes a function child when a day needs more than its number
1468
+ * in it - a dot for an appointment, a price for a night - and it is handed the
1469
+ * date and returns a `Calendar.Day`.
1470
+ *
1471
+ * Dates are `@uniflowed/temporal`'s `PlainDate`, or the ISO strings it reads.
1472
+ * `isDateDisabled` marks a day unavailable *without* making it unreachable: it
1473
+ * is `aria-disabled` and the arrow keys still land on it, which is the opposite
1474
+ * of what a disabled menu item does and the only way a reader can find out which
1475
+ * days are unavailable.
1476
+ */
1477
+ export const Calendar = {
1478
+ Root: CalendarRoot,
1479
+ Previous: CalendarPrevious,
1480
+ Next: CalendarNext,
1481
+ Month: CalendarMonth,
1482
+ Day: CalendarDay,
1483
+ };
1484
+
1485
+ /**
1486
+ * A field somebody types a date into, and a calendar for the times they would
1487
+ * rather point at one.
1488
+ *
1489
+ * <DatePicker.Root onValueChange={setWhen} value={when}>
1490
+ * <DatePicker.Input aria-label="Arrive on" />
1491
+ * <DatePicker.Trigger>Choose a date</DatePicker.Trigger>
1492
+ * <DatePicker.Calendar>
1493
+ * <Calendar.Previous>Previous month</Calendar.Previous>
1494
+ * <Calendar.Next>Next month</Calendar.Next>
1495
+ * <Calendar.Month />
1496
+ * </DatePicker.Calendar>
1497
+ * </DatePicker.Root>
1498
+ *
1499
+ * The field is the control and the grid is the second way in: `Escape` and a
1500
+ * chosen date both put focus back on the field. `format` and `parse` are ISO
1501
+ * 8601 both ways unless a caller passes their own - `date-picker.js` says why a
1502
+ * locale format is not this package's to guess.
1503
+ */
1504
+ export const DatePicker = {
1505
+ Root: DatePickerRoot,
1506
+ Input: DatePickerInput,
1507
+ Trigger: DatePickerTrigger,
1508
+ Calendar: DatePickerCalendar,
1509
+ };
1510
+
1511
+ /**
1512
+ * A phrase about a control, on hover and on focus, that WCAG would accept.
1513
+ *
1514
+ * Dismissible with `Escape`, hoverable — the pointer can travel onto it — and
1515
+ * never focusable. It does not open on touch, deliberately, so the trigger must
1516
+ * carry its own name for a reader holding a phone.
1517
+ *
1518
+ * <Tooltip.Provider delayDuration={700} skipDelayDuration={300}>
1519
+ * <Tooltip.Root>
1520
+ * <Tooltip.Trigger aria-label="Bold">B</Tooltip.Trigger>
1521
+ * <Tooltip.Body>Bold (⌘B)</Tooltip.Body>
1522
+ * </Tooltip.Root>
1523
+ * <Tooltip.Root>
1524
+ * <Tooltip.Trigger aria-label="Italic">I</Tooltip.Trigger>
1525
+ * <Tooltip.Body>Italic (⌘I)</Tooltip.Body>
1526
+ * </Tooltip.Root>
1527
+ * </Tooltip.Provider>
1528
+ *
1529
+ * `Tooltip.Provider` is what makes the second icon in that toolbar answer at
1530
+ * once instead of making the reader wait the delay again. A tooltip outside one
1531
+ * is a complete tooltip with a delay of its own.
1532
+ */
1533
+ export const Tooltip = {
1534
+ Provider: TooltipProvider,
1535
+ Root: TooltipRoot,
1536
+ Trigger: TooltipTrigger,
1537
+ Body: TooltipBody,
1538
+ };
1539
+
1540
+ /**
1541
+ * The preview a name expands into: hovered, focused, and full of links.
1542
+ *
1543
+ * Not a tooltip — its contents are reachable, by pointer and by `Tab` — and not
1544
+ * a dialog, because nothing about it is modal.
1545
+ *
1546
+ * <HoverCard.Root>
1547
+ * <HoverCard.Trigger render={(props) => <a href="/ada" {...props}>@ada</a>} />
1548
+ * <HoverCard.Body>
1549
+ * <p>Ada Lovelace</p>
1550
+ * <a href="/ada/notes">Notes</a>
1551
+ * </HoverCard.Body>
1552
+ * </HoverCard.Root>
1553
+ */
1554
+ export const HoverCard = {
1555
+ Root: HoverCardRoot,
1556
+ Trigger: HoverCardTrigger,
1557
+ Body: HoverCardBody,
1558
+ };
1559
+
1560
+ /**
1561
+ * Notifications, in a live region that was watching before them.
1562
+ *
1563
+ * Render `Toast.Region` once, in the layout; `toast()` from anywhere.
1564
+ *
1565
+ * <Toast.Region>
1566
+ * {(each) => (
1567
+ * <Toast.Root>
1568
+ * <Toast.Title>{each.content}</Toast.Title>
1569
+ * <Toast.Action onClick={undo}>Undo</Toast.Action>
1570
+ * <Toast.Close />
1571
+ * </Toast.Root>
1572
+ * )}
1573
+ * </Toast.Region>
1574
+ */
1575
+ export const Toast = {
1576
+ Region: ToastRegion,
1577
+ Root: ToastRoot,
1578
+ Title: ToastTitle,
1579
+ Description: ToastDescription,
1580
+ Action: ToastAction,
1581
+ Close: ToastClose,
1582
+ };
1583
+
1584
+ /**
1585
+ * A value in a range, with `role="slider"` on the thumb where it belongs.
1586
+ *
1587
+ * One thumb or two; a range is the same component with a second one, each
1588
+ * bounded by its neighbour and each needing its own name.
1589
+ *
1590
+ * <Slider.Root defaultValue={[20, 60]} valueText={(each) => `£${each}`}>
1591
+ * <Slider.Track>
1592
+ * <Slider.Range />
1593
+ * </Slider.Track>
1594
+ * <Slider.Thumb aria-label="Minimum" index={0} />
1595
+ * <Slider.Thumb aria-label="Maximum" index={1} />
1596
+ * </Slider.Root>
1597
+ */
1598
+ export const Slider = {
1599
+ Root: SliderRoot,
1600
+ Track: SliderTrack,
1601
+ Range: SliderRange,
1602
+ Thumb: SliderThumb,
1603
+ };
1604
+
1605
+ /**
1606
+ * Two panes and the splitter between them, operable from the keyboard.
1607
+ *
1608
+ * <Resizable.PanelGroup defaultValue={30}>
1609
+ * <Resizable.Panel primary>Files</Resizable.Panel>
1610
+ * <Resizable.Handle label="Resize the file list" />
1611
+ * <Resizable.Panel>Editor</Resizable.Panel>
1612
+ * </Resizable.PanelGroup>
1613
+ */
1614
+ export const Resizable = {
1615
+ PanelGroup: ResizablePanelGroup,
1616
+ Panel: ResizablePanel,
1617
+ Handle: ResizableHandle,
1618
+ };
1619
+
1620
+ /**
1621
+ * A table, with the four things about one nobody gets right by hand.
1622
+ *
1623
+ * A real `<table>`, deliberately not a `role="grid"` — `table.js` says why —
1624
+ * and its own live region, so a re-sort is something a reader is told about
1625
+ * rather than something that happens silently behind them.
1626
+ *
1627
+ * <Table.Root onSortChange={setSort} rowCount={500} rowOffset={90} sort={sort}>
1628
+ * <Table.Caption>People</Table.Caption>
1629
+ * <Table.Header>
1630
+ * <Table.Row>
1631
+ * <Table.Head>
1632
+ * <Table.SelectAll checked={all} onCheckedChange={setAll} />
1633
+ * </Table.Head>
1634
+ * <Table.Head column="name">Name</Table.Head>
1635
+ * </Table.Row>
1636
+ * </Table.Header>
1637
+ * <Table.Body>
1638
+ * {page.map((person, at) => (
1639
+ * <Table.Row index={at} key={person.id}>
1640
+ * <Table.Cell>
1641
+ * <Table.RowSelect
1642
+ * checked={chosen.has(person.id)}
1643
+ * label={`Select ${person.name}`}
1644
+ * onCheckedChange={(on) => choose(person.id, on)}
1645
+ * />
1646
+ * </Table.Cell>
1647
+ * <Table.RowHeader>{person.name}</Table.RowHeader>
1648
+ * </Table.Row>
1649
+ * ))}
1650
+ * </Table.Body>
1651
+ * </Table.Root>
1652
+ */
1653
+ export const Table = {
1654
+ Root: TableRoot,
1655
+ Caption: TableCaption,
1656
+ Header: TableHeader,
1657
+ Body: TableBody,
1658
+ Row: TableRow,
1659
+ Head: TableHead,
1660
+ RowHeader: TableRowHeader,
1661
+ Cell: TableCell,
1662
+ SelectAll: TableSelectAll,
1663
+ RowSelect: TableRowSelect,
1664
+ };
1665
+
1666
+ /**
1667
+ * The navigation a paginated table needs, and the sentence that says it moved.
1668
+ *
1669
+ * <Pagination.Root page={4} pageCount={25}>
1670
+ * <Pagination.Content>
1671
+ * <Pagination.Previous disabled={page === 1} href={hrefFor(page - 1)}>‹</Pagination.Previous>
1672
+ * <Pagination.Item current href={hrefFor(4)}>4</Pagination.Item>
1673
+ * <Pagination.Next href={hrefFor(page + 1)}>›</Pagination.Next>
1674
+ * </Pagination.Content>
1675
+ * </Pagination.Root>
1676
+ */
1677
+ export const Pagination = {
1678
+ Root: PaginationRoot,
1679
+ Content: PaginationContent,
1680
+ Item: PaginationItem,
1681
+ Previous: PaginationPrevious,
1682
+ Next: PaginationNext,
1683
+ };
1684
+
1685
+ /**
1686
+ * The trail above the page, read as places rather than as punctuation.
1687
+ *
1688
+ * <Breadcrumb.Root>
1689
+ * <Breadcrumb.List>
1690
+ * <Breadcrumb.Item>
1691
+ * <Breadcrumb.Link href="/">Home</Breadcrumb.Link>
1692
+ * </Breadcrumb.Item>
1693
+ * <Breadcrumb.Separator>/</Breadcrumb.Separator>
1694
+ * <Breadcrumb.Item>
1695
+ * <Breadcrumb.Page>Billing</Breadcrumb.Page>
1696
+ * </Breadcrumb.Item>
1697
+ * </Breadcrumb.List>
1698
+ * </Breadcrumb.Root>
1699
+ *
1700
+ * `Pagination`'s shape one door along: a `<nav>` with a name, one
1701
+ * `aria-current="page"`, and the separators out of the accessibility tree so
1702
+ * the trail is not announced as "Home slash Settings slash Billing". The last
1703
+ * crumb is a `Breadcrumb.Page` and not a link, because it is where the reader
1704
+ * already is.
1705
+ */
1706
+ export const Breadcrumb = {
1707
+ Root: BreadcrumbRoot,
1708
+ List: BreadcrumbList,
1709
+ Item: BreadcrumbItem,
1710
+ Link: BreadcrumbLink,
1711
+ Page: BreadcrumbPage,
1712
+ Separator: BreadcrumbSeparator,
1713
+ };
1714
+
1715
+ /**
1716
+ * A callout, and the `live` that decides whether anybody is interrupted by it.
1717
+ *
1718
+ * <Alert.Root>
1719
+ * <Alert.Title>Your trial ends on Friday</Alert.Title>
1720
+ * <Alert.Description>Add a card to keep your projects.</Alert.Description>
1721
+ * </Alert.Root>
1722
+ *
1723
+ * {error != null && (
1724
+ * <Alert.Root live>
1725
+ * <Alert.Title>Could not save</Alert.Title>
1726
+ * <Alert.Description>{error}</Alert.Description>
1727
+ * </Alert.Root>
1728
+ * )}
1729
+ *
1730
+ * The first has no role at all: it was there when the page loaded, so a live
1731
+ * region would announce it on every load or never, and neither is what anybody
1732
+ * wanted. The second appeared because something happened, which is what
1733
+ * `role="alert"` is for. `alert.js`'s header says why there is no polite
1734
+ * version of this and why `Toast` is that instead.
1735
+ */
1736
+ export const Alert = {
1737
+ Root: AlertRoot,
1738
+ Title: AlertTitle,
1739
+ Description: AlertDescription,
1740
+ };
1741
+
1742
+ /**
1743
+ * A picture of a person, and the two states it is not in yet.
1744
+ *
1745
+ * <Avatar.Root>
1746
+ * <Avatar.Image src={person.photo} />
1747
+ * <Avatar.Fallback>{initials(person.name)}</Avatar.Fallback>
1748
+ * </Avatar.Root>
1749
+ *
1750
+ * The fallback is absent while the image is loading and present once it has
1751
+ * failed, held back long enough that a cached image never flashes initials.
1752
+ * `alt` defaults to `""`, because an avatar beside the name it belongs to is
1753
+ * decorative and a component that helpfully puts the name there makes every
1754
+ * screen reader say it twice; pass `alt` where the picture is the only thing
1755
+ * identifying the person.
1756
+ */
1757
+ export const Avatar = {
1758
+ Root: AvatarRoot,
1759
+ Image: AvatarImage,
1760
+ Fallback: AvatarFallback,
1761
+ };
1762
+
1763
+ /**
1764
+ * The grey boxes, and the sentence that stops them being an empty page.
1765
+ *
1766
+ * <Skeleton.Root busy={pending}>
1767
+ * {pending ? <Skeleton.Box /> : <Invoices rows={invoices} />}
1768
+ * </Skeleton.Root>
1769
+ *
1770
+ * The boxes are `aria-hidden`, the region is `aria-busy`, and a live region
1771
+ * that was mounted empty for a commit says "Loading" — a skeleton screen is
1772
+ * busy on its first render, so a region rendered with its message already in it
1773
+ * announces nothing at all. Keep the root mounted across the load and toggle
1774
+ * `busy`; unmounting it takes the region away before it can say the wait is
1775
+ * over.
1776
+ */
1777
+ export const Skeleton = {
1778
+ Root: SkeletonRoot,
1779
+ Box: SkeletonBox,
1780
+ };