@mortenbrudvik/waveui 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +413 -53
- package/dist/components/data-display/InfoButton.cjs +1 -1
- package/dist/components/data-display/InfoButton.cjs.map +1 -1
- package/dist/components/data-display/InfoButton.mjs +90 -82
- package/dist/components/data-display/InfoButton.mjs.map +1 -1
- package/dist/components/data-display/InfoLabel.cjs +1 -1
- package/dist/components/data-display/InfoLabel.cjs.map +1 -1
- package/dist/components/data-display/InfoLabel.mjs +19 -18
- package/dist/components/data-display/InfoLabel.mjs.map +1 -1
- package/dist/components/data-display/List.cjs +1 -1
- package/dist/components/data-display/List.cjs.map +1 -1
- package/dist/components/data-display/List.mjs +405 -219
- package/dist/components/data-display/List.mjs.map +1 -1
- package/dist/components/data-display/List.registry.cjs +1 -1
- package/dist/components/data-display/List.registry.cjs.map +1 -1
- package/dist/components/data-display/List.registry.mjs +12 -6
- package/dist/components/data-display/List.registry.mjs.map +1 -1
- package/dist/components/feedback/MessageBarGroup.cjs +2 -0
- package/dist/components/feedback/MessageBarGroup.cjs.map +1 -0
- package/dist/components/feedback/MessageBarGroup.mjs +63 -0
- package/dist/components/feedback/MessageBarGroup.mjs.map +1 -0
- package/dist/components/feedback/ProgressBar.cjs +1 -1
- package/dist/components/feedback/ProgressBar.cjs.map +1 -1
- package/dist/components/feedback/ProgressBar.mjs +41 -39
- package/dist/components/feedback/ProgressBar.mjs.map +1 -1
- package/dist/components/feedback/Toaster.cjs +1 -1
- package/dist/components/feedback/Toaster.cjs.map +1 -1
- package/dist/components/feedback/Toaster.mjs +222 -216
- package/dist/components/feedback/Toaster.mjs.map +1 -1
- package/dist/components/input/Combobox.cjs +1 -1
- package/dist/components/input/Combobox.cjs.map +1 -1
- package/dist/components/input/Combobox.mjs +163 -148
- package/dist/components/input/Combobox.mjs.map +1 -1
- package/dist/components/input/Dropdown.cjs +1 -1
- package/dist/components/input/Dropdown.cjs.map +1 -1
- package/dist/components/input/Dropdown.mjs +132 -117
- package/dist/components/input/Dropdown.mjs.map +1 -1
- package/dist/components/input/Listbox.cjs +1 -1
- package/dist/components/input/Listbox.cjs.map +1 -1
- package/dist/components/input/Listbox.mjs +90 -78
- package/dist/components/input/Listbox.mjs.map +1 -1
- package/dist/components/input/Option.cjs +1 -1
- package/dist/components/input/Option.cjs.map +1 -1
- package/dist/components/input/Option.data.cjs +2 -0
- package/dist/components/input/Option.data.cjs.map +1 -0
- package/dist/components/input/Option.data.mjs +114 -0
- package/dist/components/input/Option.data.mjs.map +1 -0
- package/dist/components/input/Option.mjs +21 -17
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/layout/Accordion.cjs +1 -1
- package/dist/components/layout/Accordion.cjs.map +1 -1
- package/dist/components/layout/Accordion.mjs +124 -109
- package/dist/components/layout/Accordion.mjs.map +1 -1
- package/dist/components/layout/Carousel.cjs +1 -1
- package/dist/components/layout/Carousel.cjs.map +1 -1
- package/dist/components/layout/Carousel.mjs +207 -193
- package/dist/components/layout/Carousel.mjs.map +1 -1
- package/dist/components/layout/FlatTree.cjs +2 -0
- package/dist/components/layout/FlatTree.cjs.map +1 -0
- package/dist/components/layout/FlatTree.context.cjs +2 -0
- package/dist/components/layout/FlatTree.context.cjs.map +1 -0
- package/dist/components/layout/FlatTree.context.mjs +20 -0
- package/dist/components/layout/FlatTree.context.mjs.map +1 -0
- package/dist/components/layout/FlatTree.item.cjs +2 -0
- package/dist/components/layout/FlatTree.item.cjs.map +1 -0
- package/dist/components/layout/FlatTree.item.mjs +90 -0
- package/dist/components/layout/FlatTree.item.mjs.map +1 -0
- package/dist/components/layout/FlatTree.mjs +276 -0
- package/dist/components/layout/FlatTree.mjs.map +1 -0
- package/dist/components/layout/TabList.cjs +1 -1
- package/dist/components/layout/TabList.cjs.map +1 -1
- package/dist/components/layout/TabList.mjs +149 -108
- package/dist/components/layout/TabList.mjs.map +1 -1
- package/dist/components/layout/Tree.cjs.map +1 -1
- package/dist/components/layout/Tree.context.cjs +1 -1
- package/dist/components/layout/Tree.context.cjs.map +1 -1
- package/dist/components/layout/Tree.context.mjs +2 -1
- package/dist/components/layout/Tree.context.mjs.map +1 -1
- package/dist/components/layout/Tree.core.cjs +1 -1
- package/dist/components/layout/Tree.core.cjs.map +1 -1
- package/dist/components/layout/Tree.core.mjs +162 -148
- package/dist/components/layout/Tree.core.mjs.map +1 -1
- package/dist/components/layout/Tree.mjs.map +1 -1
- package/dist/components/layout/Tree.selection.cjs +1 -1
- package/dist/components/layout/Tree.selection.cjs.map +1 -1
- package/dist/components/layout/Tree.selection.mjs +11 -57
- package/dist/components/layout/Tree.selection.mjs.map +1 -1
- package/dist/components/layout/useFlatTree.cjs +2 -0
- package/dist/components/layout/useFlatTree.cjs.map +1 -0
- package/dist/components/layout/useFlatTree.mjs +83 -0
- package/dist/components/layout/useFlatTree.mjs.map +1 -0
- package/dist/components/navigation/Menu.popover.cjs +1 -1
- package/dist/components/navigation/Menu.popover.cjs.map +1 -1
- package/dist/components/navigation/Menu.popover.mjs +154 -168
- package/dist/components/navigation/Menu.popover.mjs.map +1 -1
- package/dist/components/navigation/Nav.cjs +1 -1
- package/dist/components/navigation/Nav.cjs.map +1 -1
- package/dist/components/navigation/Nav.mjs +156 -136
- package/dist/components/navigation/Nav.mjs.map +1 -1
- package/dist/components/navigation/NavDrawer.cjs +1 -1
- package/dist/components/navigation/NavDrawer.cjs.map +1 -1
- package/dist/components/navigation/NavDrawer.mjs +48 -45
- package/dist/components/navigation/NavDrawer.mjs.map +1 -1
- package/dist/components/overlays/Dialog.cjs +1 -1
- package/dist/components/overlays/Dialog.cjs.map +1 -1
- package/dist/components/overlays/Dialog.mjs +128 -122
- package/dist/components/overlays/Dialog.mjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.cjs +1 -1
- package/dist/components/overlays/Dialog.slots.cjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.mjs +12 -12
- package/dist/components/overlays/Dialog.slots.mjs.map +1 -1
- package/dist/components/overlays/Drawer.cjs.map +1 -1
- package/dist/components/overlays/Drawer.mjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.cjs +1 -1
- package/dist/components/overlays/Drawer.panel.cjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.mjs +97 -81
- package/dist/components/overlays/Drawer.panel.mjs.map +1 -1
- package/dist/components/overlays/Drawer.root.cjs +1 -1
- package/dist/components/overlays/Drawer.root.cjs.map +1 -1
- package/dist/components/overlays/Drawer.root.mjs +34 -31
- package/dist/components/overlays/Drawer.root.mjs.map +1 -1
- package/dist/components/overlays/Popover.cjs +1 -1
- package/dist/components/overlays/Popover.cjs.map +1 -1
- package/dist/components/overlays/Popover.mjs +231 -216
- package/dist/components/overlays/Popover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.parts.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.parts.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.parts.mjs +199 -177
- package/dist/components/overlays/TeachingPopover.parts.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.root.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.root.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.root.mjs +93 -92
- package/dist/components/overlays/TeachingPopover.root.mjs.map +1 -1
- package/dist/components/overlays/Tooltip.cjs +1 -1
- package/dist/components/overlays/Tooltip.cjs.map +1 -1
- package/dist/components/overlays/Tooltip.mjs +66 -61
- package/dist/components/overlays/Tooltip.mjs.map +1 -1
- package/dist/components/provider/WaveProvider.cjs +1 -1
- package/dist/components/provider/WaveProvider.cjs.map +1 -1
- package/dist/components/provider/WaveProvider.mjs +26 -23
- package/dist/components/provider/WaveProvider.mjs.map +1 -1
- package/dist/components/table/DataGrid.cjs +1 -1
- package/dist/components/table/DataGrid.cjs.map +1 -1
- package/dist/components/table/DataGrid.context.cjs +2 -0
- package/dist/components/table/DataGrid.context.cjs.map +1 -0
- package/dist/components/table/DataGrid.context.mjs +74 -0
- package/dist/components/table/DataGrid.context.mjs.map +1 -0
- package/dist/components/table/DataGrid.header.cjs +2 -0
- package/dist/components/table/DataGrid.header.cjs.map +1 -0
- package/dist/components/table/DataGrid.header.mjs +168 -0
- package/dist/components/table/DataGrid.header.mjs.map +1 -0
- package/dist/components/table/DataGrid.mjs +488 -591
- package/dist/components/table/DataGrid.mjs.map +1 -1
- package/dist/components/table/DataGrid.sizing.cjs +2 -0
- package/dist/components/table/DataGrid.sizing.cjs.map +1 -0
- package/dist/components/table/DataGrid.sizing.mjs +187 -0
- package/dist/components/table/DataGrid.sizing.mjs.map +1 -0
- package/dist/components/table/DataGrid.store.cjs +2 -0
- package/dist/components/table/DataGrid.store.cjs.map +1 -0
- package/dist/components/table/DataGrid.store.mjs +108 -0
- package/dist/components/table/DataGrid.store.mjs.map +1 -0
- package/dist/components/table/DataGrid.window.cjs +2 -0
- package/dist/components/table/DataGrid.window.cjs.map +1 -0
- package/dist/components/table/DataGrid.window.mjs +165 -0
- package/dist/components/table/DataGrid.window.mjs.map +1 -0
- package/dist/components/table/Table.cjs +1 -1
- package/dist/components/table/Table.cjs.map +1 -1
- package/dist/components/table/Table.mjs +74 -72
- package/dist/components/table/Table.mjs.map +1 -1
- package/dist/components/table/Table.resize.cjs +2 -0
- package/dist/components/table/Table.resize.cjs.map +1 -0
- package/dist/components/table/Table.resize.mjs +98 -0
- package/dist/components/table/Table.resize.mjs.map +1 -0
- package/dist/components/table/useGridNavigation.cjs +1 -1
- package/dist/components/table/useGridNavigation.cjs.map +1 -1
- package/dist/components/table/useGridNavigation.mjs +343 -140
- package/dist/components/table/useGridNavigation.mjs.map +1 -1
- package/dist/components/table/useTableColumnSizing.cjs +2 -0
- package/dist/components/table/useTableColumnSizing.cjs.map +1 -0
- package/dist/components/table/useTableColumnSizing.mjs +76 -0
- package/dist/components/table/useTableColumnSizing.mjs.map +1 -0
- package/dist/components/table/useTableFeatures.cjs +2 -0
- package/dist/components/table/useTableFeatures.cjs.map +1 -0
- package/dist/components/table/useTableFeatures.mjs +110 -0
- package/dist/components/table/useTableFeatures.mjs.map +1 -0
- package/dist/components/table/useTableSelection.cjs +2 -0
- package/dist/components/table/useTableSelection.cjs.map +1 -0
- package/dist/components/table/useTableSelection.mjs +39 -0
- package/dist/components/table/useTableSelection.mjs.map +1 -0
- package/dist/components/table/useTableSort.cjs +2 -0
- package/dist/components/table/useTableSort.cjs.map +1 -0
- package/dist/components/table/useTableSort.mjs +58 -0
- package/dist/components/table/useTableSort.mjs.map +1 -0
- package/dist/hooks/useActiveDescendant.cjs +1 -1
- package/dist/hooks/useActiveDescendant.cjs.map +1 -1
- package/dist/hooks/useActiveDescendant.mjs +33 -25
- package/dist/hooks/useActiveDescendant.mjs.map +1 -1
- package/dist/hooks/useCollapsePresence.cjs +2 -0
- package/dist/hooks/useCollapsePresence.cjs.map +1 -0
- package/dist/hooks/useCollapsePresence.mjs +21 -0
- package/dist/hooks/useCollapsePresence.mjs.map +1 -0
- package/dist/hooks/useFocusBeforeClose.cjs +2 -0
- package/dist/hooks/useFocusBeforeClose.cjs.map +1 -0
- package/dist/hooks/useFocusBeforeClose.mjs +33 -0
- package/dist/hooks/useFocusBeforeClose.mjs.map +1 -0
- package/dist/hooks/useListbox.cjs +1 -1
- package/dist/hooks/useListbox.cjs.map +1 -1
- package/dist/hooks/useListbox.mjs +140 -130
- package/dist/hooks/useListbox.mjs.map +1 -1
- package/dist/hooks/useMotion.cjs +2 -0
- package/dist/hooks/useMotion.cjs.map +1 -0
- package/dist/hooks/useMotion.mjs +14 -0
- package/dist/hooks/useMotion.mjs.map +1 -0
- package/dist/hooks/usePresenceGroup.cjs +2 -0
- package/dist/hooks/usePresenceGroup.cjs.map +1 -0
- package/dist/hooks/usePresenceGroup.mjs +68 -0
- package/dist/hooks/usePresenceGroup.mjs.map +1 -0
- package/dist/hooks/useRovingTabIndex.cjs +1 -1
- package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.mjs +1 -1
- package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
- package/dist/hooks/useSelection.cjs +2 -0
- package/dist/hooks/useSelection.cjs.map +1 -0
- package/dist/hooks/useSelection.mjs +57 -0
- package/dist/hooks/useSelection.mjs.map +1 -0
- package/dist/hooks/useVirtualWindow.cjs +2 -0
- package/dist/hooks/useVirtualWindow.cjs.map +1 -0
- package/dist/hooks/useVirtualWindow.mjs +102 -0
- package/dist/hooks/useVirtualWindow.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2249 -179
- package/dist/index.d.ts +2249 -179
- package/dist/index.mjs +147 -135
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +125 -95
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/columnSizing.cjs +2 -0
- package/dist/lib/columnSizing.cjs.map +1 -0
- package/dist/lib/columnSizing.mjs +27 -0
- package/dist/lib/columnSizing.mjs.map +1 -0
- package/dist/lib/flatTree.cjs +2 -0
- package/dist/lib/flatTree.cjs.map +1 -0
- package/dist/lib/flatTree.mjs +118 -0
- package/dist/lib/flatTree.mjs.map +1 -0
- package/dist/lib/motion.cjs +2 -0
- package/dist/lib/motion.cjs.map +1 -0
- package/dist/lib/motion.mjs +43 -0
- package/dist/lib/motion.mjs.map +1 -0
- package/dist/lib/selection.cjs +2 -0
- package/dist/lib/selection.cjs.map +1 -0
- package/dist/lib/selection.mjs +28 -0
- package/dist/lib/selection.mjs.map +1 -0
- package/dist/lib/treeCascade.cjs +2 -0
- package/dist/lib/treeCascade.cjs.map +1 -0
- package/dist/lib/treeCascade.mjs +52 -0
- package/dist/lib/treeCascade.mjs.map +1 -0
- package/dist/lib/virtual.cjs +2 -0
- package/dist/lib/virtual.cjs.map +1 -0
- package/dist/lib/virtual.mjs +54 -0
- package/dist/lib/virtual.mjs.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# @mortenbrudvik/waveui
|
|
2
2
|
|
|
3
|
-
A React component library inspired by the Fluent UI 2 design language:
|
|
3
|
+
A React component library inspired by the Fluent UI 2 design language: 80 accessible, composable components built with TypeScript, Tailwind CSS 4 and CSS custom properties, with light, dark and high-contrast themes and right-to-left support.
|
|
4
4
|
|
|
5
5
|
> This is **not** Microsoft's official `@fluentui/react-components` package.
|
|
6
6
|
|
|
7
7
|
- [Requirements](#requirements) · [Installation](#installation) · [Quick start](#quick-start)
|
|
8
8
|
- [Styles](#styles) · [Theming](#theming) · [Global effects](#global-effects) · [Styling hooks](#styling-hooks)
|
|
9
9
|
- [React Server Components](#react-server-components) · [Components](#components) · [Usage notes](#usage-notes)
|
|
10
|
-
- [Keyboard support](#keyboard-support) · [Hooks and utilities](#hooks-and-utilities) · [Upgrading from 0.13](#upgrading-from-013) · [Upgrading from 0.12](#upgrading-from-012) · [Upgrading from 0.11](#upgrading-from-011) · [Upgrading from 0.10](#upgrading-from-010) · [Upgrading from 0.9](#upgrading-from-09) · [Upgrading from 0.8](#upgrading-from-08) · [Upgrading from 0.7](#upgrading-from-07) · [Upgrading from 0.6](#upgrading-from-06) · [Upgrading from 0.5](#upgrading-from-05) · [Upgrading from 0.4](#upgrading-from-04) · [Development](#development)
|
|
10
|
+
- [Large data](#large-data) · [Keyboard support](#keyboard-support) · [Hooks and utilities](#hooks-and-utilities) · [Upgrading from 0.15](#upgrading-from-015) · [Upgrading from 0.14](#upgrading-from-014) · [Upgrading from 0.13](#upgrading-from-013) · [Upgrading from 0.12](#upgrading-from-012) · [Upgrading from 0.11](#upgrading-from-011) · [Upgrading from 0.10](#upgrading-from-010) · [Upgrading from 0.9](#upgrading-from-09) · [Upgrading from 0.8](#upgrading-from-08) · [Upgrading from 0.7](#upgrading-from-07) · [Upgrading from 0.6](#upgrading-from-06) · [Upgrading from 0.5](#upgrading-from-05) · [Upgrading from 0.4](#upgrading-from-04) · [Development](#development)
|
|
11
11
|
|
|
12
12
|
## Requirements
|
|
13
13
|
|
|
@@ -372,8 +372,8 @@ export function checkedRows(tree: HTMLElement) {
|
|
|
372
372
|
|
|
373
373
|
- Every component and hook module of the package starts with `"use client"`. The entry point, `cn`, the slot helpers, the class-name constants (`buttonClassNames`, …), `hsvToHex`, `hexToHsv`, `createToaster`, the date helpers (`addDays`, `addMonths`, `isSameDay`, `startOfWeek`, `getWeekNumber`), `getInitials`, `partitionAvatarGroupItems`, the breadcrumb helpers (`partitionBreadcrumbItems`, `truncateBreadcrumbLongName`, `truncateBreadcrumbLongTooltip`, `isTruncatableBreadcrumbContent`), `createTableColumn` and the types are server-safe, so a Server Component can import from `@mortenbrudvik/waveui`, read the constants, create a toaster handle, compute dates and render Wave components with serializable props. A DataGrid column with functions (`compare`, `renderCell`) is not serializable: define such columns in a client component.
|
|
374
374
|
- A Server Component **cannot dot into a client component**: `Card.Header` throws "Cannot access Header on the server". Every sub-component is therefore also exported under a flat name. Use the flat names in Server Components and either form in client components.
|
|
375
|
-
- Every compound can be composed in a Server Component. Parts written there reach the client as lazy references, and the compounds recognise them, so the server HTML and the client behaviour are the same as in a client file: Accordion items, Carousel slides, the options of Combobox, Dropdown and Listbox, the parts and options of TagPicker, DataGrid parts (`DataGrid.SelectionCell` included), the Table parts, Overflow items and dividers, Breadcrumb items (collapsed into the overflow menu too), the TeachingPopover parts, Drawer and Menu triggers, the Drawer header, body and footer parts, the tags of a TagGroup and the halves of an InteractionTag, the header of a `Menu.Group`, the halves of a `Menu.SplitGroup`, List, TabList and Tree items, Nav items, categories, section headers, dividers and app items, the header, body and footer of a NavDrawer, the stars of a Rating or RatingDisplay, the parts of Toast and MessageBar, the ColorPicker parts, `SwatchPicker.Row` and the swatches (`ColorSwatch`, `ImageSwatch`, `EmptySwatch`, in rows of the grid layout or the implicit ones), `Tree.ItemPersonaLayout`, a `Label` or `InfoLabel` passed as the `label` of a Field, and a `Button` passed as a MessageBar, SearchBox or Tag dismiss slot.
|
|
376
|
-
- Event handlers and other functions cannot be passed from a Server Component; put interactive parts in a `'use client'` component.
|
|
375
|
+
- Every compound can be composed in a Server Component. Parts written there reach the client as lazy references, and the compounds recognise them, so the server HTML and the client behaviour are the same as in a client file: Accordion items, Carousel slides, the options of Combobox, Dropdown and Listbox, the parts and options of TagPicker, DataGrid parts (`DataGrid.SelectionCell` included), the Table parts, Overflow items and dividers, Breadcrumb items (collapsed into the overflow menu too), the TeachingPopover parts, Drawer and Menu triggers, the Drawer header, body and footer parts, the tags of a TagGroup and the halves of an InteractionTag, the header of a `Menu.Group`, the halves of a `Menu.SplitGroup`, List, TabList and Tree items, Nav items, categories, section headers, dividers and app items, the header, body and footer of a NavDrawer, the stars of a Rating or RatingDisplay, the parts of Toast and MessageBar, the bars of a MessageBarGroup, the ColorPicker parts, `SwatchPicker.Row` and the swatches (`ColorSwatch`, `ImageSwatch`, `EmptySwatch`, in rows of the grid layout or the implicit ones), `Tree.ItemPersonaLayout`, a `Label` or `InfoLabel` passed as the `label` of a Field, and a `Button` passed as a MessageBar, SearchBox or Tag dismiss slot.
|
|
376
|
+
- Event handlers and other functions cannot be passed from a Server Component; put interactive parts in a `'use client'` component. That includes every windowed or data-driven component: a `virtualWindow` (from the `useVirtualWindow` hook), `renderOption`, a FlatTree's model and `renderItem`, a windowed List's function child and `isSelectionDisabled` are client values. The server HTML of a windowed component holds its first window (the top of the list in the window's `initialViewportSize`), and that of a closed data-mode Combobox or Dropdown no options.
|
|
377
377
|
- A `Popover`, `Dialog`, overlay `Drawer` or `NavDrawer`, popup `Menu`, `Combobox`, `Dropdown`, `DatePicker`, `TimePicker` or `TagPicker` rendered on the server with `defaultOpen` (or `open`) is closed in the server HTML and opens once hydrated, so no ARIA reference points at a popup that is not there. `onOpenChange` is not called for it.
|
|
378
378
|
- An inline `Drawer` or `NavDrawer` (`type="inline"`) renders in the server HTML: open, or `hidden` when it is closed and kept mounted (`unmountOnClose={false}`).
|
|
379
379
|
- A TeachingPopover with a `TeachingPopover.Trigger` written directly inside it starts closed. One that points at its Trigger or a `target` renders its surface once hydrated (the surface sits in a portal). Write `TeachingPopover.Surface` directly inside TeachingPopover too: a Surface rendered by a component of yours is found only once it has mounted, so the server HTML holds the popover's own surface as well (two dialogs with the same title id) until then. A Surface with children of yours is named by its `TeachingPopover.Title` once mounted (as a Dialog is by `Dialog.Title`), so its server HTML has no `aria-labelledby`.
|
|
@@ -457,6 +457,7 @@ export function SaveArea() {
|
|
|
457
457
|
| `Dialog` | `DialogTrigger`, `DialogContent`, `DialogTitle`, `DialogFooter`, `DialogClose` |
|
|
458
458
|
| `Drawer` | `DrawerTrigger`, `DrawerTitle`, `DrawerClose`, `DrawerHeader`, `DrawerHeaderTitle`, `DrawerHeaderNavigation`, `DrawerBody`, `DrawerFooter` |
|
|
459
459
|
| `Dropdown` | `DropdownOption`, `DropdownOptionGroup` |
|
|
460
|
+
| `FlatTree` | `FlatTreeItem` (the row the tree renders; it is not written by hand) |
|
|
460
461
|
| `InteractionTag` | `InteractionTagPrimary`, `InteractionTagSecondary` |
|
|
461
462
|
| `List` | `ListItem` |
|
|
462
463
|
| `Listbox` | `ListboxOption`, `ListboxOptionGroup` (also `Option`, `OptionGroup`) |
|
|
@@ -472,7 +473,7 @@ export function SaveArea() {
|
|
|
472
473
|
| `Skeleton` | `SkeletonGroup` |
|
|
473
474
|
| `Stepper` | `StepperStep` |
|
|
474
475
|
| `SwatchPicker` | `SwatchPickerRow` |
|
|
475
|
-
| `Table` | `TableHeader`, `TableHeaderCell`, `TableBody`, `TableRow`, `TableCell`, `TableSelectionCell`, `TableCellLayout`, `TableCellActions`
|
|
476
|
+
| `Table` | `TableHeader`, `TableHeaderCell`, `TableBody`, `TableRow`, `TableCell`, `TableSelectionCell`, `TableCellLayout`, `TableCellActions`, `TableResizeHandle` |
|
|
476
477
|
| `TabList` | `TabListTab`, `TabListPanel`, `TabListPanels` |
|
|
477
478
|
| `TagPicker` | `TagPickerControl`, `TagPickerGroup`, `TagPickerInput`, `TagPickerButton`, `TagPickerList`, `TagPickerOption`, `TagPickerOptionGroup` (also `OptionGroup`) |
|
|
478
479
|
| `TeachingPopover` | `TeachingPopoverTrigger`, `TeachingPopoverSurface`, `TeachingPopoverHeader`, `TeachingPopoverTitle`, `TeachingPopoverBody`, `TeachingPopoverFooter`, `TeachingPopoverCarousel`, `TeachingPopoverCarouselNav`, `TeachingPopoverCarouselPageCount`, `TeachingPopoverCarouselFooter` |
|
|
@@ -482,7 +483,7 @@ export function SaveArea() {
|
|
|
482
483
|
|
|
483
484
|
## Components
|
|
484
485
|
|
|
485
|
-
|
|
486
|
+
80 components, plus the `Portal` and `Presence` utilities. Every component accepts `ref` as a prop (React 19), a `className`, and the native attributes of its element. Storybook (`npm run dev`) shows every component with its props, states and themes.
|
|
486
487
|
|
|
487
488
|
Your `className` is merged last with `cn()`, so it replaces a conflicting class **of the same variant**: `bg-error` replaces a resting `bg-primary`. A class behind a variant is replaced only by a class with the same variant. That matters for the gated hover and pressed classes (`not-disabled:not-aria-disabled:hover:…` / `…:active:…`) and the state classes (`data-[selected]:…`, `aria-disabled:…`): they stay next to yours and win while their state applies, because they are more specific. A bare `hover:bg-error` therefore does not override a Button's built-in hover color while the Button is enabled. It still paints on hover while the Button is disabled or `aria-disabled`, and on `appearance="transparent"`, which has no hover background. To override the gated and state classes, use the same prefix (`not-disabled:not-aria-disabled:hover:bg-error`, `data-[selected]:bg-error`) or the important modifier (`hover:bg-error!`); see [Buttons](#buttons).
|
|
488
489
|
|
|
@@ -513,9 +514,9 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
513
514
|
| `SearchBox` | Search input with a clear button; `size`, `appearance`, `onValueChange`; input attributes go to the `<input>`. The root draws the field, so `className` and `style` style the field box, and `contentBefore`/`contentAfter` sit beside the text. |
|
|
514
515
|
| `Slider` | Styled native range input, filled up to the thumb; `size` (`small`, `medium`); `onValueChange(number)`. |
|
|
515
516
|
| `SpinButton` | Numeric input with step buttons; `size`, `appearance`; typed text is a draft committed on blur or Enter; `min`, `max`, `step`, `largeStep`, `precision`; `displayValue` shows formatted text; `allowEmpty` allows a `null` value; holding a step button repeats it (see [SpinButton](#spinbutton)). |
|
|
516
|
-
| `Combobox` | Editable combobox: typing filters the options (`filter` replaces the match, `query` controls the typed text); `multiselect` selects several values; `freeform` makes the typed text the value; a chevron (`expandIcon`) and `clearable`. `Combobox.Option`, `Combobox.OptionGroup` (see [Multi-select pickers](#multi-select-pickers)
|
|
517
|
-
| `Dropdown` | Select-only combobox (a button) with typeahead; `multiselect` selects several values; `clearable`, `expandIcon` and `renderValue`. `Dropdown.Option`, `Dropdown.OptionGroup
|
|
518
|
-
| `Listbox` | A list of options in the page that holds focus itself (`aria-activedescendant`), single-select or `multiselect`, with typeahead, groups and native form support; `Listbox.Option`, `Listbox.OptionGroup`. The listbox primitives under the pickers are public, for pickers of your own (see [Listbox and custom pickers](#listbox-and-custom-pickers)). |
|
|
517
|
+
| `Combobox` | Editable combobox: typing filters the options (`filter` replaces the match, `query` controls the typed text); `multiselect` selects several values; `freeform` makes the typed text the value; a chevron (`expandIcon`) and `clearable`. `Combobox.Option`, `Combobox.OptionGroup`, or the options as data (`items`, `renderOption`) with an optional `virtualWindow` for long lists (see [Multi-select pickers](#multi-select-pickers), [Filtering and async search](#filtering-and-async-search) and [Large data](#large-data)). |
|
|
518
|
+
| `Dropdown` | Select-only combobox (a button) with typeahead; `multiselect` selects several values; `clearable`, `expandIcon` and `renderValue`. `Dropdown.Option`, `Dropdown.OptionGroup`, or the options as data (`items`, `renderOption`) with an optional `virtualWindow` (see [Large data](#large-data)). |
|
|
519
|
+
| `Listbox` | A list of options in the page that holds focus itself (`aria-activedescendant`), single-select or `multiselect`, with typeahead, groups and native form support; `Listbox.Option`, `Listbox.OptionGroup`, or the options as data (`items`, `renderOption`) with an optional `virtualWindow`. The listbox primitives under the pickers are public, for pickers of your own (see [Listbox and custom pickers](#listbox-and-custom-pickers)). |
|
|
519
520
|
| `TagPicker` | Multi-select combobox that shows the selection as removable tags (Tags in a TagGroup list named "Selected", one Tag size below the picker); `size` (`medium`, `large`, `extra-large`), `appearance`; the input is described by a summary of the selection ("Selected: Apple, Banana"), and additions and removals are announced; `filter` and `query` as in Combobox. A chevron opens the list (`expandIcon`), `clearable` removes every tag, `overflow="count"` keeps the tags on one line with a "+N" count, and `allowCreate` with `onOptionCreate` adds a tag of the user's own text. Composable: `TagPicker.Control`, `.Group`, `.Input` or `.Button` (a trigger without text entry), `.List`, `.Option` (`media`, `secondaryContent`) and `.OptionGroup`; a part without children renders its defaults (see [TagPicker parts and free tagging](#tagpicker-parts-and-free-tagging)). |
|
|
520
521
|
| `DatePicker` | Date input with a calendar dialog (month and year views, week numbers, marked days, optional "Go to today" and Close buttons); `size`, `appearance`; locale-aware format and parse, `minDate`, `maxDate`, `disabledDates`, `onInvalidInput`. |
|
|
521
522
|
| `Calendar` | The DatePicker's calendar in the page (`role="group"`): month and year views, week numbers, marked days and custom day content, week, work-week or month ranges, "Go to today", native form support (see [Calendar](#calendar)). |
|
|
@@ -542,12 +543,12 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
542
543
|
| `Tag` | Chip: `appearance` (`filled`, `outline`, `brand`), `size` (`medium` 28px, `small`, `extra-small`), `shape` (`circular`, `rounded`), `disabled`, a look-only `selected`, `value` (its key in a TagGroup) and the `media` (an Avatar), `icon` and `secondaryText` slots; an optional Dismiss button (`dismissible`, `onDismiss`, `dismissLabel`) that also dismisses on Delete and Backspace. `onDismiss` and `dismissIcon` need `dismissible` (a development warning says so). In a TagGroup focus moves for you when a tag is removed; on its own, move it yourself (see [Tags](#tags)). |
|
|
543
544
|
| `TagGroup` | A set of tags: a toolbar (one Tab stop, arrow keys) or, with `role="list"`, a list; default `size`, `appearance`, `shape`, `dismissible` and `disabled` for its tags; `onDismiss(value)`, focus to the next tag (else the previous one, else `fallbackFocusRef`) when the focused tag is removed; selectable InteractionTags through `value`/`defaultValue`/`onValueChange`, with `name`, `form` and `required`. |
|
|
544
545
|
| `InteractionTag` | A tag with an action: `InteractionTag.Primary` (a button, or a toggle in a selecting TagGroup; `media`, `icon`, `secondaryText`) and an optional `InteractionTag.Secondary` (a separate Dismiss button named "Dismiss <text>"); `size`, `appearance`, `shape`, `disabled`, `selected`, `value`. |
|
|
545
|
-
| `InfoLabel` | A real `<label>` (`children`, `htmlFor`, `required`, `size`, `weight`, `disabled`) with an info button after it that shows `info` (text, links or other content) in a note; `openOnHover`, `infoButtonLabel
|
|
546
|
-
| `InfoButton` | A button with the info glyph that shows `info` in a `role="note"` next to it, without moving focus: opens on click, on mouse hover and on keyboard focus (`openOnHover={false}`: click only); Tab from the button enters the note; `size
|
|
546
|
+
| `InfoLabel` | A real `<label>` (`children`, `htmlFor`, `required`, `size`, `weight`, `disabled`) with an info button after it that shows `info` (text, links or other content) in a note; `openOnHover`, `infoButtonLabel`, `motion` (its note's). Usable as the `label` of a Field. |
|
|
547
|
+
| `InfoButton` | A button with the info glyph that shows `info` in a `role="note"` next to it, without moving focus: opens on click, on mouse hover and on keyboard focus (`openOnHover={false}`: click only); Tab from the button enters the note; `size`; the note fades and slides in (`motion`). Named "Information" (`aria-label`). |
|
|
547
548
|
| `Persona` | Avatar with up to four lines of text (`primaryText` to `quaternaryText`), `textPosition` (`after`, `before`, `below`), `presenceOnly` and presence; sizes up to `huge`. |
|
|
548
549
|
| `Divider` | Horizontal or vertical separator, optionally labelled. |
|
|
549
550
|
| `Image` | `<img>` with `fit`, `shape`, `shadow`, `bordered`; warns in development when `alt` is missing. Never wider than its parent; with the default `fit` its height follows its width. `fit="none"` keeps the top-left corner in view, `"center"` the middle. |
|
|
550
|
-
| `List` | Plain list, selectable listbox (single or multiple) or, with item `action`s, a grid; `navigationMode` (`none`, `items`, `composite`) gives a list without selection arrow keys, or pins the grid; `List.Item onAction`, `disabledSelection` and check marks (`checkmark`); in a selectable or navigable List item values are unique (development warning; see [Lists](#lists)). |
|
|
551
|
+
| `List` | Plain list, selectable listbox (single or multiple) or, with item `action`s, a grid; `navigationMode` (`none`, `items`, `composite`) gives a list without selection arrow keys, or pins the grid; `List.Item onAction`, `disabledSelection` and check marks (`checkmark`); `virtualWindow` with `count` and a function child renders only the items in view of a long list; in a selectable or navigable List item values are unique (development warning; see [Lists](#lists)). |
|
|
551
552
|
|
|
552
553
|
### Typography
|
|
553
554
|
|
|
@@ -560,10 +561,11 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
560
561
|
| Component | Description |
|
|
561
562
|
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
562
563
|
| `Card` | Surface with `Card.Header` (`image`, `action`), `Card.Preview`, `Card.Body`, `Card.Footer`; `appearance`, `size`, `orientation`; optionally selectable (`onSelect`, `selected`, `selectionControl`, `checkbox`) with a `floatingAction` (see [Cards, tabs and carousels](#cards-tabs-and-carousels)). |
|
|
563
|
-
| `Accordion` | Disclosure sections (`Accordion.Item`, `.Trigger`, `.Panel`); single (default) or `type="multiple"`; `headingLevel`; item values are unique (development warning). |
|
|
564
|
-
| `TabList` | Tabs (`TabList.Tab`, `.Panel`, `.Panels`) with automatic activation, or manual with `selectTabOnFocus={false}`; `orientation`, `appearance` (`transparent`, `subtle`, `subtle-circular`, `filled-circular`), `size`, a `Tab` `icon` (alone: icon-only); tab values are unique (development warning). |
|
|
565
|
-
| `Tree` | Hierarchical tree (`Tree.Item`) with expand/collapse and typeahead; single selection, or checkboxes with a cascading mixed state (`selectionMode="multiple"`); row `actions`, `aside` and `Tree.ItemPersonaLayout`; `navigationMode="treegrid"` (see [Trees](#trees)). Nested items are `Tree.Item` elements in their parent's children (directly, in Fragments or from a render function), not rendered by a recursive component; values are unique. |
|
|
566
|
-
| `
|
|
564
|
+
| `Accordion` | Disclosure sections (`Accordion.Item`, `.Trigger`, `.Panel`); single (default) or `type="multiple"`; `headingLevel`; panels collapse as they open and close (`Accordion.Panel motion`); item values are unique (development warning). |
|
|
565
|
+
| `TabList` | Tabs (`TabList.Tab`, `.Panel`, `.Panels`) with automatic activation, or manual with `selectTabOnFocus={false}`; `orientation`, `appearance` (`transparent`, `subtle`, `subtle-circular`, `filled-circular`), `size`, a `Tab` `icon` (alone: icon-only); the selection line slides to the new tab (`motion`); tab values are unique (development warning). |
|
|
566
|
+
| `Tree` | Hierarchical tree (`Tree.Item`) with expand/collapse and typeahead; single selection, or checkboxes with a cascading mixed state (`selectionMode="multiple"`); row `actions`, `aside` and `Tree.ItemPersonaLayout`; `navigationMode="treegrid"`; groups collapse as they open and close (`motion`) (see [Trees](#trees)). Nested items are `Tree.Item` elements in their parent's children (directly, in Fragments or from a render function), not rendered by a recursive component; values are unique. |
|
|
567
|
+
| `FlatTree` | A tree from flat data (`useFlatTree(items)`: items with a `parentValue`, in any order) for trees of thousands of items: the visible items as sibling `treeitem`s with their level and position, the nested Tree's keys, single selection and cascading checkboxes, and a `virtualWindow` that renders only the items in view (see [Trees](#trees) and [Large data](#large-data)). |
|
|
568
|
+
| `Carousel` | Slides (`Carousel.Item` children, directly or in Fragments; a component that renders `Carousel.Item` is not a slide) with previous/next, a slide picker and optional auto-rotation with a pause control. `groupSize` (a number or `auto`) shows several at once, `draggable` drags them, `Carousel.Item` `thumbnail` shows thumbnails, `controlsLayout` places the buttons, `Carousel.Button` replaces one, and `motion` (`slide`, `fade` or `null`) with `motionDuration` sets how the page changes. |
|
|
567
569
|
| `Overflow` | Hides items that do not fit in one row (or column, `orientation="vertical"`) and renders an overflow button (`useOverflowMenu` lists the hidden items); items hide by `priority` unless `pinned`, from the end or the start (`overflowDirection`), in groups with `Overflow.Divider`; `buttonPosition`, `minimumVisible`, `asChild` items, `onOverflowChange`; item ids are unique (development warning). The row has a 4px padding, so its items' focus rings are not clipped (see [Overflow](#overflow)). |
|
|
568
570
|
| `Grid` | CSS grid with a column count and token gaps. |
|
|
569
571
|
| `Stack` | Vertical or horizontal stack (`orientation`, `gap`). |
|
|
@@ -574,19 +576,20 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
574
576
|
| Component | Description |
|
|
575
577
|
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
576
578
|
| `MessageBar` | Inline status message (`info`, `success`, `warning`, `error`) with hidden status text, `MessageBar.Title`, `MessageBar.Actions` that reflow with `layout`, and an optional dismiss button (see [Message bars](#message-bars)). |
|
|
577
|
-
| `
|
|
579
|
+
| `MessageBarGroup` | A stack of MessageBars that animates the bars it gains and loses: a removed bar fades out while its row closes, so the bars below glide up; `animate` (`exit-only`, `both`), `motion`. A component of its own, so an import of only MessageBar loads no motion (see [Message bars](#message-bars)). |
|
|
580
|
+
| `ProgressBar` | Determinate or indeterminate progress; name it with `label` (inside a `Field`, the Field's label names it); `color`; `indeterminateMotion` replaces the indeterminate sweep. |
|
|
578
581
|
| `Skeleton` | Loading placeholder (`width`, `height`, `shape`); `Skeleton.Group` marks the loading region busy. |
|
|
579
582
|
| `Spinner` | Loading indicator announced as "Loading" (localize with `label`); `appearance="inverted"` for brand surfaces; `delay`. |
|
|
580
|
-
| `Toast` | Notifications shown by `<Toaster>` (`limit` queues the rest): a `<Toast>` with `Toast.Title`, `.Body`, `.Footer` and `.Close`, or text, dispatched through `useToastController()` or a `createToaster()` handle; a keyboard shortcut, Escape and Delete (see [Toasts](#toasts)). |
|
|
583
|
+
| `Toast` | Notifications shown by `<Toaster>` (`limit` queues the rest; `motion` replaces their slide, fade and collapse): a `<Toast>` with `Toast.Title`, `.Body`, `.Footer` and `.Close`, or text, dispatched through `useToastController()` or a `createToaster()` handle; a keyboard shortcut, Escape and Delete (see [Toasts](#toasts)). |
|
|
581
584
|
|
|
582
585
|
### Navigation
|
|
583
586
|
|
|
584
587
|
| Component | Description |
|
|
585
588
|
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
586
589
|
| `Breadcrumb` | Trail of links, buttons or text (`Breadcrumb.Item`, `current`); `maxDisplayedItems` or `overflow="auto"` collapse the middle items into a "…" menu, `size`, `focusMode` (`tab`, `arrow`), `divider` (see [Breadcrumbs](#breadcrumbs)). |
|
|
587
|
-
| `Menu` | Popup menu (`Menu.Trigger`, `Menu.Popover`, which scrolls when it is taller than the viewport) or static menu; `Menu.Item`, `Menu.Divider`; checkbox, radio and switch items bound to `checkedValues`, `Menu.Group`, `Menu.ItemLink`, submenus, `Menu.SplitGroup`, hover opening and context menus (see [Menus](#menus)). |
|
|
588
|
-
| `Nav` | Side navigation with items, collapsible categories and sub-items; links or buttons; a collapsed category that holds the current page is marked. `Nav.SectionHeader` (a heading, `headingLevel`), `Nav.Divider` (decorative), `Nav.AppItem` and `Nav.AppItemStatic` (the app's name at the top), and `actions` on `Nav.Item` and `Nav.SubItem` (your buttons beside the link, shown on hover and focus). Every child of its list is a list item (a development warning says so). |
|
|
589
|
-
| `NavDrawer` | Nav in a drawer panel (see [Navigation drawers and app shells](#navigation-drawers-and-app-shells)): `type` `overlay` (a modal or non-modal dialog around a `<nav>`) or `inline` (part of the page layout), `position` (default `start`), `size` (default `small`, 320px), `separator`, `modalType`, `unmountOnClose`, `finalFocusRef`, `backdrop`, Nav's value and category props and `onItemSelect(value)` on every activation; `NavDrawer.Header`, `NavDrawer.Body` (`focusMode` `tab` or `arrow`) and `NavDrawer.Footer`. |
|
|
590
|
+
| `Menu` | Popup menu (`Menu.Trigger`, `Menu.Popover`, which scrolls when it is taller than the viewport) or static menu; `Menu.Item`, `Menu.Divider`; checkbox, radio and switch items bound to `checkedValues`, `Menu.Group`, `Menu.ItemLink`, submenus, `Menu.SplitGroup`, hover opening and context menus; menus fade and slide in (`Menu.Popover motion`) (see [Menus](#menus)). |
|
|
591
|
+
| `Nav` | Side navigation with items, collapsible categories and sub-items; links or buttons; a collapsed category that holds the current page is marked; categories collapse as they open and close (`Nav.Category motion`, and `expandIcon` with `expandIconMotion` for its chevron). `Nav.SectionHeader` (a heading, `headingLevel`), `Nav.Divider` (decorative), `Nav.AppItem` and `Nav.AppItemStatic` (the app's name at the top), and `actions` on `Nav.Item` and `Nav.SubItem` (your buttons beside the link, shown on hover and focus). Every child of its list is a list item (a development warning says so). |
|
|
592
|
+
| `NavDrawer` | Nav in a drawer panel (see [Navigation drawers and app shells](#navigation-drawers-and-app-shells)): `type` `overlay` (a modal or non-modal dialog around a `<nav>`) or `inline` (part of the page layout), `position` (default `start`), `size` (default `small`, 320px), `separator`, `modalType`, `unmountOnClose`, `finalFocusRef`, `backdrop`, `motion`, `backdropMotion`, Nav's value and category props and `onItemSelect(value)` on every activation; `NavDrawer.Header`, `NavDrawer.Body` (`focusMode` `tab` or `arrow`) and `NavDrawer.Footer`. |
|
|
590
593
|
| `Hamburger` | A `subtle` icon-only Button with the navigation glyph, named "Navigation"; inside a NavDrawer it toggles the drawer (`aria-expanded`, `aria-controls`). |
|
|
591
594
|
| `Stepper` | Multi-step progress (`Stepper.Step`), horizontal or vertical, optionally `linear`. |
|
|
592
595
|
| `Pagination` | Page buttons with ellipses, previous/next and optional first/last. |
|
|
@@ -595,24 +598,24 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
595
598
|
|
|
596
599
|
| Component | Description |
|
|
597
600
|
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
598
|
-
| `Dialog` | Modal dialog (`modalType="alert"` for a confirmation, `"non-modal"` for one the page stays usable around): `Dialog.Trigger` (toggles), `Dialog.Content` (`size` `small`, `medium`, `large`, `full`; `closeLabel` names its Close button, `closeButton` replaces or hides it, `backdrop`, `titleAction`, `unmountOnClose`), `Dialog.Title` (`action`), `Dialog.Footer` (stays in view), `Dialog.Close` (see [Dialogs and triggers](#dialogs-and-triggers)). |
|
|
599
|
-
| `Drawer` | Side or bottom panel (see [Drawers](#drawers)): `type` `overlay` (`modalType` `modal`, `alert` or `non-modal`) or `inline`; `position` (`start`, `end`, `left`, `right`, `bottom`), `size` (`small` 320px, `medium`, `large`, `full`), `separator`, `backdrop`, `closeButton`, `closeLabel`, `unmountOnClose`; `Drawer.Trigger` (toggles), `Drawer.Title`, `Drawer.Close`, and the layout parts `Drawer.Header`, `Drawer.HeaderTitle`, `Drawer.HeaderNavigation`, `Drawer.Body` and `Drawer.Footer`, whose separators show while content is scrolled under them. |
|
|
600
|
-
| `Popover` | Non-modal popup anchored to `Popover.Trigger` or a `target`; `side`, `align`; hover cards (`openOnHover`) and context popovers (`openOnContext`). |
|
|
601
|
-
| `Tooltip` | Hover and focus text for its child, as a description or (`relationship="label"`) a name; `open`/`defaultOpen`/`onOpenChange`; `openDelay`, `closeDelay
|
|
602
|
-
| `TeachingPopover` | Step-by-step onboarding popover, optionally pointing at a `target`; `primaryAction` and `secondaryAction` replace its buttons, `footerLayout`, `onFinish`; composable from parts: `TeachingPopover.Trigger`, `.Surface`, `.Header`, `.Title`, `.Body`, `.Footer` and the carousel parts `.Carousel`, `.CarouselNav`, `.CarouselPageCount`, `.CarouselFooter` (see [Teaching popovers and tours](#teaching-popovers-and-tours)). |
|
|
601
|
+
| `Dialog` | Modal dialog (`modalType="alert"` for a confirmation, `"non-modal"` for one the page stays usable around): `Dialog.Trigger` (toggles), `Dialog.Content` (`size` `small`, `medium`, `large`, `full`; `closeLabel` names its Close button, `closeButton` replaces or hides it, `backdrop`, `titleAction`, `unmountOnClose`, `motion`, `backdropMotion`), `Dialog.Title` (`action`), `Dialog.Footer` (stays in view), `Dialog.Close` (see [Dialogs and triggers](#dialogs-and-triggers)). |
|
|
602
|
+
| `Drawer` | Side or bottom panel (see [Drawers](#drawers)): `type` `overlay` (`modalType` `modal`, `alert` or `non-modal`) or `inline`; `position` (`start`, `end`, `left`, `right`, `bottom`), `size` (`small` 320px, `medium`, `large`, `full`), `separator`, `backdrop`, `closeButton`, `closeLabel`, `unmountOnClose`, `motion` (an overlay's slide, an inline drawer's collapse), `backdropMotion`; `Drawer.Trigger` (toggles), `Drawer.Title`, `Drawer.Close`, and the layout parts `Drawer.Header`, `Drawer.HeaderTitle`, `Drawer.HeaderNavigation`, `Drawer.Body` and `Drawer.Footer`, whose separators show while content is scrolled under them. |
|
|
603
|
+
| `Popover` | Non-modal popup anchored to `Popover.Trigger` or a `target`; `side`, `align`; hover cards (`openOnHover`) and context popovers (`openOnContext`); the content fades and slides in (`Popover.Content motion`). |
|
|
604
|
+
| `Tooltip` | Hover and focus text for its child, as a description or (`relationship="label"`) a name; `open`/`defaultOpen`/`onOpenChange`; `openDelay`, `closeDelay`; a fade (`motion`). |
|
|
605
|
+
| `TeachingPopover` | Step-by-step onboarding popover, optionally pointing at a `target`; `primaryAction` and `secondaryAction` replace its buttons, `footerLayout`, `onFinish`, `motion`; composable from parts: `TeachingPopover.Trigger`, `.Surface`, `.Header`, `.Title`, `.Body`, `.Footer` and the carousel parts `.Carousel`, `.CarouselNav`, `.CarouselPageCount`, `.CarouselFooter` (see [Teaching popovers and tours](#teaching-popovers-and-tours)). |
|
|
603
606
|
|
|
604
607
|
### Tables
|
|
605
608
|
|
|
606
609
|
| Component | Description |
|
|
607
610
|
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
608
|
-
| `Table` | Static table: `Table.Header`, `Table.HeaderCell` (`sortable`, with your sort state), `Table.Body`, `Table.Row`, `Table.Cell`, and the cell parts `Table.SelectionCell`, `Table.CellLayout` and `Table.
|
|
609
|
-
| `DataGrid` | Interactive grid (APG grid keyboard model) with row selection and sortable headers; header from children or `columns`, rows from `items` with typed columns (`createTableColumn`) that sort themselves; `selectOnRowClick`, `selectionAppearance`, `subtleSelection`, `DataGrid.SelectionCell`; row ids are unique (development warning; see [Sorting and selecting in a DataGrid](#sorting-and-selecting-in-a-datagrid)). |
|
|
611
|
+
| `Table` | Static table: `Table.Header`, `Table.HeaderCell` (`sortable`, with your sort state), `Table.Body`, `Table.Row`, `Table.Cell`, and the cell parts `Table.SelectionCell`, `Table.CellLayout`, `Table.CellActions` and `Table.ResizeHandle` (a column's resize separator); `striped`; scrolls horizontally; its state can come from the headless `useTableFeatures` with sort, selection and column-sizing plugins (see [Static tables](#static-tables)). |
|
|
612
|
+
| `DataGrid` | Interactive grid (APG grid keyboard model) with row selection and sortable headers; header from children or `columns`, rows from `items` with typed columns (`createTableColumn`) that sort themselves; with `items`, selection and select-all follow the data (`isSelectionDisabled`); `selectOnRowClick`, `selectionAppearance`, `subtleSelection`, `DataGrid.SelectionCell`; `resizableColumns` and column widths (`columnSizingOptions`, `columnWidths`); `virtualWindow` renders only the rows in view; row ids are unique (development warning; see [Sorting and selecting in a DataGrid](#sorting-and-selecting-in-a-datagrid) and [Large data](#large-data)). |
|
|
610
613
|
|
|
611
614
|
### Provider
|
|
612
615
|
|
|
613
616
|
| Component | Description |
|
|
614
617
|
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
615
|
-
| `WaveProvider` | Theme (`light`, `dark`, `high-contrast`), direction, portal container and `inputDefaults` (the default `size` and `appearance` of the text inputs and pickers). |
|
|
618
|
+
| `WaveProvider` | Theme (`light`, `dark`, `high-contrast`), direction, portal container and `inputDefaults` (the default `size` and `appearance` of the text inputs and pickers) and `motion` (`'none'` turns the default motion of every animated component below it off). |
|
|
616
619
|
|
|
617
620
|
`Portal` renders its children into `document.body` (or the provider's `portalContainer`) inside a themed `wave-portal` wrapper; use it for your own overlays.
|
|
618
621
|
|
|
@@ -620,7 +623,7 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
620
623
|
|
|
621
624
|
| Component | Description |
|
|
622
625
|
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
623
|
-
| `Presence` | Shows and hides its child with CSS enter and exit motion: `data-presence` phases, `inert` while it exits, unmounted when the motion ends; `usePresence` is the hook form (see [Enter and exit motion](#enter-and-exit-motion)). |
|
|
626
|
+
| `Presence` | Shows and hides its child with CSS enter and exit motion: `data-presence` phases, `inert` while it exits, unmounted when the motion ends; `usePresence` is the hook form. The components' own motion is built on it (see [Enter and exit motion](#enter-and-exit-motion)). |
|
|
624
627
|
|
|
625
628
|
## Usage notes
|
|
626
629
|
|
|
@@ -1086,10 +1089,11 @@ export function FruitList() {
|
|
|
1086
1089
|
- **Focus and selection.** One Tab stop. The active option exists only while the list has focus: the selected option (with `multiselect`, the first selected one in list order), else the first; while no option can be active, the list draws its own focus ring. Space and Enter select, so the selection does not follow the active option; a single-select Listbox never deselects. A press on an option focuses the list and makes that option active without scrolling, so the click selects it even while the selected option is scrolled out of view. Tab, Escape and the Alt+arrow keys are left to the page: an enclosing Dialog closes on Escape.
|
|
1087
1090
|
- **Props.** `multiselect` (an array value, announced toggles; see [Multi-select pickers](#multi-select-pickers)), `onActiveOptionChange` (`null` when focus leaves the list), `disabled` (out of the Tab order, selects and submits nothing), `disabledOptionsFocusable`, `autoFocus`, `name`, `form` and `required`, and Field wiring. It has no built-in height: give it one through `className` and it scrolls.
|
|
1088
1091
|
- **Option groups.** `OptionGroup`'s `label` is optional and takes any text content (an icon next to the text is fine). Without one no heading renders, and `aria-label` or `aria-labelledby` names the group; these two always land on the `role="group"` list. A group without a name warns in development.
|
|
1092
|
+
- **Data mode** (Listbox, Combobox, Dropdown): `items` (`ListboxItem`: `value`, `label`, and optionally `textValue`, `disabled` and `hidden`; values unique) replace the option children, which are then neither read nor rendered (passing both warns once). The keys, typeahead, the selected labels and the Combobox filter come from `items`, also for a selected item whose option is not rendered, and every option carries `aria-posinset` and `aria-setsize` among the shown options. `renderOption(item, state)` draws an option (`state`: `selected`, `active`, and `index` among the shown options; `ListboxOptionRenderState`): return an `Option`, or an option of yours on `useListboxOption`, with the item's `value`; it is called again only when its item, state or position, or the function, change, so pass a stable function. A closed Combobox or Dropdown in data mode renders no options (its labels come from `items`). `virtualWindow` windows a long data-mode list (see [Large data](#large-data)).
|
|
1089
1093
|
|
|
1090
1094
|
**Pickers of your own.** The pieces under Dropdown, Combobox and TagPicker are public:
|
|
1091
1095
|
|
|
1092
|
-
- `useListbox(options)` holds the listbox: its keys, typeahead, filtering, the active option and the registration of the options. `mode` is `'select-only'` (a button combobox, as Dropdown), `'editable'` (an `<input>` combobox, as Combobox) or `'standalone'` (a list that holds focus, as Listbox). You own the open state (`open`, `onOpenChange(open, details)`) and the value (`selectedValues`, `onSelect(value, details)`, with `details.item` and `details.event`); spread `getComboboxProps()` on your combobox and attach `onKeyDown` and `onKeyUp`.
|
|
1096
|
+
- `useListbox(options)` holds the listbox: its keys, typeahead, filtering, the active option and the registration of the options. `mode` is `'select-only'` (a button combobox, as Dropdown), `'editable'` (an `<input>` combobox, as Combobox) or `'standalone'` (a list that holds focus, as Listbox). You own the open state (`open`, `onOpenChange(open, details)`) and the value (`selectedValues`, `onSelect(value, details)`, with `details.item` and `details.event`); spread `getComboboxProps()` on your combobox and attach `onKeyDown` and `onKeyUp`. With `items` (data mode) it reads no children, so you may render only the shown options (`listbox.items`), or a window of them: `onScrollToItem(index)` is then called first for every move that scrolls the active option into view, with its index among the shown options, so pass `(index) => virtualWindow.scrollToIndex(index, { align: 'auto' })` and keep the active option rendered.
|
|
1093
1097
|
- `useListboxPopup(options)` dismisses the popup (Escape, a press outside, focus leaving: `DismissReason`) and positions it, and `ListboxSurface` renders the list: in a portaled surface while it is shown, `hidden` in place otherwise, so the options register from the first render. `listClassName` and `surfaceClassName` are merged last (`max-h-*` replaces the list's height). Two duties remain yours: set your combobox's `aria-expanded` from `expanded` after spreading `getComboboxProps()`, and while the listbox is open with nothing to show, close it on Escape yourself (outside an IME composition) without calling `listbox.onKeyDown`, so the Escape reaches an enclosing layer.
|
|
1094
1098
|
- `ListboxProvider` provides the listbox's context to its options when you render the list yourself instead of through `ListboxSurface`. `ListboxSurface`'s `listProps` (`ListboxSurfaceListProps`) pass attributes and a `ref` to the list element; the listbox's own `aria-multiselectable` and `aria-activedescendant` always win.
|
|
1095
1099
|
- Options are `Option` and `OptionGroup` (or `TagPicker.Option`, an Option with `media` and `secondaryContent`), or components of your own on `useListboxOption`, marked with `markListboxElement` so that `collectOptionLabels(children)` knows their labels before they register (in the server HTML and the first render).
|
|
@@ -1419,7 +1423,7 @@ export function ViewMenu() {
|
|
|
1419
1423
|
|
|
1420
1424
|
- **A menu at another element.** `Menu.Popover` `target` (`PopupTarget`) places the menu at an element (hold it in state: `const [button, setButton] = useState<HTMLElement | null>(null)`) or at a `VirtualElement`, a rectangle such as a point (`target={{ getBoundingClientRect: () => rect }}`; an inline object is fine). A controlled menu with a `target` needs no `Menu.Trigger`: name it with `aria-label`, and give a toggle button used as the target `aria-haspopup="menu"` and `aria-expanded` yourself. A press on a target element does not count as outside, so the toggle closes the menu with one click. Without `Menu.Trigger`, Escape (or a close by your app) returns focus to the element that had it when the menu opened, while activating an item or pressing Tab focuses the target element when it can take focus; when you open the menu from the toggle, both are the toggle. The menu's `id` is generated, so the toggle cannot point `aria-controls` at it.
|
|
1421
1425
|
- **Columns.** The item labels line up: once any item of a menu list shows a check or an icon, every item of that list keeps that column (through groups and wrappers too), and the others render an empty placeholder there. A `shortcut` takes its direction from its own text (`dir="auto"`), so "Ctrl+," reads the same in an RTL menu.
|
|
1422
|
-
- **
|
|
1426
|
+
- **Motion.** A menu, and each submenu, fades in and slides 4px away from its trigger (a submenu away from its parent menu, on the side it opened on) over 200ms, and fades and slides back over 100ms. While it exits it stays mounted with `data-presence="exiting"`, `data-state="closed"` and `inert`; focus is already back and Escape already reaches the next layer. `Menu.Popover motion` replaces the motion, or turns it off with `null`: `<Menu.Popover motion="transition-opacity duration-wave-fast data-[presence=exiting]:opacity-0 motion-reduce:transition-none">` only fades out (Tailwind path). 0.14's form, the same classes in `className`, still works and turns the default off. See [Enter and exit motion](#enter-and-exit-motion).
|
|
1423
1427
|
|
|
1424
1428
|
### Toolbars
|
|
1425
1429
|
|
|
@@ -1559,7 +1563,8 @@ export function DeleteDialog({ onDelete }: { onDelete: () => void }) {
|
|
|
1559
1563
|
- **Backdrop.** The backdrop is the surface's sibling, `aria-hidden` (`.wave-dialog-content__backdrop`, `.wave-drawer__backdrop`), and a slot on `Dialog.Content` and Drawer: content renders inside it, a slot object's `className`, `style` and handlers go to it, and its `appearance` (`'dimmed'` or `'transparent'`, Fluent's name) picks the fill. A modal opened while another modal Dialog or Drawer is open below it gets a transparent backdrop, so the page dims once; `backdrop={{ appearance: 'dimmed' }}` dims it again. `null` renders no backdrop (the page stays inert, and a press outside still closes a modal); a boolean is no slot. A non-modal surface or an inline drawer ignores `backdrop`, with a development warning.
|
|
1560
1564
|
- **Close button.** `closeButton` sets the content of the built-in Close button (decorative: the button keeps `closeLabel` as its name, unless a slot object's `aria-label`, `aria-labelledby` or `title` names it); `null` hides it (Escape and `.Close` parts still close). A `<button>` or `Button` passed there is merged into the built-in button, never nested, and its text label names it. Fluent's dialogs have no Close button by default: pass `closeButton={null}` and keep yours, or drop yours.
|
|
1561
1565
|
- **Title actions.** `Dialog.Content`'s `titleAction` (for the `title` prop) and `Dialog.Title`'s `action` place header buttons before the Close button, in the top end corner, outside the heading, so they never join the dialog's name; the title's end padding follows their width. Drawer's `titleAction` and `Drawer.HeaderTitle`'s `action` do the same in a drawer header. Name icon-only buttons.
|
|
1562
|
-
- **Kept mounted.** `unmountOnClose={false}` keeps `Dialog.Content` mounted while closed, `hidden` and `inert`, so a form keeps what was typed; a popup opened inside that is still open when the dialog closes stays open over the page (a development warning says so). Drawers take the same prop. See [
|
|
1566
|
+
- **Kept mounted.** `unmountOnClose={false}` keeps `Dialog.Content` mounted while closed, `hidden` and `inert`, so a form keeps what was typed; a popup opened inside that is still open when the dialog closes stays open over the page (a development warning says so). Drawers take the same prop. See [Drawers](#drawers) for side and bottom panels.
|
|
1567
|
+
- **Motion.** The surface scales up from 85% and fades in over 250ms, and scales down and fades out over 200ms, while the backdrop fades over the same times. A closing dialog unmounts, or hides with `unmountOnClose={false}`, once the surface's motion ends; focus, the trap and the isolation follow the open state at once, and a stacked dialog's backdrop stays transparent while it fades. `Dialog.Content` `motion` and `backdropMotion` replace them, or turn them off with `null`; a backdrop motion longer than the surface's exit is cut off when the surface goes. `backdropMotion` counts as given on a non-modal dialog even when it is `null`, so leave it unset there (a development warning names it). See [Enter and exit motion](#enter-and-exit-motion).
|
|
1563
1568
|
|
|
1564
1569
|
```tsx
|
|
1565
1570
|
import * as React from 'react';
|
|
@@ -1635,7 +1640,7 @@ export function FiltersDrawer() {
|
|
|
1635
1640
|
- **Non-modal.** `modalType="non-modal"` leaves the page usable (see [Dialogs and triggers](#dialogs-and-triggers)); reserve the panel's width (its height at the bottom) in your layout while it is open, so it hides no page control. `modalType="alert"` renders `role="alertdialog"`.
|
|
1636
1641
|
- **Inline.** `type="inline"` renders the panel where the Drawer is, after its direct triggers, as an `<aside>` (a complementary landmark named by its title), also in the server HTML. It is page content: no portal, backdrop, focus trap, inert page or scroll lock, Escape and presses outside never close it, and toasts keep their corner. Its trigger toggles it; opening it moves no focus, and closing it returns focus only when focus was inside. `position` picks its inner edge and `size` its width; `separator` draws the inner edge. Place it in your layout, for example a flex row of the panel and the content. Changing `type` (an overlay below a breakpoint, inline above it) remounts the panel and its content.
|
|
1637
1642
|
- **Kept mounted.** `unmountOnClose={false}` keeps the panel and its content mounted while closed, `hidden` and `inert`, so a form keeps what was typed; an `autoFocus` inside never applies (each open of an overlay focuses its first focusable element), and a kept inline panel is in the server HTML, hidden. A `required` empty field in a closed kept drawer inside a page `<form>` still blocks its submission.
|
|
1638
|
-
- **Motion.**
|
|
1643
|
+
- **Motion.** An overlay panel slides in from its edge by its own size and fades, while its backdrop fades, over 250ms at `small`, 300ms at `medium`, 400ms at `large` and 500ms at `full`, both ways; `start` and `end` mirror in right-to-left layouts, `left` and `right` do not. An inline drawer opens and closes the space it takes in the layout over the same durations while its panel fades: the `<aside>` sits in an element of its own (`wave-drawer__collapse`), which renders no box while the drawer is open and is a clipping grid while it moves, so the panel keeps its size against its inner edge and the page beside it moves with the space. A `full` inline drawer has no motion, and one whose width (height at the bottom) in `className` is a percentage resizes oddly during the motion, correctly once open (give it a length, or pass `motion={null}`). `motion` and `backdropMotion` replace them, or turn them off with `null`. A closing drawer waits for its motion before it unmounts or hides. A `className` with `data-[presence=…]` classes while `motion` is unset keeps 0.14's behaviour: no default motion on the panel, and your classes animate it (an inline one inside the clipping element). See [Enter and exit motion](#enter-and-exit-motion).
|
|
1639
1644
|
|
|
1640
1645
|
### Popovers and tooltips
|
|
1641
1646
|
|
|
@@ -1663,6 +1668,7 @@ export function Author() {
|
|
|
1663
1668
|
- **Context popovers.** `openOnContext` works as on Menu, with Popover's focus rules: a right click opens the popover at the pointer and leaves focus where it is (Tab from that element enters the content); Shift+F10 or the ContextMenu key opens it at the focused element and moves focus into the content, and Escape returns focus to that element. The region gets no `aria-haspopup` or `aria-expanded`, so name the content with `title`, `aria-label` or `aria-labelledby`.
|
|
1664
1669
|
- **`target`** on the Popover root (next to `side` and `align`; `PopupTarget`, as on `Menu.Popover`) places the content at an element held in state or at a `VirtualElement` instead of the trigger. A controlled popover with a `target` needs no trigger; a toggle button used as the target carries its own `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls` (give `Popover.Content` an `id`), a press on it does not count as outside, and Tab from it enters the content. `Popover`, `Menu.Popover` and TeachingPopover take the same `target`: an element held in state, a `VirtualElement` or a ref object (a ref is read after each render of the popup, and the popup stays hidden while it holds no element).
|
|
1665
1670
|
- **Tooltip timing.** `openDelay` (default 200ms) is how long the pointer rests on the child, or the child has keyboard focus, before the tooltip appears: unlike Menu's and Popover's `openDelay`, it applies to focus too. `closeDelay` (default 100ms) is how long it stays once the pointer has left the child and the tooltip; blur and Escape hide it at once. `delay` is the deprecated name of `openDelay` (it warns once in development).
|
|
1671
|
+
- **Motion.** `Popover.Content` (and InfoButton's note) fades in and slides 4px away from the trigger, on the side it opened on, over 200ms, and back over 100ms; the Tooltip surface fades over 100ms both ways, without a slide (Fluent's Tooltip has no motion). While they exit they stay mounted, `inert`, with `data-presence` and `data-state="closed"`; focus leaves a closing popover in the same commit, and a tooltip pointed at again during its exit shows again on the same element. `motion` on `Popover.Content`, InfoButton, InfoLabel and Tooltip (its portaled surface; `className` stays on its wrapper) replaces it, or turns it off with `null`. A tooltip whose `content` you change as it closes (a "Copied" text reset in `onOpenChange`) shows the new text while it fades: reset it once the exit is over, or pass `motion={null}`.
|
|
1666
1672
|
|
|
1667
1673
|
### Teaching popovers and tours
|
|
1668
1674
|
|
|
@@ -1703,6 +1709,7 @@ export function Tour({ onSeen }: { onSeen: () => void }) {
|
|
|
1703
1709
|
- **Parts.** `TeachingPopover.Trigger` opens and closes the popover (`aria-haspopup="dialog"`, `aria-expanded`, `aria-controls`). While `target` is unset the popover points at it and follows it in the Tab order. Focus returns to the Trigger when the popover closes, also when `target` is set. Written directly inside TeachingPopover (a Fragment is fine), it makes the popover start closed. `TeachingPopover.Surface` is the popover and takes the `className`, `style`, `ref` and other element props that the `steps` form takes on TeachingPopover. Inside it, `Header` (the Close button, an optional caption and `icon`; `dismissButton` replaces the Close glyph or merges a Button into the Close button, and `null` removes it), `Title` (`as`; it names the popover), `Body` (`media` above the text) and `Footer` each show their share of the current step unless you give them children; a Surface without children shows all four, which is what the `steps` form renders. The steps, labels, actions and callbacks stay on TeachingPopover.
|
|
1704
1710
|
- **Carousel parts.** `TeachingPopover.Carousel` holds the current page (the title, the body and a `CarouselFooter`). `CarouselFooter` lays out an indicator and the buttons: `layout="offset"` (the default, the indicator at the start, as in the Footer) or `"centered"` (the indicator between the buttons; Fluent's default). `finalStepText` relabels Done, and `initialStepText` turns the first step's unavailable Back into an enabled button that dismisses the popover (`onOpenChange(false)`, then `onDismiss`; Fluent's calls `onFinish`). It is the same element as Back, so focus stays on it when Back leads to the first step, where a second press dismisses. The indicator is the decorative step dots unless you put `CarouselNav` there (a group of step buttons named "Step 2 of 3", each a Tab stop, the current one with `aria-current="step"`; `labels.nav` and `labels.navButton` translate them) or `CarouselPageCount` (a visible "2 of 3", worded by its render function child, `(current, total) => …`).
|
|
1705
1711
|
- **Placement.** Write the Trigger and the Surface directly inside TeachingPopover. A Trigger nested deeper does not make the popover start closed, and an open popover renders in place until that Trigger has mounted (in the server HTML too), then moves next to it, with focus passing through the Trigger. A Surface inside a component of yours works once mounted, but the server HTML holds the popover's own surface as well until then; a second Surface renders a second popover (development warning `TeachingPopover.Surface:placement`).
|
|
1712
|
+
- **Motion.** The surface fades in over 200ms and out over 100ms, and, pointed at a `target` or the Trigger, slides 4px away from it and back; rendered in place, it only fades. It stays visible, with the step it showed, while it exits, and focus is back on the Trigger at once. `motion` on the TeachingPopover (for the surface it renders) or on `TeachingPopover.Surface` replaces it, or turns it off with `null`.
|
|
1706
1713
|
|
|
1707
1714
|
### Custom overlays
|
|
1708
1715
|
|
|
@@ -1782,7 +1789,67 @@ function Details() {
|
|
|
1782
1789
|
|
|
1783
1790
|
### Enter and exit motion
|
|
1784
1791
|
|
|
1785
|
-
|
|
1792
|
+
Dialog, Drawer, NavDrawer, Menu, Popover, InfoButton, Tooltip, TeachingPopover, Accordion, Tree, Nav, the toasts and MessageBarGroup animate in and out by default, with Fluent's motion tokens, and TabList, Carousel and ProgressBar take motion options. The listbox popups of Combobox, Dropdown, TagPicker and TimePicker, the DatePicker calendar and the AvatarGroup popover (and a popup of your own on `ListboxSurface`) do not animate yet ([ROADMAP §8.5](docs/ROADMAP.md#85-follow-ups-outside-the-gap-list)). The guide's "Component Motion" section lists the curves and every class.
|
|
1793
|
+
|
|
1794
|
+
| Part | Motion | In | Out |
|
|
1795
|
+
|---|---|---|---|
|
|
1796
|
+
| `Dialog.Content` | scales up from 85% and fades; its backdrop fades | 250ms | 200ms |
|
|
1797
|
+
| Drawer and NavDrawer, overlay | slides in from its edge by its own size and fades; its backdrop fades | 250, 300, 400 or 500ms by `size` | the same |
|
|
1798
|
+
| Drawer and NavDrawer, inline | the space it takes opens and closes while the panel fades (none at `full`) | by `size`, as above | the same |
|
|
1799
|
+
| `Popover.Content`, `Menu.Popover` and its submenus, InfoButton's note, the TeachingPopover surface | fades and slides 4px away from its anchor, on the side it opened on | 200ms | 100ms |
|
|
1800
|
+
| Tooltip | fades | 100ms | 100ms |
|
|
1801
|
+
| `Accordion.Panel`, a Tree group, a `Nav.Category`'s list | the space it takes opens and closes while it fades | 200ms | 200ms |
|
|
1802
|
+
| A toast (Toaster) | its place opens while it fades in and slides 8px from the stack's edge | 250ms | fades over 200ms, then its place closes over 200ms |
|
|
1803
|
+
| A bar of a MessageBarGroup | its row closes, or (`animate="both"`) opens, while it fades | 250ms | 250ms |
|
|
1804
|
+
| Nav item actions | fade as their row is hovered or takes focus | 150ms | 150ms |
|
|
1805
|
+
| `Nav.Category`'s chevron | its half turn | 150ms | 150ms |
|
|
1806
|
+
| TabList, `transparent` | the selection line slides and stretches to the new tab | 300ms | — |
|
|
1807
|
+
| Carousel | the track slides, or (`motion="fade"`) the slides cross-fade | `motionDuration`, 300ms | — |
|
|
1808
|
+
|
|
1809
|
+
- **Exits.** A closing surface stays mounted for its exit, `inert`, with `data-presence="exiting"` (a popup also with `data-state="closed"`), so it takes no focus, click or key while it fades and is out of the accessibility tree. Everything else follows the open state: focus is back on the trigger, and the next Escape reaches the layer below, as soon as it closes, and its dismiss layer, focus trap, `inert` isolation, scroll lock and positioning are released then, not when its motion ends. Focus inside a collapsing panel moves to its trigger. Because `inert` hit-tests like `pointer-events: none`, a second click during an exit reaches what is under the surface (the page behind a fading Dialog); the page's scroll bar returns as a modal starts to fade; and an exiting popup keeps its last position while the page scrolls.
|
|
1810
|
+
- **Turning it off, or replacing it.** Each animated part takes a motion slot (`MotionSlot`): `motion` on the part that moves, `backdropMotion` on the backdrops of Dialog, Drawer and NavDrawer, `expandIconMotion` on `Nav.Category` and `indeterminateMotion` on ProgressBar (Carousel's `motion` takes `'slide'`, `'fade'` or `null` instead). Unset is the default; `null` turns it off (a part with presence phases still renders them, and they end at once; Nav's expand icon, ProgressBar's fill and TabList's line just do not move); a class string or `{ className }` replaces the default classes with yours:
|
|
1811
|
+
|
|
1812
|
+
```tsx
|
|
1813
|
+
import { Button, Dialog, Popover } from '@mortenbrudvik/waveui';
|
|
1814
|
+
|
|
1815
|
+
export function Examples() {
|
|
1816
|
+
return (
|
|
1817
|
+
<>
|
|
1818
|
+
<Dialog>
|
|
1819
|
+
<Dialog.Trigger>
|
|
1820
|
+
<Button>Rename</Button>
|
|
1821
|
+
</Dialog.Trigger>
|
|
1822
|
+
<Dialog.Content title="Rename" motion={null} backdropMotion={null}>
|
|
1823
|
+
The dialog appears and disappears at once.
|
|
1824
|
+
</Dialog.Content>
|
|
1825
|
+
</Dialog>
|
|
1826
|
+
<Popover>
|
|
1827
|
+
<Popover.Trigger>
|
|
1828
|
+
<Button>Details</Button>
|
|
1829
|
+
</Popover.Trigger>
|
|
1830
|
+
<Popover.Content
|
|
1831
|
+
title="Details"
|
|
1832
|
+
motion="transition-opacity duration-wave-slow ease-wave-easy-ease motion-reduce:transition-none data-[presence=entering]:starting:opacity-0 data-[presence=exiting]:opacity-0"
|
|
1833
|
+
>
|
|
1834
|
+
A slower fade, without the slide.
|
|
1835
|
+
</Popover.Content>
|
|
1836
|
+
</Popover>
|
|
1837
|
+
</>
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
```
|
|
1841
|
+
|
|
1842
|
+
Write a replacement as the defaults are written: the enter's start with `starting:` gated on the entering phase (`data-[presence=entering]:starting:…`), the exit with `data-[presence=exiting]:…`, and a `motion-reduce:` counterpart. A positioned surface (Popover, Menu, Tooltip, InfoButton's note, a TeachingPopover with a target) is placed with an inline `transform`: move it with `translate` or `scale`, and never transition `transform` (`transition`, `transition-all`, `transition-transform`), or it glides from its last position. The surface's motion decides when it unmounts, so a backdrop motion longer than the surface's exit is cut off.
|
|
1843
|
+
- **Replacements and the precompiled CSS.** The default motion works on both style paths. A replacement's classes must exist in your CSS: your Tailwind build generates them, but the precompiled `./styles` holds only Wave's own classes, so there pass a class of your own CSS written against `[data-presence]` (as in the CSS example below).
|
|
1844
|
+
- **Turning it all off.** `<WaveProvider motion="none">` makes every motion slot below it act as `null` (TabList's line included) and stops Carousel's slide or fade (a Carousel without its own `motion`) and the Nav item actions' fade; a part's own value still wins, and transitions that are no slot keep running (the turn of Accordion's and Tree's chevrons, colour changes), and a nested provider inherits `motion` like `theme` (`motion="default"` turns the defaults back on below it). It is Fluent's `MotionBehaviourProvider` with `'skip'`. End-to-end tests that expect a closed surface to be gone at once can render under it. Classes of yours on a `Presence` child are no motion slot and keep running.
|
|
1845
|
+
- **Tuning.** The durations and curves are the `--wave-duration-*` and `--wave-curve-*` variables: redefine one in your CSS to retime every motion that uses it (`:root { --wave-duration-gentle: 200ms; }`), or replace one part's classes. There is no parameter form (Fluent's `surfaceMotion={{ duration }}`).
|
|
1846
|
+
- **Reduced motion.** Under `prefers-reduced-motion: reduce` every animated surface and part appears and disappears at once: the presence core ends each phase at once, and every default class has a `motion-reduce:` counterpart. The indeterminate ProgressBar pulses at full width instead of sweeping, and a Carousel with `autoPlay` starts stopped.
|
|
1847
|
+
- **0.14's classes in `className`.** A `className` with `data-[presence=…]` classes on `Menu.Popover`, `Dialog.Content`, Drawer or NavDrawer, while `motion` is unset, turns that part's default motion off, so the classes animate it as before (its backdrop still fades by default). Prefer `motion`.
|
|
1848
|
+
- **Collapsing parts.** Accordion's region, a Tree group, a Nav category's list and an inline drawer's panel sit inside an element of their own, which carries the phases and the motion (`wave-accordion-panel__collapse`, `wave-tree-item__collapse`, `wave-nav-category__collapse`, `wave-drawer__collapse`). It renders no box while the part is open (`display: contents`), so the part lays out as without it, and is a clipping grid of one track while the part opens or closes, so the space changes while the part keeps its size.
|
|
1849
|
+
- **Mounting.** A surface that is open when it mounts (`defaultOpen`) animates in, after hydration and on a client render alike. In-flow content (inline drawers, Accordion panels, Tree groups, Nav categories, the bars a MessageBarGroup mounts with) never animates on mount, on the server or the client.
|
|
1850
|
+
- **Browser support.** `starting:` needs `@starting-style` (Chrome and Edge 117+, Safari 17.5+, Firefox 129+); older browsers show an element at once, without its enter motion, and the exit still runs.
|
|
1851
|
+
|
|
1852
|
+
**Your own motion.** Motion tokens are CSS variables on `:root`, the same in every theme: `--wave-duration-ultra-fast` (50ms), `-faster` (100ms), `-fast` (150ms), `-normal` (200ms), `-gentle` (250ms), `-slow` (300ms), `-slower` (400ms), `-ultra-slow` (500ms), and the curves `--wave-curve-accelerate-max`, `-accelerate-mid`, `-accelerate-min`, `-decelerate-max`, `-decelerate-mid`, `-decelerate-min`, `-easy-ease-max`, `-easy-ease` and `-linear`, with the values of Fluent's motion tokens (the guide's "Motion & Animation" chapter lists them). On the Tailwind path they are the utilities `duration-wave-*` and `ease-wave-*`; the precompiled `./styles` contains only the utilities Wave itself uses, so there use the variables in your own CSS.
|
|
1786
1853
|
|
|
1787
1854
|
`Presence` (and its hook, `usePresence`) mounts and unmounts an element with CSS enter and exit motion. The element carries `data-presence`: `entering`, `entered`, `exiting` or `exited`. Style its enter with the `starting:` variant (CSS `@starting-style`) gated on the entering phase, `data-[presence=entering]:starting:`, and its exit with `data-[presence=exiting]:` classes. It stays mounted, and `inert`, until its own transitions and animations have finished, and it unmounts at once when it has none.
|
|
1788
1855
|
|
|
@@ -1831,8 +1898,6 @@ export function SavedNotice() {
|
|
|
1831
1898
|
- **Phases.** On the server and while hydrating the element renders its `entered` phase (a hidden one its `exited` phase), so no enter motion replays after hydration; `appear` runs it for an element that mounts visible on the client. Showing the element again while it exits returns it to `entering`, on the same element. `unmountOnExit={false}` keeps an exited element mounted, `hidden` and `inert`, so its state (a typed value, a scroll position) survives. `onEntered` and `onExited` report the end of a phase.
|
|
1832
1899
|
- **Gate the enter on the phase.** `@starting-style` applies to every first style of an element, whatever its phase. An ungated `starting:` class, or a `@starting-style` rule without `[data-presence='entering']`, therefore also fades in an element that mounts `entered`: without `appear`, and server-rendered content after hydration, while the core already reports `entered` and has called `onEntered`. The gated form runs only when the core runs the enter phase: a show after mount, `appear`, and a kept-mounted element shown again.
|
|
1833
1900
|
- **The hook.** `const { isMounted, ref, presenceProps } = usePresence(open)` for your own components: render the element while `isMounted`, pass it `ref` and spread `presenceProps` (`data-presence`, and `inert`/`hidden` when they apply). Destructure the result: `eslint-plugin-react-hooks` treats the whole object as a ref once its `ref` is passed on, and reports reads of its other members during render. Keep focus, dismissal and positioning on your open state, not on `isMounted`, so they happen on close and not after the exit motion.
|
|
1834
|
-
- **Browser support.** `starting:` needs `@starting-style` (Chrome and Edge 117+, Safari 17.5+, Firefox 129+); older browsers show the element at once, without its enter motion, and the exit still runs.
|
|
1835
|
-
- `Menu.Popover`, the toasts in a `Toaster`, and since 0.11 `Dialog.Content` and Drawer (the panel and its backdrop, overlay or inline, NavDrawer's too) mount through the presence core, so they take exit classes too (see [Menus](#menus) and [Toasts](#toasts)). An enter class for them takes the same gate; a surface that is open when it mounts (`defaultOpen`) mounts `entered`, so it does not animate that first time. A closing Dialog or Drawer waits for its motion before it unmounts, or hides with `unmountOnClose={false}` (`exited`, `hidden` and `inert`); focus, the trap and the isolation follow the open state at once. Every top-level element of their portal carries the presence attributes. No component ships enter or exit classes yet; Popover and Tooltip get their motion in a later release.
|
|
1836
1901
|
|
|
1837
1902
|
### Toasts
|
|
1838
1903
|
|
|
@@ -1892,7 +1957,7 @@ export function App({ undoSend }: { undoSend: () => void }) {
|
|
|
1892
1957
|
- Toasts stay reachable by Tab over an open Dialog and move out of an open overlay Drawer's way, modal or not: beside a side drawer at their corner, above a bottom one (below it for top toasts; `--wave-toaster-offset-block`), and they keep their corner when there is no room. Next to an inline drawer, which is page content, they keep their corner. `statusLabel` and `dismissLabel` (props of `<Toast>`; `statusLabel` is also an option of both `dispatchToast` forms, `dismissLabel` of the text form) translate the hidden status text and the Dismiss button's name.
|
|
1893
1958
|
- **`limit`** caps how many toasts show at once (`<Toaster limit={3}>`). Further toasts wait in a queue, in dispatch order, and appear as shown toasts go; a waiting toast is not announced and its timer does not run until it shows. A dismissed toast stops counting at once, also while an exit motion of yours runs. Lowering the limit never hides a shown toast. A value below 1 counts as 1 (with a development warning), and a fraction rounds down. Dispatching the id of a waiting toast again replaces it and keeps its place; `dismissToast(id)` removes a waiting toast silently.
|
|
1894
1959
|
- **`dismissAllToasts()`** removes every toast, shown and waiting, and cancels their timers. When focus was in a toast, it returns to the element that had it before.
|
|
1895
|
-
- **Motion.**
|
|
1960
|
+
- **Motion.** A toast opens its place in the stack, fades in and slides 8px in from the stack's edge (from below at the bottom positions, from above at the top) over 250ms. Dismissed, it fades out over 200ms, then its place closes over 200ms more, so the toasts around it glide together and nothing jumps when it unmounts. It stays mounted and `inert` until the exit ends; focus has already moved on, and it no longer counts toward `limit`. The motion is on each toast's container (`wave-toaster__toast`, a one-cell grid around `wave-toaster__toast-content`); `<Toaster motion={…}>` replaces it, or turns it off with `null`. The timer starts when a toast is shown, as before. See [Enter and exit motion](#enter-and-exit-motion).
|
|
1896
1961
|
|
|
1897
1962
|
**Toasts from code outside components.** A query cache, an event handler or a helper module cannot call a hook. Create a handle with `createToaster()` at module scope and bind a Toaster to it:
|
|
1898
1963
|
|
|
@@ -1946,6 +2011,35 @@ export function UpdateNotice({ onUpdate, onLater }: { onUpdate: () => void; onLa
|
|
|
1946
2011
|
- **Layouts.** `layout="singleline"` keeps the actions on the text's row, `"multiline"` puts them on a row of their own under the text, and `"auto"` (the default) keeps them on the text's row while the text fits on one line and moves them below when it does not (measured after mount with a ResizeObserver; the server renders one row). No layout cuts text off: on one row it wraps inside its column. The DOM order is the same in every layout (icon, text, actions, Dismiss), so a reflow never moves the focused button, and the Tab order is the text's links, the actions, the Dismiss button. A bar without actions looks as in 0.7. The root carries `data-layout` with the layout in use (`singleline` or `multiline`).
|
|
1947
2012
|
- **Names.** With its default role (`alert` for warnings and errors, `status` otherwise) the bar is a live region, and its title is read as the start of its content, once: "Warning: Update available A new version is ready to install.", then the actions' labels. A notice that stays on the page can take `role="region"`: then the title names the bar ("Warning: Update available"), which makes it a landmark; such a bar needs a title or an `aria-label` (a development warning says so). A consumer `aria-label` or `aria-labelledby` always wins.
|
|
1948
2013
|
|
|
2014
|
+
**Groups.** `MessageBarGroup` stacks bars with an 8px gap and animates the ones it gains and loses:
|
|
2015
|
+
|
|
2016
|
+
```tsx
|
|
2017
|
+
import { MessageBar, MessageBarGroup, type MessageBarProps } from '@mortenbrudvik/waveui';
|
|
2018
|
+
|
|
2019
|
+
interface Notice {
|
|
2020
|
+
id: string;
|
|
2021
|
+
status: MessageBarProps['status'];
|
|
2022
|
+
text: string;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
export function Notices({ notices, onDismiss }: { notices: Notice[]; onDismiss: (id: string) => void }) {
|
|
2026
|
+
return (
|
|
2027
|
+
<MessageBarGroup animate="both">
|
|
2028
|
+
{notices.map((notice) => (
|
|
2029
|
+
<MessageBar key={notice.id} status={notice.status} onDismiss={() => onDismiss(notice.id)}>
|
|
2030
|
+
{notice.text}
|
|
2031
|
+
</MessageBar>
|
|
2032
|
+
))}
|
|
2033
|
+
</MessageBarGroup>
|
|
2034
|
+
);
|
|
2035
|
+
}
|
|
2036
|
+
```
|
|
2037
|
+
|
|
2038
|
+
- **Motion.** A bar removed from the children stays in its place, `inert`, while it fades out and its row closes over 250ms, so the bars below glide up instead of jumping; a negative bottom margin makes up for the gap, so nothing moves when it unmounts. `animate="both"` also opens the row of a bar added after mount and fades it in (`'exit-only'`, the default, adds it at once). Bars present at the group's first render, on the server too, never animate. The root renders `data-animate`; each bar sits in an element of its own (`wave-message-bar-group__item`, no box while the bar is shown), and `motion` replaces the motion of both directions, or turns it off with `null`. The margin matches the group's own 8px gap (`gap-2`): with another gap in `className` (`gap-4`), pass a `motion` whose margin matches it (`data-[presence=exiting]:-mb-4`, and `data-[presence=entering]:starting:-mb-4` with `animate="both"`).
|
|
2039
|
+
- **Keys.** Give every bar a stable `key` (`key={notice.id}`): the group follows its children by key, so it keeps a removed bar in its place, moves a reordered one without remounting it, and shows again a bar that comes back during its exit. A child without a key of its own, or text, renders after the bars without motion, with a development warning (`MessageBarGroup:children`).
|
|
2040
|
+
- **Focus.** When a bar leaves with focus inside it (its Dismiss button was pressed), focus moves in the same commit to the first tabbable element of the nearest remaining bar after it that has one, else of the nearest before it; when no bar is left, move it yourself.
|
|
2041
|
+
- **Markup and bundles.** The group is a plain `<div>` without a role (`wave-message-bar-group`), and each MessageBar stays its own live region. It is a component of its own (Fluent's `MessageBarGroup`), not `MessageBar.Group`, so a page that imports only MessageBar loads none of its motion.
|
|
2042
|
+
|
|
1949
2043
|
### Badges
|
|
1950
2044
|
|
|
1951
2045
|
- `color` takes `brand` (the default), `danger`, `important`, `informative`, `severe`, `subtle`, `success` and `warning`, with `appearance` `filled` (the default), `tint` or `outline`. `severe` is dark orange; `subtle` is the page background with foreground text, for a badge on a colored surface. CounterBadge takes the same palette (`<CounterBadge count={3} color="danger" />`), and `dot` makes it a small dot without a number. A dot keeps 3:1 against the page: the `informative` and `warning` dots are darker than those counts, and `subtle` is for dots on colored surfaces.
|
|
@@ -2099,10 +2193,41 @@ const rows = React.useMemo(() => {
|
|
|
2099
2193
|
// sort={sort} onSortChange={setSort}> <DataGrid.Body>{rows.map(…)}</DataGrid.Body> </DataGrid>
|
|
2100
2194
|
```
|
|
2101
2195
|
|
|
2102
|
-
- **Row selection.** `selectionMode` (`single`, `multiple`) with `selectedItems`, `defaultSelectedItems` and `onSelectedItemsChange(selectedItems, details)`. With `selectionMode="single"`, pass at most one id (with several, the first one whose row is rendered
|
|
2196
|
+
- **Row selection.** `selectionMode` (`single`, `multiple`) with `selectedItems`, `defaultSelectedItems` and `onSelectedItemsChange(selectedItems, details)`. With `selectionMode="single"`, pass at most one id (with several, the first one is selected, or in a grid without `items` the first one whose row is rendered, with a development warning). Each row's selection control is named after the row's first `DataGrid.Cell`, `<td>` or `<th>` child (Fragments are looked into); a cell rendered by another component is not seen, so pass `selectionLabel` to `DataGrid.Row` then.
|
|
2103
2197
|
- **Selecting on a row click.** `selectOnRowClick` toggles a row when it is clicked (in single mode it selects the row alone, or clears it). The row's `onClick` runs first, and its `preventDefault()` keeps the selection. A click on a link, a button, an input or another interactive element in the row, a click from a popup opened from the row (a Menu in a cell), and a click that ends a text selection never toggle; Space on a cell toggles either way.
|
|
2104
2198
|
- **Looks.** `selectionAppearance` fills a selected row with the selected token (`brand`, the default), the subtle one (`neutral`) or not at all (`none`; the control, `aria-selected` and the forced-colors outline still show it). `subtleSelection` draws an unchecked row control only while its row is hovered or holds focus, and the select-all while the grid is (always in forced colors and on touch screens); the controls stay focusable and named.
|
|
2105
|
-
- **
|
|
2199
|
+
- **Selection follows the data** in a grid with `items` (0.16): select-all selects every item whose selection is not disabled, rendered or not, and its checkbox shows their state; when all of them are selected it removes only their ids. A row click, Space and a row's control change one id, and the ids of rows that are not rendered (filtered out, on another page, outside a `virtualWindow`, restored by id) stay selected and are reported by `onSelectedItemsChange`, until your code removes them. Select-all reports the ids outside `items` first, then the selected ids in `items` order. Both use the ids from `getRowId`: a row that sets its own, different `rowId` is outside them (a development warning says so). A grid of `DataGrid.Row` children keeps the 0.15 rule: ids of rows that are not rendered drop out at the next change.
|
|
2200
|
+
- **Per row.** `DataGrid.Row selectionCellProps` takes `disabled` (a natively disabled control: row clicks, Space and select-all skip the row, and select-all counts the enabled rows only), `invisible` (no visible control; the row still selects), `className` and `aria-label`. `DataGrid.SelectionCell` places the selection cell where you write it in a row (the select-all in a header row), instead of the automatic first cell. In a grid with `items`, `isSelectionDisabled(item, index)` disables items whether their rows are rendered or not (select-all skips them and keeps their state) and is the default `disabled` of each rendered row's selection cell; a cell's own `disabled` wins for its control, and a value that disagrees warns in development. Define it outside the component or memoise it: it is called for every item when `items`, `getRowId` or the function change.
|
|
2201
|
+
- **Resizable columns.** With `columns` and `items`, `resizableColumns` puts a resize handle (a focusable `role="separator"`) at the end edge of each column's header cell: drag it with a mouse, a finger or a pen (mirrored in right-to-left grids), or press F2 on the header cell (or its sort button) to move focus to it, then the arrow keys (20 pixels, 5 with Shift), Home (the minimum width), and Enter, Space or Escape to return; Enter on a sortable header still sorts. A double click sets a resized column back to its initial width. `columnSizingOptions` gives each column a `minWidth` (80 by default) and its starting width (`defaultWidth`, else `idealWidth`, else 150), and `columnWidths` or `defaultColumnWidths` with `onColumnWidthsChange(widths, details)` hold the widths (every column's, by `columnId`), for instance to persist them. Any of these props lays the columns out from widths (a `<colgroup>`, a fixed table layout, cells truncated with an ellipsis), with or without handles. An unresized grid fills its container (at least as wide as its columns); after the first change, or with `columnWidths` or `defaultColumnWidths`, it is exactly as wide as its columns, so the dragged edge follows the pointer and slack shows after the last column. There is no auto-fit and no maximum width. `labels.resizeColumn` and `labels.columnWidth(width)` name the handles and their value ("Resize column", "150 pixels").
|
|
2202
|
+
|
|
2203
|
+
```tsx
|
|
2204
|
+
import * as React from 'react';
|
|
2205
|
+
import { DataGrid } from '@mortenbrudvik/waveui';
|
|
2206
|
+
|
|
2207
|
+
// Person, columns and personId as in the first example.
|
|
2208
|
+
const SIZING = { name: { defaultWidth: 240, minWidth: 120 }, role: { idealWidth: 160 } };
|
|
2209
|
+
|
|
2210
|
+
export function ResizablePeople({ people }: { people: Person[] }) {
|
|
2211
|
+
const [widths, setWidths] = React.useState<Record<string, number>>(() =>
|
|
2212
|
+
JSON.parse(localStorage.getItem('people-widths') ?? '{}'),
|
|
2213
|
+
);
|
|
2214
|
+
return (
|
|
2215
|
+
<DataGrid
|
|
2216
|
+
aria-label="People"
|
|
2217
|
+
items={people}
|
|
2218
|
+
columns={columns}
|
|
2219
|
+
getRowId={personId}
|
|
2220
|
+
resizableColumns
|
|
2221
|
+
columnSizingOptions={SIZING}
|
|
2222
|
+
columnWidths={widths}
|
|
2223
|
+
onColumnWidthsChange={(next) => {
|
|
2224
|
+
setWidths(next);
|
|
2225
|
+
localStorage.setItem('people-widths', JSON.stringify(next));
|
|
2226
|
+
}}
|
|
2227
|
+
/>
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
```
|
|
2106
2231
|
- **Typing a wrapper.** A generic wrapper `function AppGrid<T>(props: DataGridProps<T>)` compiles and forwards every form, but an inline `onSortChange` passed to it next to `items` gets no contextual type: annotate its parameters, or type the wrapper's props as `DataGridBaseProps<T> & DataGridItemsSortProps<T>`.
|
|
2107
2232
|
|
|
2108
2233
|
### Static tables
|
|
@@ -2177,6 +2302,88 @@ export function Members({ members }: { members: Member[] }) {
|
|
|
2177
2302
|
- **Cell layout.** `Table.CellLayout` puts `media` (an icon, or an Avatar, which gets a 32px box) before the main text and a `description` under it; `truncate` keeps each line on one line where the column's width is bounded, and `appearance="primary"` marks the cell that names the row.
|
|
2178
2303
|
- **Cell actions.** `Table.CellActions` covers the end of its cell (the cell is its positioning box) and shows its buttons while the row is hovered or holds focus, always in forced colors and on touch screens; `visible` keeps them (while a menu opened from one is open). Its background follows the library's own row fills: none, the row hover, a `striped` table's stripe and a DataGrid's selected rows. A row background of your own, or a table on another surface (a Card), needs the same background class on the actions, and the actions must be direct children of the cell for the stripe and selection fills to match.
|
|
2179
2304
|
|
|
2305
|
+
**State from hooks.** `useTableFeatures` holds a table's rows and its plugins hold the sort, the selection and the column widths, by row id and over every item, so a table that renders only a page or a window of its rows keeps select-all and the sort over all of them (Fluent's call shape; the `Components/Table/Features` story is the full version):
|
|
2306
|
+
|
|
2307
|
+
```tsx
|
|
2308
|
+
import {
|
|
2309
|
+
Table,
|
|
2310
|
+
createTableColumn,
|
|
2311
|
+
useTableColumnSizing,
|
|
2312
|
+
useTableFeatures,
|
|
2313
|
+
useTableSelection,
|
|
2314
|
+
useTableSort,
|
|
2315
|
+
} from '@mortenbrudvik/waveui';
|
|
2316
|
+
|
|
2317
|
+
type Member = { id: string; name: string; role: string };
|
|
2318
|
+
|
|
2319
|
+
// Outside the component, as the grid's columns: the rows keep their array while these stay.
|
|
2320
|
+
const columns = [
|
|
2321
|
+
createTableColumn<Member>({ columnId: 'name', label: 'Name', compare: (a, b) => a.name.localeCompare(b.name) }),
|
|
2322
|
+
createTableColumn<Member>({ columnId: 'role', label: 'Role', compare: (a, b) => a.role.localeCompare(b.role) }),
|
|
2323
|
+
];
|
|
2324
|
+
const memberId = (member: Member) => member.id;
|
|
2325
|
+
const sizing = { name: { defaultWidth: 240, minWidth: 120 } };
|
|
2326
|
+
|
|
2327
|
+
export function Members({ members }: { members: Member[] }) {
|
|
2328
|
+
const { getRows, sort, selection, columnSizing } = useTableFeatures(
|
|
2329
|
+
{ items: members, columns, getRowId: memberId },
|
|
2330
|
+
[
|
|
2331
|
+
useTableSort({ defaultSort: { columnId: 'name', direction: 'ascending' } }),
|
|
2332
|
+
useTableSelection({ selectionMode: 'multiple' }),
|
|
2333
|
+
useTableColumnSizing({ columnSizingOptions: sizing }),
|
|
2334
|
+
],
|
|
2335
|
+
);
|
|
2336
|
+
return (
|
|
2337
|
+
<Table aria-label="Members" {...columnSizing.getTableProps()}>
|
|
2338
|
+
<colgroup>
|
|
2339
|
+
<col style={{ width: 40 }} />
|
|
2340
|
+
<col {...columnSizing.getColumnProps('name')} />
|
|
2341
|
+
<col {...columnSizing.getColumnProps('role')} />
|
|
2342
|
+
</colgroup>
|
|
2343
|
+
<Table.Header>
|
|
2344
|
+
<Table.Row>
|
|
2345
|
+
<Table.SelectionCell
|
|
2346
|
+
aria-label="Select all members"
|
|
2347
|
+
checked={selection.allRowsSelected ? true : selection.someRowsSelected ? 'mixed' : false}
|
|
2348
|
+
onCheckedChange={(_checked, details) => selection.toggleAllRows(details?.event)}
|
|
2349
|
+
/>
|
|
2350
|
+
{(['name', 'role'] as const).map((columnId) => (
|
|
2351
|
+
<Table.HeaderCell
|
|
2352
|
+
key={columnId}
|
|
2353
|
+
className="relative"
|
|
2354
|
+
sortable
|
|
2355
|
+
sortDirection={sort.getSortDirection(columnId)}
|
|
2356
|
+
onSortClick={(event) => sort.toggleColumnSort(columnId, event.nativeEvent)}
|
|
2357
|
+
aside={<Table.ResizeHandle {...columnSizing.getResizeHandleProps(columnId)} />}
|
|
2358
|
+
>
|
|
2359
|
+
{columnId === 'name' ? 'Name' : 'Role'}
|
|
2360
|
+
</Table.HeaderCell>
|
|
2361
|
+
))}
|
|
2362
|
+
</Table.Row>
|
|
2363
|
+
</Table.Header>
|
|
2364
|
+
<Table.Body>
|
|
2365
|
+
{getRows().map(({ item, rowId }) => (
|
|
2366
|
+
<Table.Row key={rowId}>
|
|
2367
|
+
<Table.SelectionCell
|
|
2368
|
+
aria-label={`Select ${item.name}`}
|
|
2369
|
+
checked={selection.isRowSelected(rowId)}
|
|
2370
|
+
onCheckedChange={(_checked, details) => selection.toggleRow(rowId, details?.event)}
|
|
2371
|
+
/>
|
|
2372
|
+
<Table.Cell>{item.name}</Table.Cell>
|
|
2373
|
+
<Table.Cell>{item.role}</Table.Cell>
|
|
2374
|
+
</Table.Row>
|
|
2375
|
+
))}
|
|
2376
|
+
</Table.Body>
|
|
2377
|
+
</Table>
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
```
|
|
2381
|
+
|
|
2382
|
+
- **Plugins.** Call each plugin hook unconditionally in the array (or before it) and pass the same plugins on every render; a slot without its plugin holds inert values, and the order of the array does not matter (a feature given twice uses its last plugin, with a development warning). Options may be written inline: a plugin whose options change identity only makes the state be computed again, while `getRows()` keeps its array until `items`, `getRowId`, `columns` or the sort change.
|
|
2383
|
+
- **Sort.** `useTableSort` takes DataGrid's sort shape (`sort`, `defaultSort`, `onSortChange(sort, details)`); `getRows()` is a stable sort of a copy of `items` by the sorted column's `compare` (descending negates it), and `sort` gives `value`, `setColumnSort`, `toggleColumnSort` and `getSortDirection`.
|
|
2384
|
+
- **Selection.** `useTableSelection` takes DataGrid's selection props (`selectionMode`, `selectedItems`, `defaultSelectedItems`, `onSelectedItemsChange(selectedItems, details)`, row ids) and `isDisabled(item, index)`. `allRowsSelected` and `someRowsSelected` count every selectable item of `items`, rendered or not; `toggleAllRows` selects them all, or removes only their ids (a selected disabled item and ids outside `items` keep their state), and `clearRows` empties the selection. For a selection without a table, `useSelection` holds the same id set (`toggle`, `select`, `deselect`, `toggleAll(universe)`, `clear`).
|
|
2385
|
+
- **Column widths.** `useTableColumnSizing` takes DataGrid's sizing props (`columnSizingOptions`, `columnWidths`, `defaultColumnWidths`, `onColumnWidthsChange(widths, details)`); spread `getTableProps()` on the table (a fixed layout and its width), `getColumnProps(columnId)` on each `<col>` and `getResizeHandleProps(columnId)` on a `Table.ResizeHandle`, which the `aside` of a positioned header cell (`relative`) holds. The handle is a Tab stop in a static table: Tab to it, then the arrow keys (20 pixels, 5 with Shift), Home and End (the minimum and, with `maxWidth`, the maximum); a double click sets a resized column back to its initial width. It keeps no width of its own: without the hook, pass `width`, `minWidth` and `onResize(width, details)` from your state. `label` ("Resize column") and `valueText(width)` ("N pixels") name it and its value.
|
|
2386
|
+
|
|
2180
2387
|
### Lists
|
|
2181
2388
|
|
|
2182
2389
|
A `List` without selection is a plain list. `navigationMode="items"` makes its items one Tab stop with the arrow keys, Home, End and typeahead, and `onAction` gives an item an action of its own:
|
|
@@ -2202,6 +2409,7 @@ export function RecentFiles({ open }: { open: (file: string) => void }) {
|
|
|
2202
2409
|
- **`onAction`** runs on a click and on Enter, and on Space where the item cannot be selected (a list without selection, an item without `value`); in a selectable item Space only toggles. It runs after the item's `onClick` and before the selection toggles: `event.preventDefault()` keeps the selection. Clicks and keys in the item's actions never run it. In a list that does not navigate its items it runs on clicks only, with a development warning.
|
|
2203
2410
|
- **`disabledSelection`** keeps an item's selection as it is (click, Enter and Space) while the item stays focusable and in the arrow order; without `onAction` it is also `aria-disabled`.
|
|
2204
2411
|
- **Check marks.** `checkmark` on a selectable List draws a decorative check at the start of each selected item; `List.Item checkmark` replaces it with your glyph or hides it (`false`).
|
|
2412
|
+
- **Long lists.** `virtualWindow` with `count` and a function child `(index) => <List.Item …/>` renders only the items in view (see [Large data](#large-data)); the keys then move by index over the whole list, and selection keeps the values of items that are not rendered.
|
|
2205
2413
|
|
|
2206
2414
|
### Trees
|
|
2207
2415
|
|
|
@@ -2248,6 +2456,38 @@ export function FileTree() {
|
|
|
2248
2456
|
- **Dialogs opened from an action.** The actions also stay shown while a Menu, Popover or Dialog rendered inside them is open or has focus, so its focus return reaches the action. A Dialog rendered elsewhere and opened from an action through state (a "Delete…" menu item that opens a confirm Dialog outside the tree) closes after the actions have hidden again, on devices with hover and outside forced colors: a hidden action cannot take focus, so focus returns to the nearest element that can, normally the item. To return focus to the action, render the Dialog inside `actions`, with its `Dialog.Trigger` there or as a controlled `Dialog` next to the Menu that opens it (`finalFocusRef` cannot name the action, which is still hidden when the dialog closes).
|
|
2249
2457
|
- **Navigation modes.** With `navigationMode="tree"` (the default), Tab from the focused item enters its actions (one stop; the arrow keys move inside them) and Shift+Tab returns. With `"treegrid"`, the "next" arrow (Right, Left in RTL) moves into the actions of an expanded parent or a leaf (a collapsed parent still expands first), "previous" returns, and Up and Down move between items from an action; the actions then take no Tab stop. This replaces APG's "next on an open item moves to its first child" (Down does that). The roles stay `tree` and `treeitem` in both modes, as in Fluent.
|
|
2250
2458
|
- **Persona rows.** `Tree.ItemPersonaLayout` (flat `TreeItemPersonaLayout`, Fluent's name) lays out an item's label as a person: `media` (an Avatar of size 32 fits a row), the name as children and a `description` under it; the item is named by the name and described by the description.
|
|
2459
|
+
- **Motion.** A child group opens and closes the space it takes while it fades, over 200ms both ways, in an element of its own around the `role="group"` element (`wave-tree-item__collapse`, `display: contents` while open); a nested group goes with its parent. The items of a closing group leave the arrow order and typeahead at once, and focus inside it moves to the item that collapsed. `Tree motion` replaces the motion of every group, or turns it off with `null`; the chevron's turn is not part of it. A pointer click that falls through an exiting group onto its item (an `inert` element lets the pointer through) does nothing; Enter and a screen reader's click on the item still activate it.
|
|
2460
|
+
|
|
2461
|
+
**Flat trees.** `FlatTree` builds a tree from flat data, for trees of thousands of items: `useFlatTree(items, options)` takes items with a `parentValue` (in any order) and the nested Tree's state props, and the tree renders the visible items (the roots and the children of every expanded branch) as sibling `role="treeitem"` elements with `aria-level`, `aria-setsize`, `aria-posinset` and, on branches, `aria-expanded`:
|
|
2462
|
+
|
|
2463
|
+
```tsx
|
|
2464
|
+
import * as React from 'react';
|
|
2465
|
+
import { FlatTree, useFlatTree, useVirtualWindow, type FlatTreeItemData } from '@mortenbrudvik/waveui';
|
|
2466
|
+
|
|
2467
|
+
export function Files({ items }: { items: readonly FlatTreeItemData[] }) {
|
|
2468
|
+
const [checked, setChecked] = React.useState<string[]>([]);
|
|
2469
|
+
const model = useFlatTree(items, {
|
|
2470
|
+
defaultExpandedItems: ['docs'],
|
|
2471
|
+
selectionMode: 'multiple',
|
|
2472
|
+
checkedItems: checked,
|
|
2473
|
+
onCheckedItemsChange: setChecked,
|
|
2474
|
+
});
|
|
2475
|
+
const virtualWindow = useVirtualWindow({ itemSize: 28 });
|
|
2476
|
+
return (
|
|
2477
|
+
<FlatTree
|
|
2478
|
+
aria-label="Files"
|
|
2479
|
+
model={model}
|
|
2480
|
+
{...model.getTreeProps()}
|
|
2481
|
+
virtualWindow={virtualWindow}
|
|
2482
|
+
style={{ height: 400 }}
|
|
2483
|
+
/>
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
// items: [{ value: 'docs', label: 'Documents' }, { value: 'cv', parentValue: 'docs', label: 'CV.pdf' }, …]
|
|
2487
|
+
```
|
|
2488
|
+
|
|
2489
|
+
- **The model.** `useFlatTree` returns `visibleItems` (each with `item`, `level`, `position`, `setSize`, `parentValue`, `hasChildren`, `isBranch`, `expanded`, `index` and, in multiple mode, `checked`), `getItem`, `expand` (a value or a list, in one change), `collapse`, `toggle`, `isExpanded`, `setChecked` and `getTreeProps()`. Expanded and checked state take the nested Tree's names (`expandedItems`, `defaultExpandedItems`, `onExpandedItemsChange`, `checkedItems`, `defaultCheckedItems`, `onCheckedItemsChange(checkedItems, details)`), and checks cascade the same way. `itemType: 'branch'` makes an item without children expandable (children loaded on demand). Pass `items`, and controlled arrays, as stable references: the model is rebuilt when they change. An unknown `parentValue` (the item becomes a root), a cycle of parents (cut) and a duplicate value (the first wins) warn in development.
|
|
2490
|
+
- **The tree.** `FlatTree` takes `model`, `selectionMode` (`model.getTreeProps()` passes the model's), `selected`, `current`, `onItemSelect(value)` and `renderItem(row)` (the item's content; its `label` by default), with the nested Tree's keys, chevrons, checkboxes and radios. An item whose data is `disabled` is skipped by the keys and typeahead and cannot be activated, expanded or checked by the user. When a branch collapses around the focused item (from code), focus moves to the branch. It has no row actions, `aside` or motion (items expand and collapse at once). Name it with `aria-label` or `aria-labelledby` (a development warning asks for one).
|
|
2251
2491
|
|
|
2252
2492
|
### Breadcrumbs
|
|
2253
2493
|
|
|
@@ -2308,7 +2548,8 @@ export function Sidebar() {
|
|
|
2308
2548
|
|
|
2309
2549
|
- **Section headers** (`Nav.SectionHeader`) are headings inside list items, `<h3>` by default (`headingLevel`); pick the level that fits the page's outline. **`Nav.Divider`** is a decorative line, hidden from assistive technology (the section header carries the structure).
|
|
2310
2550
|
- **App items.** `Nav.AppItem` is the app's name at the top, with an `icon`: a link with `href`, else a button, never selected. `Nav.AppItemStatic` is the same row without an action.
|
|
2311
|
-
- **Item actions.** `actions` on `Nav.Item` and `Nav.SubItem` (Fluent's SplitNavItem) renders your own controls beside the item's link, never inside it, in a group named by the item: a ToggleButton that pins the page, a MenuButton with more commands, each with its own name (a Tooltip with `relationship="label"` names an icon-only one). Each is a Tab stop after its item, and a click on one never selects the item. They are drawn while the row is hovered or has focus, while one is pressed or has its popup open, and always on touch screens and in forced colours; otherwise they are transparent and still focusable.
|
|
2551
|
+
- **Item actions.** `actions` on `Nav.Item` and `Nav.SubItem` (Fluent's SplitNavItem) renders your own controls beside the item's link, never inside it, in a group named by the item: a ToggleButton that pins the page, a MenuButton with more commands, each with its own name (a Tooltip with `relationship="label"` names an icon-only one). Each is a Tab stop after its item, and a click on one never selects the item. They are drawn while the row is hovered or has focus, while one is pressed or has its popup open, and always on touch screens and in forced colours; otherwise they are transparent and still focusable. They fade in and out over 150ms (at once under reduced motion and below `WaveProvider motion="none"`); the fade sets the transition of each action, so a transition class of yours on one is replaced.
|
|
2552
|
+
- **Categories.** A category's list opens and closes the space it takes while it fades, over 200ms both ways, in an element of its own around the `<ul>` (`wave-nav-category__collapse`, `display: contents` while open); focus inside a closing list moves to the category's button. `Nav.Category motion` replaces the collapse, or turns it off with `null`. `expandIcon` replaces the chevron (an optional indicator: `false` hides it; a glyph of yours is not turned, so a plus or a minus picked from `openCategories` works; a `<button>` or `Button` passed there is unwrapped, with a development warning), and `expandIconMotion` the transition of its half turn (150ms; `null` makes the turn snap).
|
|
2312
2553
|
|
|
2313
2554
|
### Navigation drawers and app shells
|
|
2314
2555
|
|
|
@@ -2377,15 +2618,17 @@ export function Shell({ narrow }: { narrow: boolean }) {
|
|
|
2377
2618
|
- **`focusMode`.** Every item is a Tab stop by default (`NavDrawer.Body focusMode="tab"`). `focusMode="arrow"` makes the body one Tab stop, at first the current page, with both arrow pairs (wrapping), Home and End; screen readers do not announce arrow keys in a navigation landmark, so prefer the default unless your users expect an app pane. Fluent's NavDrawer uses arrow keys by default (its `tabbable` adds Tab stops).
|
|
2378
2619
|
- **Width.** `size` defaults to `small` (320px), Drawer's scale; Fluent's navigation pane is 260px wide, `className="w-65"` on your Tailwind build (or a class of your own with `width: 260px`). `position` defaults to `start` (Drawer's to `end`), and `separator` to on.
|
|
2379
2620
|
- **Server rendering.** An inline NavDrawer renders in the server HTML (open, or hidden when kept mounted); with `focusMode="arrow"` its Tab stop is set once it has mounted.
|
|
2621
|
+
- **Motion.** As Drawer's: an overlay slides in from its edge and fades while its backdrop fades, and an inline drawer opens and closes its space (`wave-drawer__collapse` and `wave-nav-drawer__collapse` on the element around its panel); a NavDrawer without `size` takes the `small` duration, 250ms. `motion` and `backdropMotion` replace them, or turn them off with `null`.
|
|
2380
2622
|
|
|
2381
2623
|
### Cards, tabs and carousels
|
|
2382
2624
|
|
|
2383
2625
|
- **Card** `appearance` is `filled` (the default), `filled-alternative`, `outline` or `subtle`; `size` (`small`, `medium`, `large`) sets the padding of its parts through `--wave-card-padding`; `orientation="horizontal"` puts a `Card.Preview` beside the text. `Card.Preview` takes a `logo` over its bottom start corner. `Card.Header` takes an `image` (an Avatar, say) and an `action` (a menu button). With `selectionControl="checkbox"`, `checkbox` merges onto the built-in checkbox (an object), replaces it (an element) or removes it (`null`, when you render the control elsewhere); `floatingAction` sits over the top end corner and moves the checkbox to the start corner.
|
|
2384
|
-
- **TabList** `appearance` is `transparent` (the default), `subtle`, `subtle-circular` or `filled-circular`, and `size` `small`, `medium` or `large`. A `TabList.Tab` with an `icon` and no text is icon-only: give it an `aria-label` (development warning `TabList.Tab:icon-only-name`).
|
|
2626
|
+
- **TabList** `appearance` is `transparent` (the default), `subtle`, `subtle-circular` or `filled-circular`, and `size` `small`, `medium` or `large`. A `TabList.Tab` with an `icon` and no text is icon-only: give it an `aria-label` (development warning `TabList.Tab:icon-only-name`). In the `transparent` appearance the selection line slides and stretches from the tab selected before to the new one over 300ms; the line is the selected tab's `::after` (recolour it with `after:border-b-…`, `after:border-s-…` when vertical), and `motion` replaces its timing, written for the `after:` variant, or turns it off with `null`.
|
|
2385
2627
|
- **Carousel groups.** `groupSize={n}` shows `n` slides per page and `"auto"` as many as fit (the slides keep their width, which must be equal; `align` places a page that does not fill the width). `value`, `onValueChange` and `defaultValue` are the index of the first slide of the page; previous, next, the picker (one button per page), looping and autoplay move by page. `labels.slides(start, end, total)` names a page.
|
|
2386
2628
|
- **Dragging.** `draggable` follows the pointer (mouse, touch, pen); a drag past a quarter of the width, or a release faster than 0.5 px/ms, changes the page, else the track snaps back. The click that ends a drag is dropped, a vertical swipe still scrolls the page, and autoplay pauses while dragging.
|
|
2387
2629
|
- **Thumbnails.** `<Carousel.Item thumbnail="/a.png">` (or an object of `<img>` attributes) turns the picker into a row of thumbnails; each button keeps its slide name.
|
|
2388
2630
|
- **Controls.** `controlsLayout` is `overlay` (the default, round buttons over the slides), `overlay-wide` (full-height edge strips), `inline` (buttons beside the slides) or `inline-wide`. `Carousel.Button` (`direction="previous"` or `"next"`) replaces the built-in button of that direction and keeps its name, `aria-controls` and disabled behaviour; its children replace the chevron. `appearance="elevated"` rounds the carousel and adds a shadow.
|
|
2631
|
+
- **Carousel motion.** `motion` is `slide` (the default: the slides glide in from the side of the page order, mirrored in right-to-left layouts), `fade` (the slides cross-fade in place; one slide per page, else they slide, with a development warning; a drag still pages on release) or `null` (the page changes at once); the root renders it as `data-motion` (`slide`, `fade`, `none`). `motionDuration` is the time in milliseconds (300 by default; Fluent's `duration` is another unit, Embla's speed of 20 to 60). Below `WaveProvider motion="none"` a carousel moves only with its own `motion`, and under reduced motion it never moves.
|
|
2389
2632
|
|
|
2390
2633
|
### Slots
|
|
2391
2634
|
|
|
@@ -2447,7 +2690,8 @@ The names and hidden texts that components render themselves are English by defa
|
|
|
2447
2690
|
| `Rating` | `labels` (`star(value, max)`; it also names the half values of a `step={0.5}` Rating, "2.5 stars") |
|
|
2448
2691
|
| `RatingDisplay` | `labels` (`rating(value, max, formattedValue)`, `count(count, formattedCount)`); `locale` formats the value and the count (pass it when rendering on the server) |
|
|
2449
2692
|
| `SpinButton` | `labels` (`increment`, `decrement`) |
|
|
2450
|
-
| `DataGrid` | `labels` (`selectAll`, `selectionHeader`); each row's fallback name "Select row": `DataGrid.Row` `selectionLabel` |
|
|
2693
|
+
| `DataGrid` | `labels` (`selectAll`, `selectionHeader`, and with `resizableColumns` `resizeColumn` ("Resize column", the handles' name) and `columnWidth(width)` ("150 pixels", their value text)); each row's fallback name "Select row": `DataGrid.Row` `selectionLabel` |
|
|
2694
|
+
| `Table.ResizeHandle` | `label` ("Resize column"), `valueText(width)` ("150 pixels") |
|
|
2451
2695
|
| `AvatarGroup` | `overflowLabel`, `unnamedMemberLabel` |
|
|
2452
2696
|
| `InfoButton` | `aria-label` (the button and its note; default "Information") |
|
|
2453
2697
|
| `Hamburger` | `aria-label` ("Navigation", only while it has no other name) |
|
|
@@ -2457,11 +2701,95 @@ The names and hidden texts that components render themselves are English by defa
|
|
|
2457
2701
|
|
|
2458
2702
|
Without a text prop, these defaults are replaced through `aria-label`: the PresenceBadge status names ("Available", …: `aria-label` on the badge), the MessageBar "Dismiss" and SearchBox "Clear search" button names (`dismiss={{ 'aria-label': 'Lukk' }}`, which keeps the default icon), and the names of the Breadcrumb, Pagination, Nav, Stepper, Carousel and Toaster regions ("Breadcrumb", "Pagination", "Navigation", "Progress", "Carousel", "Notifications"). The placeholders of Dropdown, DatePicker, TimePicker and SearchBox are `placeholder` props, and ColorPicker's default presets are replaced through `presets`.
|
|
2459
2703
|
|
|
2460
|
-
0.7 and 0.8 add no other built-in text: the submenu half of a `Menu.SplitGroup`, a context menu (`Menu.Popover`) and a `Toolbar.RadioGroup` are named by the `aria-label` you pass, a toast's container by its severity and title, and a tree row's actions toolbar by its item's label. Nor do 0.10 and 0.11, beyond the rows above: a `TagGroup` is named by the `aria-label` you pass, an inline Drawer by its title; a `Listbox`, a `Calendar` and a `ColorSwatch` or `ImageSwatch` are named by the `aria-label` you pass (or a `Field`'s label, or a `Tooltip` around the swatch), an `OptionGroup` by its label, and the month and year buttons by their text in `locale`. 0.14 adds none either beyond the rows above: a `Table.SelectionCell` control is named by the `aria-label` or `aria-labelledby` you pass (a development warning asks for one), and a `CarouselFooter`'s `initialStepText` and `finalStepText` are your text.
|
|
2704
|
+
0.7 and 0.8 add no other built-in text: the submenu half of a `Menu.SplitGroup`, a context menu (`Menu.Popover`) and a `Toolbar.RadioGroup` are named by the `aria-label` you pass, a toast's container by its severity and title, and a tree row's actions toolbar by its item's label. Nor do 0.10 and 0.11, beyond the rows above: a `TagGroup` is named by the `aria-label` you pass, an inline Drawer by its title; a `Listbox`, a `Calendar` and a `ColorSwatch` or `ImageSwatch` are named by the `aria-label` you pass (or a `Field`'s label, or a `Tooltip` around the swatch), an `OptionGroup` by its label, and the month and year buttons by their text in `locale`. 0.14 adds none either beyond the rows above: a `Table.SelectionCell` control is named by the `aria-label` or `aria-labelledby` you pass (a development warning asks for one), and a `CarouselFooter`'s `initialStepText` and `finalStepText` are your text. 0.16 adds none beyond the resize handles' rows above: a `FlatTree` is named by the `aria-label` or `aria-labelledby` you pass (a development warning asks for one).
|
|
2705
|
+
|
|
2706
|
+
## Large data
|
|
2707
|
+
|
|
2708
|
+
DataGrid, List, Listbox, Combobox, Dropdown and FlatTree can render only the items in view of a long list, plus an overscan, with spacers (`aria-hidden`, `data-virtual-spacer`) as tall as the items they stand for. Create a window with `useVirtualWindow({ itemSize })` and pass it as `virtualWindow`; the component calls its `layout`, attaches its scroller and keeps the focused item rendered. Selection, select-all, sorting, the keyboard and the positions a screen reader announces follow the data, not the rendered items. No virtualization library is needed (an adapter of one can implement the same `VirtualWindow` interface: the guide's "Data at Scale" chapter has one for `@tanstack/react-virtual`).
|
|
2709
|
+
|
|
2710
|
+
```tsx
|
|
2711
|
+
import {
|
|
2712
|
+
Combobox,
|
|
2713
|
+
DataGrid,
|
|
2714
|
+
Field,
|
|
2715
|
+
List,
|
|
2716
|
+
createTableColumn,
|
|
2717
|
+
useVirtualWindow,
|
|
2718
|
+
type ListboxItem,
|
|
2719
|
+
} from '@mortenbrudvik/waveui';
|
|
2720
|
+
|
|
2721
|
+
interface Member {
|
|
2722
|
+
id: string;
|
|
2723
|
+
name: string;
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
// Outside the component: a new array would be computed again over every item at every row the
|
|
2727
|
+
// window scrolls past.
|
|
2728
|
+
const columns = [
|
|
2729
|
+
createTableColumn<Member>({
|
|
2730
|
+
columnId: 'name',
|
|
2731
|
+
label: 'Name',
|
|
2732
|
+
compare: (a, b) => a.name.localeCompare(b.name),
|
|
2733
|
+
renderCell: (member) => member.name,
|
|
2734
|
+
}),
|
|
2735
|
+
];
|
|
2736
|
+
const memberId = (member: Member) => member.id;
|
|
2737
|
+
|
|
2738
|
+
export function Members({ members }: { members: Member[] }) {
|
|
2739
|
+
// 44-pixel rows under a header of 45 pixels (with the select-all checkbox), which sticks over them.
|
|
2740
|
+
const virtualWindow = useVirtualWindow({ itemSize: 44, scrollPaddingStart: 45 });
|
|
2741
|
+
return (
|
|
2742
|
+
<DataGrid
|
|
2743
|
+
aria-label="Members"
|
|
2744
|
+
items={members}
|
|
2745
|
+
columns={columns}
|
|
2746
|
+
getRowId={memberId}
|
|
2747
|
+
selectionMode="multiple"
|
|
2748
|
+
virtualWindow={virtualWindow}
|
|
2749
|
+
containerProps={{ style: { height: 480 } }}
|
|
2750
|
+
/>
|
|
2751
|
+
);
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
export function Files({ files }: { files: readonly string[] }) {
|
|
2755
|
+
const virtualWindow = useVirtualWindow({ itemSize: 40 });
|
|
2756
|
+
return (
|
|
2757
|
+
<List
|
|
2758
|
+
aria-label="Files"
|
|
2759
|
+
selectable
|
|
2760
|
+
selectionMode="multiple"
|
|
2761
|
+
virtualWindow={virtualWindow}
|
|
2762
|
+
count={files.length}
|
|
2763
|
+
style={{ height: 320 }}
|
|
2764
|
+
>
|
|
2765
|
+
{(index) => <List.Item value={files[index]}>{files[index]}</List.Item>}
|
|
2766
|
+
</List>
|
|
2767
|
+
);
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
export function PersonPicker({ people }: { people: readonly ListboxItem[] }) {
|
|
2771
|
+
const virtualWindow = useVirtualWindow({ itemSize: 32 });
|
|
2772
|
+
return (
|
|
2773
|
+
<Field label="Person">
|
|
2774
|
+
<Combobox items={people} virtualWindow={virtualWindow} />
|
|
2775
|
+
</Field>
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2778
|
+
```
|
|
2779
|
+
|
|
2780
|
+
- **A fixed item height.** Every item is exactly the window's `itemSize` tall: the component sets that height on each row, item or option, after its own `style`, so the content must fit. A windowed DataGrid's body cells drop their block padding and keep their text on one line (truncated, as the columns are laid out from widths); a List item with one line of text fits in 40 pixels and the default `Option` in 32 (the stories use 44-pixel DataGrid rows and 28-pixel FlatTree items). Variable heights are not supported.
|
|
2781
|
+
- **A bounded height.** The scroller needs a height or a maximum height: the DataGrid's scroll wrapper (`containerProps`), the List, Listbox or FlatTree element. Without one the window measures the whole list and renders every item (DataGrid warns in development). The popup list of a Combobox or Dropdown scrolls within its own maximum height.
|
|
2782
|
+
- **Data, not elements.** The DataGrid needs `items` and `columns` (rows rendered from them, or by a `DataGrid.Body` render function), the pickers their data mode (`items`, `renderOption`), and the List `count` with a function child `(index) => <List.Item …/>`, called for the rendered indexes only; FlatTree renders its model. Keep `items`, `columns`, `getRowId`, `isSelectionDisabled` and `renderOption` stable (outside the component, or memoised): the component that calls `useVirtualWindow` renders again at every row boundary, and a new array or function is computed again over every item.
|
|
2783
|
+
- **The keyboard** moves by the data: a target that is not rendered is scrolled into the window and focused (or, in a picker, made active) in the commit that renders it, and the focused item stays rendered when it is scrolled away. The DataGrid carries `aria-rowcount` and `aria-rowindex`, List items and options `aria-setsize` and `aria-posinset` (a windowed List that is a grid, `aria-rowcount` and `aria-rowindex`), and FlatTree items `aria-level`, `aria-setsize` and `aria-posinset`.
|
|
2784
|
+
- **The scroller** turns the browser's scroll anchoring off, and is no Tab stop of its own while one of its items is the Tab stop; a plain windowed List (`navigationMode` `none`) keeps the browser's default, under which WebKit's keyboard cannot reach it: pass `tabIndex={0}` or `navigationMode="items"`.
|
|
2785
|
+
- **Overscan.** By default the window renders enough items for 240 pixels beyond each edge, and at least 6 (`overscan` sets a number of items). A scroll faster than the overscan in one frame (a fling of the scroll bar) shows the spacers until the next frame renders the new items; raise `overscan` if that matters.
|
|
2786
|
+
- **On the server** the window lays out the top of the list in `initialViewportSize` (480 pixels), so the server HTML holds the first window.
|
|
2787
|
+
- **Your own scroller.** `useVirtualWindow` works for markup of your own too: render `layout(count)`, items for the item entries and empty blocks of `size` pixels for the spacers, and attach `scrollRef` by merging it (`useMergedRefs`) or after destructuring it (`const { scrollRef } = virtualWindow`): `ref={virtualWindow.scrollRef}` fails the React Compiler's lint. `scrollToIndex(index, { align })` scrolls an item into view and moves the window in the same update.
|
|
2788
|
+
- **Limits.** Typeahead in a windowed List matches the rendered items only (in the pickers and FlatTree it searches the data); option groups cannot be windowed (`OptionGroup` children beside a `virtualWindow` are a development warning); TagPicker, TimePicker and the static `Table` have no `virtualWindow` (the table hooks keep a static table's state over every item); `<tfoot>` rows are not supported in a windowed DataGrid (they get no position, and the arrow keys skip them). The guide lists the rest.
|
|
2461
2789
|
|
|
2462
2790
|
## Keyboard support
|
|
2463
2791
|
|
|
2464
|
-
As implemented through 0.
|
|
2792
|
+
As implemented through 0.16. Buttons, links, checkboxes, switches and the trigger buttons of Accordion, Carousel, Nav, Breadcrumb and Pagination are native elements: Tab to reach them, Enter or Space to activate. A Link or `Button as="a"` without `href`, and a Button or Link rendered as a `div` or `span`, are buttons: Tab reaches them, Enter (key down) and Space (key up) activate them. A `disabledFocusable` control stays in the Tab order but Enter, Space and clicks do nothing. The sort buttons of a sortable `Table.HeaderCell` and the step buttons of `TeachingPopover.CarouselNav` are native buttons too.
|
|
2465
2793
|
|
|
2466
2794
|
| Component | Keys |
|
|
2467
2795
|
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -2472,12 +2800,14 @@ As implemented through 0.14. Buttons, links, checkboxes, switches and the trigge
|
|
|
2472
2800
|
| RadioGroup, SwatchPicker (row layout) | One Tab stop. All four arrows move and select (wrapping), Home/End jump; Left/Right mirrored in RTL. Keys pressed on a link inside a radio's label stay the link's. An `EmptySwatch` in the row layout is a Tab stop of its own, and the arrow keys pressed on it do nothing. |
|
|
2473
2801
|
| SwatchPicker (`layout="grid"`) | One Tab stop: the last focused swatch, at first the selected one, else the first. The arrows move focus only: Left/Right through the swatches in reading order across the rows (mirrored in RTL), stopping at the grid's first and last swatch; Up/Down to the enabled swatch of the previous/next row nearest the same column, stopping at the first and last row; Home/End to the first/last swatch of the row, Ctrl+Home/Ctrl+End of the grid. Space, Enter and a click select. Disabled swatches are skipped; an `EmptySwatch` is a cell like the others. |
|
|
2474
2802
|
| SwatchPicker (`focusMode="tab"`) | Every enabled swatch is a Tab stop, and the arrow keys do nothing; Space, Enter and a click select (the selected swatch stays selected). |
|
|
2475
|
-
| Listbox | One Tab stop: the list. The active option shows only while the list has focus (the selected option, else the first). Down/Up move without wrapping, Home/End, PageUp/PageDown by 10, typing moves to the matching option (a Space within 500 ms of a letter continues the search); Space and Enter select, or with `multiselect` toggle. Tab, Escape and Alt with an arrow are left to the page (an enclosing Dialog closes on Escape). With `disabledOptionsFocusable` the keys reach disabled options, but Space and Enter do nothing on them.
|
|
2803
|
+
| Listbox | One Tab stop: the list. The active option shows only while the list has focus (the selected option, else the first). Down/Up move without wrapping, Home/End, PageUp/PageDown by 10, typing moves to the matching option (a Space within 500 ms of a letter continues the search); Space and Enter select, or with `multiselect` toggle. Tab, Escape and Alt with an arrow are left to the page (an enclosing Dialog closes on Escape). With `disabledOptionsFocusable` the keys reach disabled options, but Space and Enter do nothing on them. Windowed (`virtualWindow`): the same keys over every option, rendered or not; the active option is scrolled into view with the window. |
|
|
2476
2804
|
| Rating | Right/Up one step more (a star, or half a star with `step={0.5}`), Left/Down one fewer (never below `step`), Home/End `step`/`max`; mirrored in RTL. A pointer click on a half-star Rating chooses the half or the whole star by position; Space and Enter on a focused radio choose that radio's own value. |
|
|
2477
2805
|
| TabList | One Tab stop (the selected tab). Left/Right (Up/Down when vertical) move and select, Home/End; with `selectTabOnFocus={false}` they only move focus, and Enter or Space selects the focused tab; disabled tabs skipped; mirrored in RTL. |
|
|
2478
|
-
| Tree | One Tab stop. Up/Down, Home/End; Right expands or moves to the first child, Left collapses or moves to the parent (mirrored in RTL); Enter activates; Space activates, or in a checkbox tree (`selectionMode="multiple"`) toggles the focused item's check; `*` expands siblings; type to jump (a Space within 500 ms of a letter continues the search). Row actions: Tab from the focused item enters them and Shift+Tab returns (`navigationMode="tree"`); with `navigationMode="treegrid"` the "next" arrow moves into them (on an expanded parent too, instead of to its first child), "previous" returns, and Up/Down move between items.
|
|
2479
|
-
|
|
|
2480
|
-
|
|
|
2806
|
+
| Tree | One Tab stop. Up/Down, Home/End; Right expands or moves to the first child, Left collapses or moves to the parent (mirrored in RTL); Enter activates; Space activates, or in a checkbox tree (`selectionMode="multiple"`) toggles the focused item's check; `*` expands siblings; type to jump (a Space within 500 ms of a letter continues the search). Row actions: Tab from the focused item enters them and Shift+Tab returns (`navigationMode="tree"`); with `navigationMode="treegrid"` the "next" arrow moves into them (on an expanded parent too, instead of to its first child), "previous" returns, and Up/Down move between items. |
|
|
2807
|
+
| FlatTree | One Tab stop: the focused item while focus is inside, else the `selected` item while it is visible, else the first enabled item. Up/Down between the visible items, Home/End; Right expands a collapsed branch or moves to its first child, Left collapses an expanded branch or moves to its parent (mirrored in RTL); Enter activates (and toggles a branch); Space activates, or in a checkbox tree toggles the check; `*` expands the sibling branches; typing moves to the next visible item whose label starts with the text. Disabled items are skipped. Windowed, an item that is not rendered is scrolled into view before it takes focus. |
|
|
2808
|
+
| List (selectable, or `navigationMode="items"`) | One Tab stop (selectable: the first selected item, else the first; without selection: the item that last had focus). Up/Down (wrapping), Home/End, typeahead above 7 items (a Space within 500 ms of a letter continues the search). Enter toggles a selectable item and runs `onAction`; Space toggles a selectable item, or runs `onAction` on an item that cannot be selected. With item actions in a selectable list, or `navigationMode="composite"` (grid): Up/Down between rows, Left/Right into the actions (mirrored in RTL), Enter/F2 into a text field, Escape back. A plain List (`navigationMode` `none`) has no arrow keys: its items' controls are Tab stops. Windowed (`virtualWindow` with `count`): the keys move by index over the whole list, Up/Down wrapping, Home/End, PageUp/PageDown by ten (stopping at the ends); typeahead searches the rendered items only; the focused item is the Tab stop while focus is in the list. |
|
|
2809
|
+
| DataGrid | One Tab stop. Arrows between cells (Left/Right to the previous/next cell of the row, mirrored in RTL; Up/Down and PageUp/PageDown keep the visual column across `colSpan`/`rowSpan` cells of grouped headers), Home/End row start/end, Ctrl+Home/Ctrl+End grid start/end, PageUp/PageDown 10 rows; Enter/F2 into a cell's widgets, Escape back; Space on a cell toggles its row's selection. A column header with a resize handle (`resizableColumns`): F2 on the header cell or its sort button moves focus to the handle (Enter on a sortable header still sorts); there Left/Right move the column's edge by 20 pixels (5 with Shift; mirrored in RTL), Home sets the minimum width, and Enter, Space or Escape return to the header; Tab moves between the header's sort button and handle. Windowed (`virtualWindow`): Up/Down, PageUp/PageDown and Ctrl+Home/Ctrl+End move over every row of the data; a row that is not rendered is scrolled in, then focused. |
|
|
2810
|
+
| Table.ResizeHandle | A Tab stop in a static table. Left/Right move the column's edge by 20 pixels (5 with Shift; mirrored in RTL), Home and End set the minimum and the maximum (`maxWidth`); with `onLeave`, Escape and the release of Enter or Space call it (to move focus back to the header; a held Enter or Space stays on the handle until it is released). |
|
|
2481
2811
|
| Menu | Trigger: Enter/Space/Down open and focus the first item, Up opens on the last; a trigger with `aria-disabled="true"` (a `disabledFocusable` MenuButton or SplitButton) ignores them and its click. Menu: Up/Down (wrapping), Home/End, typeahead, Enter/Space activate (a Space within 500 ms of a letter continues the search); on a checkbox, radio or switch item Space changes it and keeps the menu open, Enter changes it and closes a popup menu; on `Menu.ItemLink` Enter is the browser's link activation. A submenu's item: Right (Left in RTL), Enter and Space open the submenu and focus its first item; in a submenu Left (Right in RTL) and Escape close only it and return focus to its item. `Menu.SplitGroup`: Right (Left in RTL) moves from the action to the submenu button, Left back. Escape closes and returns focus, Tab closes every level. Context menu (`openOnContext`): Shift+F10 or the ContextMenu key on the region opens it at the focused element; Escape and item activation return focus there, and Tab moves on from it. |
|
|
2482
2812
|
| Combobox, TagPicker, TimePicker | Down/Up open and move, Alt+Down opens, typing makes the first match active (not in a `freeform` Combobox), Enter commits the active option (with no active option, Enter submits the form; TimePicker instead never submits while its text is edited: a complete time within the bounds is committed, erased text clears the value, other text is kept and flagged invalid, and the list closes; leaving the input with edited text settles it the same way and closes the list), Alt+Up and Tab close, Escape closes and then discards typed text. The chevron button (Combobox, TimePicker) is not a Tab stop; the clear button (`clearable`) is a Tab stop after the input. Tab from erased TimePicker text clears the value first, so focus moves past the clear button that goes with it. TagPicker: Backspace in the empty input, and the previous arrow (Left, Right in RTL) with the caret at the start of the text and nothing selected, focus the last tag; on a tag's remove button Left/Right move between the tags (mirrored in RTL) and past the last one return to the trigger, and Delete, Backspace, Enter or Space remove it and focus the next tag's remove button, else the input (a click focuses the input). Typing makes the option whose label equals the text active, else the first match; with `allowCreate` Enter on the create option adds the text. `TagPicker.Button`: Down, Up and a typed letter open and move, Enter and Space add the active option. The chevron is not a Tab stop; the clear button is, after the trigger. Multi-select (`multiselect`, Combobox): Enter toggles the active option and keeps the list open; Tab, Escape and Alt+Up close without committing; focusing the input selects the shown labels, so typing replaces them with a query, and Backspace and Delete only edit the text. With `disabledOptionsFocusable` (Combobox) the arrow keys reach disabled options, but Enter does nothing on them. |
|
|
2483
2813
|
| Dropdown | Down/Up/Home/End and typing open and move, PageUp/PageDown move by 10, Enter/Space commit (a Space within 500 ms of a letter continues the search), Alt+Up and Tab commit and close, Escape closes. The clear button (`clearable`) is a Tab stop after the combobox button. Multi-select (`multiselect`): Enter and Space toggle the active option and keep the list open; Tab, Escape and Alt+Up close without committing. With `disabledOptionsFocusable` the keys reach disabled options, but Enter and Space do nothing on them, and Tab or Alt+Up on one closes the list without selecting. |
|
|
@@ -2517,7 +2847,7 @@ Typeahead (Menu, a selectable or navigable List, Tree, Dropdown, Listbox) accept
|
|
|
2517
2847
|
| `useModalOpen()`, `hasOpenModal()`, `modalAllowProps` | Whether a modal is open; props that keep an app-owned region usable over a modal. |
|
|
2518
2848
|
| `useFocusableGroup(options)` | One tab stop that holds several focusable elements (`tabBehavior`: `unlimited`, `limited`, `limited-trap-focus`); Enter or F2 enters, Escape leaves. |
|
|
2519
2849
|
| `useFocusVisible()` | `true` while the last input was the keyboard; `false` on the server. |
|
|
2520
|
-
| `useGridNavigation(options)` | Keyboard navigation of a static `<table role
|
|
2850
|
+
| `useGridNavigation(options)` | Keyboard navigation of a static `<table role=\"grid\">` (the Table's own); `model` (`GridNavigationModel`: `rowCount`, `headerRowCount`, `scrollToRow(index)`) moves the vertical keys by the data of a grid whose rows are rendered in part (`data-row-index` on each rendered row), and returns `activeRowIndex` and `pendingRowIndex`, the rows to keep rendered. A widget with `data-grid-auxiliary` (a resize handle) is never a cell's target; F2 enters it. |
|
|
2521
2851
|
| `getTabbableElements(container)`, `getFirstTabbable`, `getLastTabbable` | The tabbable elements of a container in Tab order (hidden, disabled and `inert` ones left out). |
|
|
2522
2852
|
| `focusRing`, `focusRingInset`, `inputFocus`, `inputFocusWithin` | The focus class strings of the library, to compose with `cn()` on your own controls. |
|
|
2523
2853
|
| `useId(prefix?)` | SSR-safe ids (`prefix-<react id>`; never parse the React part). |
|
|
@@ -2527,12 +2857,16 @@ Typeahead (Menu, a selectable or navigable List, Tree, Dropdown, Listbox) accept
|
|
|
2527
2857
|
| `usePresence(visible, options?)`, `<Presence visible>` | Mount and unmount an element with CSS enter and exit motion (`data-presence` phases; see [Enter and exit motion](#enter-and-exit-motion)). |
|
|
2528
2858
|
| `useFieldControl(props, options?)` | Merge a custom control's labelling props with the surrounding `Field` (its label, validation message, hint and state). |
|
|
2529
2859
|
| `useAnnounce()`, `announce(message, politeness?)` | Screen-reader announcements through a shared live region. |
|
|
2530
|
-
| `useActiveDescendant(options)` | The `aria-activedescendant` model under the pickers, for composites that keep focus on one element: `items`, `getId`, `getElement` (the item's element for scrolling; by default the element with its id), `enabled`, `fallback`, `loop`, `activateFirstOnChange`, `onActiveValueChange`; returns `activeValue`, `activeDescendantId` and stable `setActiveValue`, `highlight` (pointer moves, not scrolled into view), `first`, `last`, `next`, `prev` and `move`. State-based: it reads no DOM during render, so an active fallback item's id is in the server HTML. |
|
|
2531
|
-
| `useListbox(options)`, `useListboxOption(props)` | The listbox under Dropdown, Combobox, TagPicker and Listbox: keys, typeahead, filtering, the active option and option registration, in `'select-only'`, `'editable'` or `'standalone'` mode, with `multiselect` and `disabledOptionsFocusable`; `onOpenChange(open, details)` and `onSelect(value, details)` (see [Listbox and custom pickers](#listbox-and-custom-pickers)). `useListboxOption` builds an option component of your own.
|
|
2860
|
+
| `useActiveDescendant(options)` | The `aria-activedescendant` model under the pickers, for composites that keep focus on one element: `items`, `getId`, `getElement` (the item's element for scrolling; by default the element with its id), `enabled`, `fallback`, `loop`, `activateFirstOnChange`, `onActiveValueChange`; returns `activeValue`, `activeDescendantId` and stable `setActiveValue`, `highlight` (pointer moves, not scrolled into view), `first`, `last`, `next`, `prev` and `move`. State-based: it reads no DOM during render, so an active fallback item's id is in the server HTML. `onScrollToItem(index)` is called first for every move that scrolls, for a windowed list that renders part of its items. |
|
|
2861
|
+
| `useListbox(options)`, `useListboxOption(props)` | The listbox under Dropdown, Combobox, TagPicker and Listbox: keys, typeahead, filtering, the active option and option registration, in `'select-only'`, `'editable'` or `'standalone'` mode, with `multiselect` and `disabledOptionsFocusable`; `onOpenChange(open, details)` and `onSelect(value, details)` (see [Listbox and custom pickers](#listbox-and-custom-pickers)). `useListboxOption` builds an option component of your own. In data mode (`items`) `onScrollToItem(index)` receives every active option that scrolls into view, by its index among the shown options. |
|
|
2862
|
+
| `useVirtualWindow(options)` | A window over a long list of fixed-height items (`itemSize`, `overscan`, `initialViewportSize`, `scrollPaddingStart`), for the `virtualWindow` prop of DataGrid, List, Listbox, Combobox, Dropdown and FlatTree: `VirtualWindow` (`itemSize`, `scrollRef`, `layout(count, { keep })`, `scrollToIndex(index, { align })`) and its `VirtualEntry` list (see [Large data](#large-data)). |
|
|
2863
|
+
| `useSelection(options)` | Selection by id held outside the DOM (`selectionMode`, `selectedItems`, `defaultSelectedItems`, `onSelectedItemsChange`): `isSelected`, `toggle`, `select`, `deselect`, `toggleAll(universe)` (keeps the ids outside the universe), `clear`. |
|
|
2864
|
+
| `useTableFeatures(options, plugins)`, `useTableSort`, `useTableSelection`, `useTableColumnSizing` | Headless state of a table built from the `Table` parts: the rows of `items` (`getRows()`), and the sort, selection and column widths of its plugins, over every item (see [Static tables](#static-tables)). |
|
|
2865
|
+
| `useFlatTree(items, options?)` | The model of a `FlatTree`: flat items with a `parentValue`, the visible rows with their level and position, the expanded and checked state (see [Trees](#trees)). |
|
|
2532
2866
|
| `useListboxPopup(options)`, `ListboxSurface` | The popup of a custom picker: dismissal (`onDismiss(reason)`, a `DismissReason`: `'escape'`, `'outside-press'` or `'focus-outside'`) and positioning, and the surface that renders the list (`listClassName`, `surfaceClassName`). |
|
|
2533
2867
|
| `ListboxProvider` | Provides a listbox's context to its options when you render the list yourself. |
|
|
2534
2868
|
| `collectOptionLabels(children)`, `markListboxElement(component, kind)` | Read option labels from children before the options register (server HTML, first render); mark your own option and group components so they are read. |
|
|
2535
|
-
| `useWaveTheme()` | Current theme, direction, portal container and `
|
|
2869
|
+
| `useWaveTheme()` | Current theme, direction, portal container, `inputDefaults` and `motion` (`'default'` or `'none'`). |
|
|
2536
2870
|
| `useToastController()` | `dispatchToast` (content or options) / `dismissToast` / `dismissAllToasts` inside `<Toaster>`. |
|
|
2537
2871
|
| `createToaster()` | A toast controller for code outside components; bind it with `<Toaster toaster={handle}>` (see [Toasts](#toasts)). Server-safe. |
|
|
2538
2872
|
| `useIsOverflowing(target)`, `useOverflowMenu()`, `useIsOverflowItemVisible(id)` | Overflow detection. |
|
|
@@ -2551,6 +2885,32 @@ Typeahead (Menu, a selectable or navigable List, Tree, Dropdown, Listbox) accept
|
|
|
2551
2885
|
| `resolveSlot`, `renderSlot` | The slot helpers the components use. |
|
|
2552
2886
|
| `getThemeClassName(theme)` | The theme classes of a theme. |
|
|
2553
2887
|
|
|
2888
|
+
## Upgrading from 0.15
|
|
2889
|
+
|
|
2890
|
+
0.16 removes nothing public, and everything it adds is opt-in (windowing, `FlatTree`, the table hooks, the pickers' data mode, resizable columns), except one behaviour change for a DataGrid given `items`:
|
|
2891
|
+
|
|
2892
|
+
1. **Selection follows the data.** Select-all selects every selectable item of `items`, rendered or not, and its checkbox reflects them; a row click, Space or a row's control no longer drops the selected ids whose rows are not rendered, and `onSelectedItemsChange` (same signature) reports them, so an id of a filtered-out item, of another page or restored by id stays selected until your code removes it. In single mode the selected id is the first one given. Select-all reports the ids outside `items` first, then the others in `items` order (0.15: the order the rows mounted in, the sorted order for a grid that mounts sorted). A grid of `DataGrid.Row` children is unchanged.
|
|
2893
|
+
2. **Disabled rows:** select-all can skip only the rows it knows. Pass `isSelectionDisabled(item, index)` when items can be disabled and not all rows are rendered (a `virtualWindow`): it also becomes each rendered row's selection-cell `disabled`.
|
|
2894
|
+
3. **New development warnings** for a `DataGrid.Row` whose own `rowId` differs from its item's id, or is no item's id, in a grid with `items` (`DataGrid.Row:row-id-mismatch`, `DataGrid.Row:row-id-outside-items`): pass a `getRowId` that returns the rows' ids. The `DataGrid:single-several-selected` text changes with `items`.
|
|
2895
|
+
4. **Types:** `UseGridNavigationResult` has two new members (`activeRowIndex`, `pendingRowIndex`), which a hand-built value adds; `ListboxOptionElementProps` gains optional `aria-posinset`, `aria-setsize` and `style` (spread them in an option of yours; merge `style` after your own); `DataGridLabels` gains `resizeColumn` and `columnWidth`.
|
|
2896
|
+
|
|
2897
|
+
A windowed list needs a bounded height and a fixed item height (see [Large data](#large-data)). The CHANGELOG's [0.16.0 section](CHANGELOG.md) lists the additions and the full upgrade notes.
|
|
2898
|
+
|
|
2899
|
+
## Upgrading from 0.14
|
|
2900
|
+
|
|
2901
|
+
0.15 removes nothing public and every type only widens, but the overlays and disclosures of item 1 (not yet the listbox popups of Combobox, Dropdown, TagPicker and TimePicker, the DatePicker calendar and the AvatarGroup popover) now animate by default; these changes can affect your end-to-end tests, styles and type mocks:
|
|
2902
|
+
|
|
2903
|
+
1. **Surfaces animate by default** in a browser: a closed Dialog, Drawer, NavDrawer, Menu, Popover, InfoButton note, Tooltip or TeachingPopover stays in the document, `inert`, for its exit (100 to 500ms). End-to-end tests that look for its absence right after a close wait for it, or render under `<WaveProvider motion="none">`; unit tests in jsdom are unaffected. A `defaultOpen` surface animates in on a client render too.
|
|
2904
|
+
2. **Popover.Content, the Tooltip surface and the TeachingPopover surface** mount through the presence core and render `data-presence` and `data-state`.
|
|
2905
|
+
3. **New wrapper elements** around Accordion's region, a Tree group, a Nav category's list and an inline drawer's panel, which are `display: contents` while open (so layout is unchanged then) and a clipping grid while they move; and a content element inside each toast container (`wave-toaster__toast-content`), around which the container is a one-cell grid in every phase. Child-combinator selectors across them, and tests that walk `parentElement` or `nextElementSibling`, change. An inline drawer with a percentage width resizes oddly during its motion: give it a length, or pass `motion={null}`.
|
|
2906
|
+
4. **0.14's `data-[presence=…]` classes in `className`** (`Menu.Popover`, `Dialog.Content`, Drawer, NavDrawer) keep working and turn that part's default motion off; its backdrop now fades by default (`backdropMotion={null}` keeps 0.14's). CSS of yours on `.wave-toaster__toast[data-presence=…]` now runs beside the toasts' default motion (a `transition` shorthand can reset the recipe's property list): pass `<Toaster motion={null}>` or move it to `motion`. The same goes for CSS on `[data-wave-infolabel-surface][data-presence=…]` (`motion={null}` on InfoButton or InfoLabel).
|
|
2907
|
+
5. **TabList's indicator** is the selected tab's `::after`: recolour it with `after:` border classes.
|
|
2908
|
+
6. **Focus in a closing Accordion panel, Tree group or Nav category** moves to its trigger (0.14: `<body>`).
|
|
2909
|
+
7. **Carousel's slide and Nav's chevron** use the `easy-ease` curve, and Nav item actions fade in over 150ms.
|
|
2910
|
+
8. **`WaveContextValue` has a new member, `motion`:** a hand-built value (a test mock of `useWaveTheme`) adds it.
|
|
2911
|
+
|
|
2912
|
+
The CHANGELOG's [0.15.0 section](CHANGELOG.md) lists the additions and the full upgrade notes.
|
|
2913
|
+
|
|
2554
2914
|
## Upgrading from 0.13
|
|
2555
2915
|
|
|
2556
2916
|
0.14 removes nothing public, and every new behaviour is opt-in; these changes can still affect your types, tests and styles:
|