@mortenbrudvik/waveui 0.7.0 → 0.8.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 +395 -132
- package/dist/components/button/Button.cjs +1 -1
- package/dist/components/button/Button.cjs.map +1 -1
- package/dist/components/button/Button.mjs +36 -35
- package/dist/components/button/Button.mjs.map +1 -1
- package/dist/components/button/Button.slots.cjs +1 -1
- package/dist/components/button/Button.slots.cjs.map +1 -1
- package/dist/components/button/Button.slots.mjs +31 -21
- package/dist/components/button/Button.slots.mjs.map +1 -1
- package/dist/components/button/CompoundButton.cjs +1 -1
- package/dist/components/button/CompoundButton.cjs.map +1 -1
- package/dist/components/button/CompoundButton.mjs +31 -29
- package/dist/components/button/CompoundButton.mjs.map +1 -1
- package/dist/components/button/Link.cjs +1 -1
- package/dist/components/button/Link.cjs.map +1 -1
- package/dist/components/button/Link.mjs +24 -23
- package/dist/components/button/Link.mjs.map +1 -1
- package/dist/components/button/MenuButton.cjs +1 -1
- package/dist/components/button/MenuButton.cjs.map +1 -1
- package/dist/components/button/MenuButton.mjs +29 -28
- package/dist/components/button/MenuButton.mjs.map +1 -1
- package/dist/components/button/SplitButton.cjs +1 -1
- package/dist/components/button/SplitButton.cjs.map +1 -1
- package/dist/components/button/SplitButton.mjs +46 -45
- package/dist/components/button/SplitButton.mjs.map +1 -1
- package/dist/components/button/ToggleButton.cjs +1 -1
- package/dist/components/button/ToggleButton.cjs.map +1 -1
- package/dist/components/button/ToggleButton.mjs +38 -35
- package/dist/components/button/ToggleButton.mjs.map +1 -1
- package/dist/components/button/Toolbar.cjs +1 -1
- package/dist/components/button/Toolbar.cjs.map +1 -1
- package/dist/components/button/Toolbar.mjs +43 -42
- package/dist/components/button/Toolbar.mjs.map +1 -1
- package/dist/components/button/Toolbar.parts.cjs +1 -1
- package/dist/components/button/Toolbar.parts.cjs.map +1 -1
- package/dist/components/button/Toolbar.parts.mjs +76 -81
- package/dist/components/button/Toolbar.parts.mjs.map +1 -1
- package/dist/components/data-display/Avatar.cjs +1 -1
- package/dist/components/data-display/Avatar.cjs.map +1 -1
- package/dist/components/data-display/Avatar.mjs +72 -67
- package/dist/components/data-display/Avatar.mjs.map +1 -1
- package/dist/components/data-display/AvatarGroup.cjs +1 -1
- package/dist/components/data-display/AvatarGroup.cjs.map +1 -1
- package/dist/components/data-display/AvatarGroup.mjs +89 -88
- package/dist/components/data-display/AvatarGroup.mjs.map +1 -1
- package/dist/components/data-display/Badge.cjs +1 -1
- package/dist/components/data-display/Badge.cjs.map +1 -1
- package/dist/components/data-display/Badge.mjs +17 -16
- package/dist/components/data-display/Badge.mjs.map +1 -1
- package/dist/components/data-display/CounterBadge.cjs +1 -1
- package/dist/components/data-display/CounterBadge.cjs.map +1 -1
- package/dist/components/data-display/CounterBadge.mjs +18 -17
- package/dist/components/data-display/CounterBadge.mjs.map +1 -1
- package/dist/components/data-display/Divider.cjs +1 -1
- package/dist/components/data-display/Divider.cjs.map +1 -1
- package/dist/components/data-display/Divider.mjs +31 -30
- package/dist/components/data-display/Divider.mjs.map +1 -1
- package/dist/components/data-display/Image.cjs +1 -1
- package/dist/components/data-display/Image.cjs.map +1 -1
- package/dist/components/data-display/Image.mjs +16 -15
- package/dist/components/data-display/Image.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 +74 -73
- 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 +175 -174
- package/dist/components/data-display/List.mjs.map +1 -1
- package/dist/components/data-display/Persona.cjs +1 -1
- package/dist/components/data-display/Persona.cjs.map +1 -1
- package/dist/components/data-display/Persona.mjs +31 -29
- package/dist/components/data-display/Persona.mjs.map +1 -1
- package/dist/components/data-display/PresenceBadge.cjs +1 -1
- package/dist/components/data-display/PresenceBadge.cjs.map +1 -1
- package/dist/components/data-display/PresenceBadge.mjs +25 -24
- package/dist/components/data-display/PresenceBadge.mjs.map +1 -1
- package/dist/components/data-display/Tag.cjs +1 -1
- package/dist/components/data-display/Tag.cjs.map +1 -1
- package/dist/components/data-display/Tag.mjs +87 -82
- package/dist/components/data-display/Tag.mjs.map +1 -1
- package/dist/components/feedback/MessageBar.cjs +1 -1
- package/dist/components/feedback/MessageBar.cjs.map +1 -1
- package/dist/components/feedback/MessageBar.mjs +129 -71
- package/dist/components/feedback/MessageBar.mjs.map +1 -1
- package/dist/components/feedback/MessageBar.parts.cjs +2 -0
- package/dist/components/feedback/MessageBar.parts.cjs.map +1 -0
- package/dist/components/feedback/MessageBar.parts.mjs +84 -0
- package/dist/components/feedback/MessageBar.parts.mjs.map +1 -0
- package/dist/components/feedback/MessageBar.reflow.cjs +2 -0
- package/dist/components/feedback/MessageBar.reflow.cjs.map +1 -0
- package/dist/components/feedback/MessageBar.reflow.mjs +74 -0
- package/dist/components/feedback/MessageBar.reflow.mjs.map +1 -0
- package/dist/components/feedback/MessageBar.status.cjs +1 -1
- package/dist/components/feedback/MessageBar.status.cjs.map +1 -1
- package/dist/components/feedback/MessageBar.status.mjs +2 -1
- package/dist/components/feedback/MessageBar.status.mjs.map +1 -1
- package/dist/components/feedback/ProgressBar.cjs +1 -1
- package/dist/components/feedback/ProgressBar.cjs.map +1 -1
- package/dist/components/feedback/ProgressBar.mjs +44 -43
- package/dist/components/feedback/ProgressBar.mjs.map +1 -1
- package/dist/components/feedback/Skeleton.cjs +1 -1
- package/dist/components/feedback/Skeleton.cjs.map +1 -1
- package/dist/components/feedback/Skeleton.mjs +26 -24
- package/dist/components/feedback/Skeleton.mjs.map +1 -1
- package/dist/components/feedback/Spinner.cjs +1 -1
- package/dist/components/feedback/Spinner.cjs.map +1 -1
- package/dist/components/feedback/Spinner.mjs +32 -31
- package/dist/components/feedback/Spinner.mjs.map +1 -1
- package/dist/components/feedback/Toast.cjs +1 -1
- package/dist/components/feedback/Toast.cjs.map +1 -1
- package/dist/components/feedback/Toast.context.cjs +2 -0
- package/dist/components/feedback/Toast.context.cjs.map +1 -0
- package/dist/components/feedback/Toast.context.mjs +23 -0
- package/dist/components/feedback/Toast.context.mjs.map +1 -0
- package/dist/components/feedback/Toast.mjs +10 -342
- package/dist/components/feedback/Toast.mjs.map +1 -1
- package/dist/components/feedback/Toast.parts.cjs +2 -0
- package/dist/components/feedback/Toast.parts.cjs.map +1 -0
- package/dist/components/feedback/Toast.parts.mjs +98 -0
- package/dist/components/feedback/Toast.parts.mjs.map +1 -0
- package/dist/components/feedback/Toast.root.cjs +2 -0
- package/dist/components/feedback/Toast.root.cjs.map +1 -0
- package/dist/components/feedback/Toast.root.mjs +109 -0
- package/dist/components/feedback/Toast.root.mjs.map +1 -0
- package/dist/components/feedback/Toaster.cjs +2 -0
- package/dist/components/feedback/Toaster.cjs.map +1 -0
- package/dist/components/feedback/Toaster.mjs +564 -0
- package/dist/components/feedback/Toaster.mjs.map +1 -0
- package/dist/components/input/AlphaSlider.cjs +2 -0
- package/dist/components/input/AlphaSlider.cjs.map +1 -0
- package/dist/components/input/AlphaSlider.mjs +115 -0
- package/dist/components/input/AlphaSlider.mjs.map +1 -0
- package/dist/components/input/Checkbox.cjs +1 -1
- package/dist/components/input/Checkbox.cjs.map +1 -1
- package/dist/components/input/Checkbox.mjs +47 -46
- package/dist/components/input/Checkbox.mjs.map +1 -1
- package/dist/components/input/ColorArea.cjs +2 -0
- package/dist/components/input/ColorArea.cjs.map +1 -0
- package/dist/components/input/ColorArea.mjs +204 -0
- package/dist/components/input/ColorArea.mjs.map +1 -0
- package/dist/components/input/ColorPicker.cjs +5 -5
- package/dist/components/input/ColorPicker.cjs.map +1 -1
- package/dist/components/input/ColorPicker.mjs +142 -165
- package/dist/components/input/ColorPicker.mjs.map +1 -1
- package/dist/components/input/ColorPicker.parts.cjs +2 -0
- package/dist/components/input/ColorPicker.parts.cjs.map +1 -0
- package/dist/components/input/ColorPicker.parts.mjs +192 -0
- package/dist/components/input/ColorPicker.parts.mjs.map +1 -0
- package/dist/components/input/ColorSlider.cjs +2 -0
- package/dist/components/input/ColorSlider.cjs.map +1 -0
- package/dist/components/input/ColorSlider.mjs +138 -0
- package/dist/components/input/ColorSlider.mjs.map +1 -0
- package/dist/components/input/Combobox.cjs +1 -1
- package/dist/components/input/Combobox.cjs.map +1 -1
- package/dist/components/input/Combobox.expand.cjs +1 -1
- package/dist/components/input/Combobox.expand.cjs.map +1 -1
- package/dist/components/input/Combobox.expand.mjs +9 -9
- package/dist/components/input/Combobox.expand.mjs.map +1 -1
- package/dist/components/input/Combobox.mjs +90 -85
- package/dist/components/input/Combobox.mjs.map +1 -1
- package/dist/components/input/DatePicker.cjs +1 -1
- package/dist/components/input/DatePicker.cjs.map +1 -1
- package/dist/components/input/DatePicker.mjs +186 -185
- package/dist/components/input/DatePicker.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 +71 -68
- package/dist/components/input/Dropdown.mjs.map +1 -1
- package/dist/components/input/Field.cjs +1 -1
- package/dist/components/input/Field.cjs.map +1 -1
- package/dist/components/input/Field.mjs +113 -112
- package/dist/components/input/Field.mjs.map +1 -1
- package/dist/components/input/Input.cjs +1 -1
- package/dist/components/input/Input.cjs.map +1 -1
- package/dist/components/input/Input.mjs +55 -54
- package/dist/components/input/Input.mjs.map +1 -1
- package/dist/components/input/Label.cjs +1 -1
- package/dist/components/input/Label.cjs.map +1 -1
- package/dist/components/input/Label.mjs +12 -11
- package/dist/components/input/Label.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.mjs +100 -98
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/input/RadioGroup.cjs +1 -1
- package/dist/components/input/RadioGroup.cjs.map +1 -1
- package/dist/components/input/RadioGroup.mjs +89 -88
- package/dist/components/input/RadioGroup.mjs.map +1 -1
- package/dist/components/input/Rating.cjs +1 -1
- package/dist/components/input/Rating.cjs.map +1 -1
- package/dist/components/input/Rating.mjs +110 -109
- package/dist/components/input/Rating.mjs.map +1 -1
- package/dist/components/input/SearchBox.cjs +1 -1
- package/dist/components/input/SearchBox.cjs.map +1 -1
- package/dist/components/input/SearchBox.mjs +118 -119
- package/dist/components/input/SearchBox.mjs.map +1 -1
- package/dist/components/input/Select.cjs +1 -1
- package/dist/components/input/Select.cjs.map +1 -1
- package/dist/components/input/Select.mjs +25 -24
- package/dist/components/input/Select.mjs.map +1 -1
- package/dist/components/input/Slider.cjs +1 -1
- package/dist/components/input/Slider.cjs.map +1 -1
- package/dist/components/input/Slider.mjs +47 -46
- package/dist/components/input/Slider.mjs.map +1 -1
- package/dist/components/input/SpinButton.cjs +1 -1
- package/dist/components/input/SpinButton.cjs.map +1 -1
- package/dist/components/input/SpinButton.mjs +76 -75
- package/dist/components/input/SpinButton.mjs.map +1 -1
- package/dist/components/input/SwatchPicker.cjs +1 -1
- package/dist/components/input/SwatchPicker.cjs.map +1 -1
- package/dist/components/input/SwatchPicker.mjs +38 -37
- package/dist/components/input/SwatchPicker.mjs.map +1 -1
- package/dist/components/input/Switch.cjs +1 -1
- package/dist/components/input/Switch.cjs.map +1 -1
- package/dist/components/input/Switch.mjs +65 -64
- package/dist/components/input/Switch.mjs.map +1 -1
- package/dist/components/input/TagPicker.cjs +1 -1
- package/dist/components/input/TagPicker.cjs.map +1 -1
- package/dist/components/input/TagPicker.mjs +161 -157
- package/dist/components/input/TagPicker.mjs.map +1 -1
- package/dist/components/input/Textarea.cjs +1 -1
- package/dist/components/input/Textarea.cjs.map +1 -1
- package/dist/components/input/Textarea.mjs +24 -23
- package/dist/components/input/Textarea.mjs.map +1 -1
- package/dist/components/input/TimePicker.cjs +1 -1
- package/dist/components/input/TimePicker.cjs.map +1 -1
- package/dist/components/input/TimePicker.mjs +202 -199
- package/dist/components/input/TimePicker.mjs.map +1 -1
- package/dist/components/input/colorName.cjs +2 -0
- package/dist/components/input/colorName.cjs.map +1 -0
- package/dist/components/input/colorName.mjs +30 -0
- package/dist/components/input/colorName.mjs.map +1 -0
- package/dist/components/input/colorUtils.cjs +1 -1
- package/dist/components/input/colorUtils.cjs.map +1 -1
- package/dist/components/input/colorUtils.mjs +154 -56
- package/dist/components/input/colorUtils.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 +130 -129
- package/dist/components/layout/Accordion.mjs.map +1 -1
- package/dist/components/layout/Card.cjs +1 -1
- package/dist/components/layout/Card.cjs.map +1 -1
- package/dist/components/layout/Card.mjs +78 -77
- package/dist/components/layout/Card.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 +111 -110
- package/dist/components/layout/Carousel.mjs.map +1 -1
- package/dist/components/layout/Flex.cjs +1 -1
- package/dist/components/layout/Flex.cjs.map +1 -1
- package/dist/components/layout/Flex.mjs +22 -21
- package/dist/components/layout/Flex.mjs.map +1 -1
- package/dist/components/layout/Grid.cjs +1 -1
- package/dist/components/layout/Grid.cjs.map +1 -1
- package/dist/components/layout/Grid.mjs +19 -18
- package/dist/components/layout/Grid.mjs.map +1 -1
- package/dist/components/layout/Overflow.cjs +1 -1
- package/dist/components/layout/Overflow.cjs.map +1 -1
- package/dist/components/layout/Overflow.mjs +92 -91
- package/dist/components/layout/Overflow.mjs.map +1 -1
- package/dist/components/layout/Stack.cjs +1 -1
- package/dist/components/layout/Stack.cjs.map +1 -1
- package/dist/components/layout/Stack.mjs +14 -13
- package/dist/components/layout/Stack.mjs.map +1 -1
- package/dist/components/layout/TabList.cjs +1 -1
- package/dist/components/layout/TabList.cjs.map +1 -1
- package/dist/components/layout/TabList.mjs +118 -117
- package/dist/components/layout/TabList.mjs.map +1 -1
- package/dist/components/layout/Tree.cjs +1 -1
- package/dist/components/layout/Tree.cjs.map +1 -1
- package/dist/components/layout/Tree.context.cjs +2 -0
- package/dist/components/layout/Tree.context.cjs.map +1 -0
- package/dist/components/layout/Tree.context.mjs +33 -0
- package/dist/components/layout/Tree.context.mjs.map +1 -0
- package/dist/components/layout/Tree.core.cjs +2 -0
- package/dist/components/layout/Tree.core.cjs.map +1 -0
- package/dist/components/layout/Tree.core.mjs +416 -0
- package/dist/components/layout/Tree.core.mjs.map +1 -0
- package/dist/components/layout/Tree.itemPersonaLayout.cjs +2 -0
- package/dist/components/layout/Tree.itemPersonaLayout.cjs.map +1 -0
- package/dist/components/layout/Tree.itemPersonaLayout.mjs +46 -0
- package/dist/components/layout/Tree.itemPersonaLayout.mjs.map +1 -0
- package/dist/components/layout/Tree.mjs +7 -215
- package/dist/components/layout/Tree.mjs.map +1 -1
- package/dist/components/layout/Tree.selection.cjs +2 -0
- package/dist/components/layout/Tree.selection.cjs.map +1 -0
- package/dist/components/layout/Tree.selection.mjs +73 -0
- package/dist/components/layout/Tree.selection.mjs.map +1 -0
- package/dist/components/navigation/Breadcrumb.cjs +1 -1
- package/dist/components/navigation/Breadcrumb.cjs.map +1 -1
- package/dist/components/navigation/Breadcrumb.mjs +76 -75
- package/dist/components/navigation/Breadcrumb.mjs.map +1 -1
- package/dist/components/navigation/Menu.group.cjs +1 -1
- package/dist/components/navigation/Menu.group.cjs.map +1 -1
- package/dist/components/navigation/Menu.group.mjs +39 -37
- package/dist/components/navigation/Menu.group.mjs.map +1 -1
- package/dist/components/navigation/Menu.items.cjs +1 -1
- package/dist/components/navigation/Menu.items.cjs.map +1 -1
- package/dist/components/navigation/Menu.items.mjs +67 -61
- package/dist/components/navigation/Menu.items.mjs.map +1 -1
- package/dist/components/navigation/Menu.link.cjs +1 -1
- package/dist/components/navigation/Menu.link.cjs.map +1 -1
- package/dist/components/navigation/Menu.link.mjs +34 -28
- package/dist/components/navigation/Menu.link.mjs.map +1 -1
- 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 +110 -109
- package/dist/components/navigation/Menu.popover.mjs.map +1 -1
- package/dist/components/navigation/Menu.root.cjs +1 -1
- package/dist/components/navigation/Menu.root.cjs.map +1 -1
- package/dist/components/navigation/Menu.root.mjs +171 -170
- package/dist/components/navigation/Menu.root.mjs.map +1 -1
- package/dist/components/navigation/Menu.selectable.cjs +1 -1
- package/dist/components/navigation/Menu.selectable.cjs.map +1 -1
- package/dist/components/navigation/Menu.selectable.mjs +85 -63
- package/dist/components/navigation/Menu.selectable.mjs.map +1 -1
- package/dist/components/navigation/Menu.splitGroup.cjs +1 -1
- package/dist/components/navigation/Menu.splitGroup.cjs.map +1 -1
- package/dist/components/navigation/Menu.splitGroup.mjs +27 -26
- package/dist/components/navigation/Menu.splitGroup.mjs.map +1 -1
- package/dist/components/navigation/Menu.trigger.cjs +1 -1
- package/dist/components/navigation/Menu.trigger.cjs.map +1 -1
- package/dist/components/navigation/Menu.trigger.mjs +6 -6
- package/dist/components/navigation/Nav.cjs +1 -1
- package/dist/components/navigation/Nav.cjs.map +1 -1
- package/dist/components/navigation/Nav.mjs +170 -155
- package/dist/components/navigation/Nav.mjs.map +1 -1
- package/dist/components/navigation/Pagination.cjs +1 -1
- package/dist/components/navigation/Pagination.cjs.map +1 -1
- package/dist/components/navigation/Pagination.mjs +73 -65
- package/dist/components/navigation/Pagination.mjs.map +1 -1
- package/dist/components/navigation/Stepper.cjs +1 -1
- package/dist/components/navigation/Stepper.cjs.map +1 -1
- package/dist/components/navigation/Stepper.mjs +110 -109
- package/dist/components/navigation/Stepper.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 +109 -108
- package/dist/components/overlays/Dialog.mjs.map +1 -1
- package/dist/components/overlays/Drawer.cjs +1 -1
- package/dist/components/overlays/Drawer.cjs.map +1 -1
- package/dist/components/overlays/Drawer.mjs +110 -109
- package/dist/components/overlays/Drawer.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 +229 -227
- package/dist/components/overlays/Popover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.mjs +41 -37
- package/dist/components/overlays/TeachingPopover.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 +87 -86
- package/dist/components/overlays/Tooltip.mjs.map +1 -1
- package/dist/components/portal/Portal.cjs +1 -1
- package/dist/components/portal/Portal.cjs.map +1 -1
- package/dist/components/portal/Portal.mjs +33 -32
- package/dist/components/portal/Portal.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 +31 -30
- 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.mjs +228 -227
- package/dist/components/table/DataGrid.mjs.map +1 -1
- package/dist/components/table/Table.cjs +1 -1
- package/dist/components/table/Table.cjs.map +1 -1
- package/dist/components/table/Table.mjs +60 -59
- package/dist/components/table/Table.mjs.map +1 -1
- package/dist/components/typography/Text.cjs +1 -1
- package/dist/components/typography/Text.cjs.map +1 -1
- package/dist/components/typography/Text.mjs +12 -11
- package/dist/components/typography/Text.mjs.map +1 -1
- package/dist/hooks/useMediaQuery.cjs +2 -0
- package/dist/hooks/useMediaQuery.cjs.map +1 -0
- package/dist/hooks/useMediaQuery.mjs +20 -0
- package/dist/hooks/useMediaQuery.mjs.map +1 -0
- package/dist/hooks/usePrefersReducedMotion.cjs +1 -1
- package/dist/hooks/usePrefersReducedMotion.cjs.map +1 -1
- package/dist/hooks/usePrefersReducedMotion.mjs +4 -18
- package/dist/hooks/usePrefersReducedMotion.mjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.cjs +1 -1
- package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.mjs +3 -3
- package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2089 -102
- package/dist/index.d.ts +2089 -102
- package/dist/index.mjs +99 -87
- package/dist/lib/classNames.cjs +2 -0
- package/dist/lib/classNames.cjs.map +1 -0
- package/dist/lib/classNames.mjs +415 -0
- package/dist/lib/classNames.mjs.map +1 -0
- package/dist/lib/color.cjs +2 -0
- package/dist/lib/color.cjs.map +1 -0
- package/dist/lib/color.mjs +86 -0
- package/dist/lib/color.mjs.map +1 -0
- package/dist/lib/globalRegistry.cjs.map +1 -1
- package/dist/lib/globalRegistry.mjs.map +1 -1
- package/dist/lib/toaster.cjs +2 -0
- package/dist/lib/toaster.cjs.map +1 -0
- package/dist/lib/toaster.mjs +77 -0
- package/dist/lib/toaster.mjs.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +1 -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: 68 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
|
-
- [Styles](#styles) · [Theming](#theming) · [Global effects](#global-effects)
|
|
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.6](#upgrading-from-06) · [Upgrading from 0.5](#upgrading-from-05) · [Upgrading from 0.4](#upgrading-from-04) · [Development](#development)
|
|
10
|
+
- [Keyboard support](#keyboard-support) · [Hooks and utilities](#hooks-and-utilities) · [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
|
|
|
@@ -124,6 +124,45 @@ export function Shell({ children }: { children: React.ReactNode }) {
|
|
|
124
124
|
|
|
125
125
|
**Theme classes.** The tokens are selected by `wave-light` (also `:root`), `wave-dark` and `wave-high-contrast`; `getThemeClassName(theme)` returns them. For dark and high contrast the provider also emits the deprecated 0.4 classes `dark` and `high-contrast`, so 0.4 `.dark` selectors in your CSS and class-based `dark:` variants (`@custom-variant dark (&:where(.dark, .dark *));`) keep working. Tailwind 4's default `dark:` variant follows `prefers-color-scheme`, not a class, and Wave does not define a class-based one. Wave's CSS still honours the two legacy classes, but they set only `--wave-*` variables. `color-scheme` is set on the three `wave-*` theme classes only, never on `:root`, so a page without a provider keeps its own scrollbar and form-control scheme.
|
|
126
126
|
|
|
127
|
+
**The page outside the provider.** The page's scrollbar and its canvas (the overscroll area and the space below short content) take their colours from `<html>`, which a provider never touches. To match them, put the theme classes on `<html>` as well, which sets `color-scheme` there; in a server-rendered layout that is `<html className={getThemeClassName(theme)}>` (safe in a Server Component). `color-scheme` gives the canvas the browser's default colour for the scheme; for the theme's own background, add `html { background-color: var(--wave-background); }` to your CSS.
|
|
128
|
+
|
|
129
|
+
**Following the operating system.** `theme` takes one of the named themes, so resolve the OS setting in your app and pass the result:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import * as React from 'react';
|
|
133
|
+
import { WaveProvider, getThemeClassName, type WaveTheme } from '@mortenbrudvik/waveui';
|
|
134
|
+
|
|
135
|
+
const DARK = '(prefers-color-scheme: dark)';
|
|
136
|
+
|
|
137
|
+
function subscribe(onChange: () => void) {
|
|
138
|
+
const query = window.matchMedia(DARK);
|
|
139
|
+
query.addEventListener('change', onChange);
|
|
140
|
+
return () => query.removeEventListener('change', onChange);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function useSystemTheme(): WaveTheme {
|
|
144
|
+
const dark = React.useSyncExternalStore(
|
|
145
|
+
subscribe,
|
|
146
|
+
() => window.matchMedia(DARK).matches,
|
|
147
|
+
() => false, // the server renders light
|
|
148
|
+
);
|
|
149
|
+
return dark ? 'dark' : 'light';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function Shell({ children }: { children: React.ReactNode }) {
|
|
153
|
+
const theme = useSystemTheme();
|
|
154
|
+
// The page outside the provider: its scrollbar and canvas.
|
|
155
|
+
React.useLayoutEffect(() => {
|
|
156
|
+
const classes = getThemeClassName(theme).split(' ');
|
|
157
|
+
document.documentElement.classList.add(...classes);
|
|
158
|
+
return () => document.documentElement.classList.remove(...classes);
|
|
159
|
+
}, [theme]);
|
|
160
|
+
return <WaveProvider theme={theme}>{children}</WaveProvider>;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A server cannot know the setting, so a server-rendered page paints light first and switches to dark after hydration on a dark system. `<meta name="color-scheme" content="light dark">` in the document head at least draws the scrollbar and canvas in the system's scheme from the start (the theme class on `<html>` takes over once it is set).
|
|
165
|
+
|
|
127
166
|
### Customizing tokens
|
|
128
167
|
|
|
129
168
|
Every color is a runtime CSS variable prefixed `--wave-`. The Tailwind color utilities use the same name without the prefix: `--wave-muted-foreground` is `text-muted-foreground`, `--wave-primary` is `bg-primary`, `border-primary`, `ring-primary`, and so on.
|
|
@@ -284,11 +323,46 @@ Workarounds when your app has its own design system:
|
|
|
284
323
|
|
|
285
324
|
Namespaced Wave utilities are being considered for 1.0.
|
|
286
325
|
|
|
326
|
+
## Styling hooks
|
|
327
|
+
|
|
328
|
+
Every component root and every named part carries a stable class that your own CSS can target: `wave-<component>` on the root and `wave-<component>__<part>` on a part.
|
|
329
|
+
|
|
330
|
+
```css
|
|
331
|
+
/* app.css */
|
|
332
|
+
.wave-button__icon {
|
|
333
|
+
color: var(--wave-primary);
|
|
334
|
+
}
|
|
335
|
+
.wave-tree-item__actions {
|
|
336
|
+
gap: 2px;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
The package exports the same names as typed constants, one per component (`as const`), so a selector built from them is checked by TypeScript:
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
import { buttonClassNames, treeItemClassNames } from '@mortenbrudvik/waveui';
|
|
344
|
+
|
|
345
|
+
// `.${buttonClassNames.icon}` is '.wave-button__icon'.
|
|
346
|
+
export const iconSelector = `.${buttonClassNames.icon}`;
|
|
347
|
+
|
|
348
|
+
export function checkedRows(tree: HTMLElement) {
|
|
349
|
+
return tree.querySelectorAll(`.${treeItemClassNames.root}[aria-checked='true']`);
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
- **Names.** The root class is `wave-` and the kebab-cased component name (`CardHeader` → `wave-card-header`, `Menu.ItemCheckbox` → `wave-menu-item-checkbox`); a part adds `__` and its name (`wave-tag__dismiss-button`). The constant is the camel-cased flat name plus `ClassNames` (`cardHeaderClassNames`). `WaveProvider` keeps `wave-root` and `Portal` `wave-portal`. Components that render no element of their own (Dialog, Popover, Presence, and the trigger and close parts, which clone your element) have none.
|
|
354
|
+
- **Where they sit.** The root class is on the element that receives your `className` (Input with `contentBefore` or `contentAfter`: the wrapper; `RadioGroup.Item`: its label). A slot's part class stays when a slot object swaps the element (`icon={{ as: 'img', … }}`).
|
|
355
|
+
- **Variants and nesting.** A component built on another carries both classes: ToggleButton is `wave-button wave-toggle-button` (its icon `wave-button__icon wave-toggle-button__icon`), so `.wave-button` styles every button; the menu item kinds carry `wave-menu-item`, and CounterBadge carries `wave-badge`. A public component rendered inside another also gets the parent's part class: SplitButton's halves are `wave-button wave-split-button__primary-action-button` and `wave-button wave-split-button__menu-button`.
|
|
356
|
+
- **No state classes.** State is in the `data-*` attributes and ARIA (`.wave-button[data-pressed]`, `.wave-tree-item[aria-checked='mixed']`); there is no `wave-button--pressed`.
|
|
357
|
+
- **Cascade.** Wave never styles through these classes and Tailwind generates nothing for them, so your rules are the only ones on them. With the precompiled `./styles` (unlayered), a one-class rule of yours ties with Wave's utility classes and wins when it comes later; on the Tailwind path Wave's utilities sit in Tailwind's `utilities` layer, and any unlayered rule of yours wins.
|
|
358
|
+
- **Stable.** The names are public API: no class is removed before 1.0. A renamed part, or a part that becomes a compound member of its own, keeps its old class next to the new one until 1.0; moving a class to another element is listed in the CHANGELOG as a DOM change. Per-part documentation comes with the part props of a later release (P13-01 in the [roadmap](docs/ROADMAP.md)).
|
|
359
|
+
- **Server Components.** The constants come from a server-safe module: a Server Component can import them from the entry.
|
|
360
|
+
|
|
287
361
|
## React Server Components
|
|
288
362
|
|
|
289
|
-
- Every component and hook module of the package starts with `"use client"`. The entry point, `cn`, the slot helpers and the types are server-safe, so a Server Component can import from `@mortenbrudvik/waveui
|
|
363
|
+
- 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` and the types are server-safe, so a Server Component can import from `@mortenbrudvik/waveui`, read the constants, create a toaster handle and render Wave components with serializable props.
|
|
290
364
|
- 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.
|
|
291
|
-
- 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 and Dropdown, DataGrid parts, Drawer and Menu triggers, the header of a `Menu.Group`, the halves of a `Menu.SplitGroup`, List, Nav, TabList and Tree items, and a `Button` passed as a MessageBar, SearchBox or Tag dismiss slot.
|
|
365
|
+
- 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 and Dropdown, DataGrid parts, Drawer and Menu triggers, the header of a `Menu.Group`, the halves of a `Menu.SplitGroup`, List, Nav, TabList and Tree items, the parts of Toast and MessageBar, the ColorPicker parts, `Tree.ItemPersonaLayout`, and a `Button` passed as a MessageBar, SearchBox or Tag dismiss slot.
|
|
292
366
|
- Event handlers and other functions cannot be passed from a Server Component; put interactive parts in a `'use client'` component.
|
|
293
367
|
- A `Popover`, `Dialog`, `Drawer`, 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.
|
|
294
368
|
|
|
@@ -358,33 +432,36 @@ export function SaveArea() {
|
|
|
358
432
|
}
|
|
359
433
|
```
|
|
360
434
|
|
|
361
|
-
| Compound
|
|
362
|
-
|
|
|
363
|
-
| `Accordion`
|
|
364
|
-
| `Breadcrumb`
|
|
365
|
-
| `Card`
|
|
366
|
-
| `Carousel`
|
|
367
|
-
| `
|
|
368
|
-
| `
|
|
369
|
-
| `
|
|
370
|
-
| `
|
|
371
|
-
| `
|
|
372
|
-
| `
|
|
373
|
-
| `
|
|
374
|
-
| `
|
|
375
|
-
| `
|
|
376
|
-
| `
|
|
377
|
-
| `
|
|
378
|
-
| `
|
|
379
|
-
| `
|
|
380
|
-
| `
|
|
381
|
-
| `
|
|
382
|
-
| `
|
|
383
|
-
| `
|
|
435
|
+
| Compound | Flat names |
|
|
436
|
+
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
437
|
+
| `Accordion` | `AccordionItem`, `AccordionTrigger`, `AccordionPanel` |
|
|
438
|
+
| `Breadcrumb` | `BreadcrumbItem` |
|
|
439
|
+
| `Card` | `CardHeader`, `CardBody`, `CardFooter` |
|
|
440
|
+
| `Carousel` | `CarouselItem` |
|
|
441
|
+
| `ColorPicker` | `ColorPickerHexField`, `ColorPickerSwatches`, `ColorPickerPreview` |
|
|
442
|
+
| `Combobox` | `ComboboxOption`, `ComboboxOptionGroup` (also `Option`, `OptionGroup`) |
|
|
443
|
+
| `DataGrid` | `DataGridHeader`, `DataGridHeaderCell`, `DataGridBody`, `DataGridRow`, `DataGridCell` |
|
|
444
|
+
| `Dialog` | `DialogTrigger`, `DialogContent`, `DialogTitle`, `DialogFooter`, `DialogClose` |
|
|
445
|
+
| `Drawer` | `DrawerTrigger`, `DrawerTitle`, `DrawerClose` |
|
|
446
|
+
| `Dropdown` | `DropdownOption`, `DropdownOptionGroup` |
|
|
447
|
+
| `List` | `ListItem` |
|
|
448
|
+
| `Menu` | `MenuTrigger`, `MenuPopover`, `MenuItem`, `MenuDivider`, `MenuItemCheckbox`, `MenuItemRadio`, `MenuItemSwitch`, `MenuItemLink`, `MenuGroup`, `MenuGroupHeader`, `MenuSplitGroup` |
|
|
449
|
+
| `MessageBar` | `MessageBarTitle`, `MessageBarActions` |
|
|
450
|
+
| `Nav` | `NavCategory`, `NavItem`, `NavSubItem` |
|
|
451
|
+
| `Overflow` | `OverflowItem` |
|
|
452
|
+
| `Popover` | `PopoverTrigger`, `PopoverContent` |
|
|
453
|
+
| `RadioGroup` | `RadioGroupItem` (also `RadioItem`) |
|
|
454
|
+
| `Skeleton` | `SkeletonGroup` |
|
|
455
|
+
| `Stepper` | `StepperStep` |
|
|
456
|
+
| `TabList` | `TabListTab`, `TabListPanel`, `TabListPanels` |
|
|
457
|
+
| `Table` | `TableHeader`, `TableHeaderCell`, `TableBody`, `TableRow`, `TableCell` |
|
|
458
|
+
| `Toast` | `ToastTitle`, `ToastBody`, `ToastFooter`, `ToastClose` |
|
|
459
|
+
| `Toolbar` | `ToolbarButton`, `ToolbarToggleButton`, `ToolbarRadioGroup`, `ToolbarRadioButton`, `ToolbarGroup`, `ToolbarDivider` |
|
|
460
|
+
| `Tree` | `TreeItem`, `TreeItemPersonaLayout` |
|
|
384
461
|
|
|
385
462
|
## Components
|
|
386
463
|
|
|
387
|
-
|
|
464
|
+
68 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.
|
|
388
465
|
|
|
389
466
|
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).
|
|
390
467
|
|
|
@@ -402,27 +479,30 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
402
479
|
|
|
403
480
|
### Inputs and forms
|
|
404
481
|
|
|
405
|
-
| Component | Description
|
|
406
|
-
| -------------- |
|
|
407
|
-
| `Field` | Label, hint, validation message (`validationState` error, warning, success or none; `error` is the shorthand for an error) and required indicator, wired to the control inside it (library controls read the Field context wherever they are in it); `orientation="horizontal"` puts the label beside the control.
|
|
408
|
-
| `Label` | Form label with a decorative required asterisk, `size` and `weight`.
|
|
409
|
-
| `Input` | Text input with `contentBefore`/`contentAfter` slots, `onValueChange(value)` next to the native `onChange`, and an `error` message.
|
|
410
|
-
| `Textarea` | Multi-line text input with an `error` message.
|
|
411
|
-
| `Select` | Styled native `<select>` with an `error` message.
|
|
412
|
-
| `Checkbox` | `checked`/`defaultChecked`/`onCheckedChange`, `indeterminate`, a rich `label` before or after the box (`labelPosition`), `disabledFocusable`, native form support.
|
|
413
|
-
| `Switch` | On/off toggle (`role="switch"`), `onCheckedChange`, a rich `label` before, after or above it (`labelPosition`), `disabledFocusable`, native form support.
|
|
414
|
-
| `RadioGroup` | Single choice with `RadioGroup.Item` items (a rich `label` each); `orientation`, `disabled`, `onValueChange`, native form support.
|
|
415
|
-
| `SearchBox` | Search input with a clear button; `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.
|
|
416
|
-
| `Slider` | Styled native range input, filled up to the thumb; `onValueChange(number)`.
|
|
417
|
-
| `SpinButton` | Numeric input with step buttons; typed text is a draft committed on blur or Enter; `min`, `max`, `step`, `largeStep`.
|
|
418
|
-
| `Combobox` | Editable combobox: typing filters the options; `freeform` makes the typed text the value; a chevron (`expandIcon`) and `clearable`. `Combobox.Option`, `Combobox.OptionGroup`.
|
|
419
|
-
| `Dropdown` | Select-only combobox (a button) with typeahead; `clearable`. `Dropdown.Option`, `Dropdown.OptionGroup`.
|
|
420
|
-
| `TagPicker` | Multi-select combobox that shows the selection as removable tags (a list named "Selected"); the input is described by a summary of the selection ("Selected: Apple, Banana"), and additions and removals are announced.
|
|
421
|
-
| `DatePicker` | Date input with a calendar dialog; locale-aware format and parse, `minDate`, `maxDate`, `disabledDates`, `onInvalidInput`.
|
|
422
|
-
| `TimePicker` | Time combobox (`12h`/`24h`, `step`, `minTime`, `maxTime`); value is `HH:mm`; `open`/`defaultOpen`/`onOpenChange` control the list; rejected text is kept and flagged (`onInvalidInput`).
|
|
423
|
-
| `ColorPicker` | Hex field, preset swatches and an optional opacity slider;
|
|
424
|
-
| `
|
|
425
|
-
| `
|
|
482
|
+
| Component | Description |
|
|
483
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
484
|
+
| `Field` | Label, hint, validation message (`validationState` error, warning, success or none; `error` is the shorthand for an error) and required indicator, wired to the control inside it (library controls read the Field context wherever they are in it); `orientation="horizontal"` puts the label beside the control. |
|
|
485
|
+
| `Label` | Form label with a decorative required asterisk, `size` and `weight`. |
|
|
486
|
+
| `Input` | Text input with `contentBefore`/`contentAfter` slots, `onValueChange(value)` next to the native `onChange`, and an `error` message. |
|
|
487
|
+
| `Textarea` | Multi-line text input with an `error` message. |
|
|
488
|
+
| `Select` | Styled native `<select>` with an `error` message. |
|
|
489
|
+
| `Checkbox` | `checked`/`defaultChecked`/`onCheckedChange`, `indeterminate`, a rich `label` before or after the box (`labelPosition`), `disabledFocusable`, native form support. |
|
|
490
|
+
| `Switch` | On/off toggle (`role="switch"`), `onCheckedChange`, a rich `label` before, after or above it (`labelPosition`), `disabledFocusable`, native form support. |
|
|
491
|
+
| `RadioGroup` | Single choice with `RadioGroup.Item` items (a rich `label` each); `orientation`, `disabled`, `onValueChange`, native form support. |
|
|
492
|
+
| `SearchBox` | Search input with a clear button; `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. |
|
|
493
|
+
| `Slider` | Styled native range input, filled up to the thumb; `onValueChange(number)`. |
|
|
494
|
+
| `SpinButton` | Numeric input with step buttons; typed text is a draft committed on blur or Enter; `min`, `max`, `step`, `largeStep`. |
|
|
495
|
+
| `Combobox` | Editable combobox: typing filters the options; `freeform` makes the typed text the value; a chevron (`expandIcon`) and `clearable`. `Combobox.Option`, `Combobox.OptionGroup`. |
|
|
496
|
+
| `Dropdown` | Select-only combobox (a button) with typeahead; `clearable`. `Dropdown.Option`, `Dropdown.OptionGroup`. |
|
|
497
|
+
| `TagPicker` | Multi-select combobox that shows the selection as removable tags (a list named "Selected"); the input is described by a summary of the selection ("Selected: Apple, Banana"), and additions and removals are announced. |
|
|
498
|
+
| `DatePicker` | Date input with a calendar dialog; locale-aware format and parse, `minDate`, `maxDate`, `disabledDates`, `onInvalidInput`. |
|
|
499
|
+
| `TimePicker` | Time combobox (`12h`/`24h`, `step`, `minTime`, `maxTime`); value is `HH:mm`; `open`/`defaultOpen`/`onOpenChange` control the list; rejected text is kept and flagged (`onInvalidInput`). |
|
|
500
|
+
| `ColorPicker` | Hex value (lowercase `#rrggbb`, or `#rrggbbaa` when not opaque) with a color area, a hue slider, a hex field, preset swatches and an optional opacity slider; with children, your own layout of `ColorArea`, `ColorSlider` and `AlphaSlider` (bound to the picker) and `ColorPicker.HexField`, `.Swatches`, `.Preview` (see [Color](#color)); `shape`. Picking a preset keeps the current opacity (a preset's alpha digits are ignored, with a development warning). |
|
|
501
|
+
| `ColorArea` | 2-D saturation and brightness field for an `HsvColor`, with two hidden range inputs (one Tab stop), pointer and touch dragging; mirrored in RTL. |
|
|
502
|
+
| `ColorSlider` | Hue (default), saturation or brightness slider (`channel`), horizontal or vertical. |
|
|
503
|
+
| `AlphaSlider` | Opacity slider over a checkerboard; `transparency` shows transparency. |
|
|
504
|
+
| `SwatchPicker` | Radio group of color swatches (`items` with a `label` each). |
|
|
505
|
+
| `Rating` | Star rating (`role="radiogroup"`); `RatingDisplay` is the read-only version (a fractional value draws a partly filled star; `showValue`, `count` and `compact` add the value and the number of ratings as text). |
|
|
426
506
|
|
|
427
507
|
### Data display
|
|
428
508
|
|
|
@@ -448,27 +528,27 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
448
528
|
|
|
449
529
|
### Layout
|
|
450
530
|
|
|
451
|
-
| Component | Description
|
|
452
|
-
| ----------- |
|
|
453
|
-
| `Card` | Surface with `Card.Header`, `Card.Body`, `Card.Footer`; optionally selectable (`onSelect`, `selected`, `selectionControl`).
|
|
454
|
-
| `Accordion` | Disclosure sections (`Accordion.Item`, `.Trigger`, `.Panel`); single (default) or `type="multiple"`; `headingLevel`; item values are unique (development warning).
|
|
455
|
-
| `TabList` | Tabs (`TabList.Tab`, `.Panel`, `.Panels`) with automatic activation, or manual with `selectTabOnFocus={false}`; `orientation`; tab values are unique (development warning).
|
|
456
|
-
| `Tree` | Hierarchical tree (`Tree.Item`) with expand/collapse
|
|
457
|
-
| `Carousel` | One slide at a time with previous/next, a slide picker and optional auto-rotation with a pause control. Slides are `Carousel.Item` children, directly or in Fragments (a component that renders `Carousel.Item` is not a slide).
|
|
458
|
-
| `Overflow` | Hides items that do not fit in one row and renders an overflow button (`useOverflowMenu` lists the hidden items); item ids are unique (development warning). The row has a 4px padding, so its items' focus rings are not clipped.
|
|
459
|
-
| `Grid` | CSS grid with a column count and token gaps.
|
|
460
|
-
| `Stack` | Vertical or horizontal stack (`orientation`, `gap`).
|
|
461
|
-
| `Flex` | Flexbox container (`direction`, `wrap`, `align`, `justify`, `gap`).
|
|
531
|
+
| Component | Description |
|
|
532
|
+
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
533
|
+
| `Card` | Surface with `Card.Header`, `Card.Body`, `Card.Footer`; optionally selectable (`onSelect`, `selected`, `selectionControl`). |
|
|
534
|
+
| `Accordion` | Disclosure sections (`Accordion.Item`, `.Trigger`, `.Panel`); single (default) or `type="multiple"`; `headingLevel`; item values are unique (development warning). |
|
|
535
|
+
| `TabList` | Tabs (`TabList.Tab`, `.Panel`, `.Panels`) with automatic activation, or manual with `selectTabOnFocus={false}`; `orientation`; tab values are unique (development warning). |
|
|
536
|
+
| `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. |
|
|
537
|
+
| `Carousel` | One slide at a time with previous/next, a slide picker and optional auto-rotation with a pause control. Slides are `Carousel.Item` children, directly or in Fragments (a component that renders `Carousel.Item` is not a slide). |
|
|
538
|
+
| `Overflow` | Hides items that do not fit in one row and renders an overflow button (`useOverflowMenu` lists the hidden items); item ids are unique (development warning). The row has a 4px padding, so its items' focus rings are not clipped. |
|
|
539
|
+
| `Grid` | CSS grid with a column count and token gaps. |
|
|
540
|
+
| `Stack` | Vertical or horizontal stack (`orientation`, `gap`). |
|
|
541
|
+
| `Flex` | Flexbox container (`direction`, `wrap`, `align`, `justify`, `gap`). |
|
|
462
542
|
|
|
463
543
|
### Feedback
|
|
464
544
|
|
|
465
|
-
| Component | Description
|
|
466
|
-
| ------------- |
|
|
467
|
-
| `MessageBar` | Inline status message (`info`, `success`, `warning`, `error`) with hidden status text and an optional dismiss button.
|
|
468
|
-
| `ProgressBar` | Determinate or indeterminate progress; name it with `label` (inside a `Field`, the Field's label names it); `color`.
|
|
469
|
-
| `Skeleton` | Loading placeholder (`width`, `height`, `shape`); `Skeleton.Group` marks the loading region busy.
|
|
470
|
-
| `Spinner` | Loading indicator announced as "Loading" (localize with `label`); `appearance="inverted"` for brand surfaces; `delay`.
|
|
471
|
-
| `Toast` | Notifications shown by `<Toaster>` (`limit` queues the rest) through `useToastController()
|
|
545
|
+
| Component | Description |
|
|
546
|
+
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
547
|
+
| `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)). |
|
|
548
|
+
| `ProgressBar` | Determinate or indeterminate progress; name it with `label` (inside a `Field`, the Field's label names it); `color`. |
|
|
549
|
+
| `Skeleton` | Loading placeholder (`width`, `height`, `shape`); `Skeleton.Group` marks the loading region busy. |
|
|
550
|
+
| `Spinner` | Loading indicator announced as "Loading" (localize with `label`); `appearance="inverted"` for brand surfaces; `delay`. |
|
|
551
|
+
| `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)). |
|
|
472
552
|
|
|
473
553
|
### Navigation
|
|
474
554
|
|
|
@@ -609,7 +689,7 @@ export function AccountSettings({ backedUp }: { backedUp: number }) {
|
|
|
609
689
|
}
|
|
610
690
|
```
|
|
611
691
|
|
|
612
|
-
- Library controls inside a `Field` are named by its label, described by its validation message and hint, and marked `aria-invalid`/`aria-required`. With `required`, native inputs (`Input`, `Select`, `Textarea`, `Slider`, `SpinButton`, `SearchBox`) also get the native `required` attribute, so the browser blocks an empty submit. The choice and picker controls (`Checkbox`, `Switch`, `RadioGroup`, `Rating`, `SwatchPicker`, `ColorPicker`, `Combobox`, `Dropdown`, `TagPicker`, `DatePicker`, `TimePicker`) are required too, also without `name`: the browser blocks the submit until the control is checked, switched on or has a value, so `<Field required><Switch /></Field>` means "must be on". Like a native read-only input, a `readOnly` Combobox, TagPicker, DatePicker or TimePicker does not block the submit (it keeps `aria-required`). Add `noValidate` to a `<form>` that validates in its submit handler.
|
|
692
|
+
- Library controls inside a `Field` are named by its label, described by its validation message and hint, and marked `aria-invalid`/`aria-required`. With `required`, native inputs (`Input`, `Select`, `Textarea`, `Slider`, `SpinButton`, `SearchBox`) also get the native `required` attribute, so the browser blocks an empty submit. The choice and picker controls (`Checkbox`, `Switch`, `RadioGroup`, `Rating`, `SwatchPicker`, `ColorPicker`, `ColorArea`, `ColorSlider`, `AlphaSlider`, `Combobox`, `Dropdown`, `TagPicker`, `DatePicker`, `TimePicker`) are required too, also without `name`: the browser blocks the submit until the control is checked, switched on or has a value, so `<Field required><Switch /></Field>` means "must be on". Like a native read-only input, a `readOnly` Combobox, TagPicker, DatePicker or TimePicker does not block the submit (it keeps `aria-required`). Add `noValidate` to a `<form>` that validates in its submit handler.
|
|
613
693
|
- **Validation states.** `validationMessage` renders below the control in the style of `validationState`: `error` (the default when a message is given) marks the control `aria-invalid` and announces the message (`role="alert"`); `warning` announces it without making the control invalid; `success` and `none` show it without announcing it. Each state but `none` shows an icon before the message; `validationMessageIcon` replaces it, and `null` removes it. `validationState="error"` without a message marks the control invalid and shows nothing. The Field root carries `data-validation-state`.
|
|
614
694
|
- **`error`** is the shorthand for an error message: `error="Enter an email address"`, or `error={true}` to mark the control invalid without a message. It wins over `validationMessage` and `validationState` (a development warning fires when both are set).
|
|
615
695
|
- **The hint** stays visible below the message, after it; the control is described by the message, then the hint.
|
|
@@ -619,7 +699,7 @@ export function AccountSettings({ backedUp }: { backedUp: number }) {
|
|
|
619
699
|
- A `label`, `hint`, `error` or `validationMessage` that renders nothing (`[]`, such as an empty `errors.map(…)`) counts as absent: no empty message renders, the hint shows, and without `validationState="error"` the control stays valid. A control's own `aria-invalid={false}` wins over the Field's error, which still describes it.
|
|
620
700
|
- One control per Field. Put wrapper or layout components (a `Tooltip`, your own row) **inside a plain `<div>`** in the Field; the library control inside is then labelled correctly.
|
|
621
701
|
- `error` on `Input`, `Select` and `Textarea`: a string renders the message after the control in a `role="alert"` element linked with `aria-describedby`/`aria-errormessage`; `error={true}` only marks the control invalid. Inside a Field that shows its own error, the message is not repeated.
|
|
622
|
-
- Value controls take part in native forms when you pass `name`, or when they are required (their own `required`, or a required `Field`): `Checkbox`, `Switch`, `RadioGroup`, `Rating`, `SpinButton`, `SwatchPicker`, `ColorPicker`, `Combobox`, `Dropdown`, `TagPicker`, `DatePicker` (ISO `yyyy-mm-dd`) and `TimePicker` (`HH:mm`) render a hidden input, honour `required` and `form`, and reset with their form. `SearchBox` submits through its own `<input name>` and resets with its form too. No name is generated for you.
|
|
702
|
+
- Value controls take part in native forms when you pass `name`, or when they are required (their own `required`, or a required `Field`): `Checkbox`, `Switch`, `RadioGroup`, `Rating`, `SpinButton`, `SwatchPicker`, `ColorPicker`, `ColorArea`, `ColorSlider` and `AlphaSlider` (the hex of the color; inside a ColorPicker they are bound and the picker submits), `Combobox`, `Dropdown`, `TagPicker`, `DatePicker` (ISO `yyyy-mm-dd`) and `TimePicker` (`HH:mm`) render a hidden input, honour `required` and `form`, and reset with their form. `SearchBox` submits through its own `<input name>` and resets with its form too. No name is generated for you.
|
|
623
703
|
|
|
624
704
|
### Value callbacks
|
|
625
705
|
|
|
@@ -631,8 +711,9 @@ export function AccountSettings({ backedUp }: { backedUp: number }) {
|
|
|
631
711
|
| checked | `checked` / `defaultChecked` / `onCheckedChange(checked)` |
|
|
632
712
|
| open | `open` / `defaultOpen` / `onOpenChange(open)` |
|
|
633
713
|
| checked values (Menu, Toolbar) | `checkedValues` / `defaultCheckedValues` / `onCheckedValuesChange(checkedValues, details)` |
|
|
714
|
+
| checked items (Tree) | `checkedItems` / `defaultCheckedItems` / `onCheckedItemsChange(checkedItems, details)` |
|
|
634
715
|
|
|
635
|
-
Extra data goes in a second, optional `details` argument: Dialog and Drawer call `onOpenChange(open, details)` with `details.reason` and `details.event` (see [Dialogs and triggers](#dialogs-and-triggers)),
|
|
716
|
+
Extra data goes in a second, optional `details` argument: Dialog and Drawer call `onOpenChange(open, details)` with `details.reason` and `details.event` (see [Dialogs and triggers](#dialogs-and-triggers)), Menu and Toolbar call `onCheckedValuesChange(checkedValues, details)` with `details.name`, `details.checkedItems` (the group's new values) and `details.event` (see [Menus](#menus)), and Tree calls `onCheckedItemsChange(checkedItems, details)` with `details.value`, `details.checked` and `details.event` (see [Trees](#trees)). These fire only when the value changes. Event callbacks such as `onPageChange`, `onStepChange` and `Tree`'s `onItemSelect` fire on every activation, also when the current item is activated again. Array props that Wave only reads (`openItems`, `expandedItems`, `checkedItems`, `selectedItems`, `columns`, `options`, `items`, `steps`, …) accept readonly arrays such as `as const` values; the callbacks receive a new, mutable array. The 0.4 names still work as deprecated aliases that warn once in development; see the [CHANGELOG](CHANGELOG.md) for the full table.
|
|
636
717
|
|
|
637
718
|
### Composite controls
|
|
638
719
|
|
|
@@ -640,6 +721,36 @@ Checkbox, Switch, SearchBox, SpinButton, Combobox, Dropdown, TagPicker, DatePick
|
|
|
640
721
|
|
|
641
722
|
`Input` with `contentBefore`/`contentAfter` content also renders a bordered wrapper `<span>`, which receives `className`, `style` and `hidden` (so they size and hide the visible field); unlike the controls above, `ref`, `id`, `aria-*` and `data-*` stay on the `<input>`.
|
|
642
723
|
|
|
724
|
+
`ColorSlider`, `AlphaSlider` and `ColorPicker.HexField` route the same props to their `<input>` and take `controlRef`. `ColorArea` is a group around two inputs: the group keeps `aria-label`, `aria-labelledby` and `aria-describedby` (a Field's label names it), with `className`, `style`, `data-*` and `ref`; both inputs get `aria-invalid` and `aria-errormessage`; `autoFocus` and `controlRef` reach the saturation input; `tabIndex` reaches the input that holds the Tab stop.
|
|
725
|
+
|
|
726
|
+
### Color
|
|
727
|
+
|
|
728
|
+
```tsx
|
|
729
|
+
import * as React from 'react';
|
|
730
|
+
import { AlphaSlider, ColorArea, ColorPicker, ColorSlider, Field } from '@mortenbrudvik/waveui';
|
|
731
|
+
|
|
732
|
+
export function AccentPicker() {
|
|
733
|
+
const [accent, setAccent] = React.useState('#0f6cbd');
|
|
734
|
+
return (
|
|
735
|
+
<Field label="Accent color">
|
|
736
|
+
<ColorPicker value={accent} onValueChange={setAccent} name="accent">
|
|
737
|
+
<ColorArea />
|
|
738
|
+
<ColorSlider />
|
|
739
|
+
<AlphaSlider />
|
|
740
|
+
<ColorPicker.HexField />
|
|
741
|
+
<ColorPicker.Swatches />
|
|
742
|
+
</ColorPicker>
|
|
743
|
+
</Field>
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
- **The picker and its parts.** Without children, a ColorPicker renders a color area and a hue slider, then its 0.7 layout: the preview and hex field, the presets and, with `showOpacity`, the opacity slider. With children it renders your layout: `ColorArea`, `ColorSlider` and `AlphaSlider` bind to the picker when you pass them neither `value` nor `defaultValue` (Fluent's composition), and `ColorPicker.HexField`, `ColorPicker.Swatches` (the picker's `presets`) and `ColorPicker.Preview` are the picker's own parts. Bound parts read and write the picker's color, take its `shape`, `labels` and disabled state, and submit nothing themselves: the picker's hidden input submits the hex value (`name`, `form`, `required`), and a form reset restores its `defaultValue`. A part with its own `value` or `defaultValue` stays independent.
|
|
749
|
+
- **Hex and HSV.** The picker's value is a hex string, as in 0.7 (`#rrggbb`, or `#rrggbbaa` below full opacity). The standalone parts take an `HsvColor` (`{ h, s, v, a? }`: `h` from 0 to 360, `s`, `v` and `a` from 0 to 1, no `a` meaning opaque; Fluent's type), and `hsvToHex(color)` and `hexToHsv(hex)` convert (`hexToHsv` returns `null` for text that is not a hex color). Inside the picker, the hue and saturation survive grays, white and black, which a hex value cannot carry. A value that is not a hex color disables the bound parts until a hex value arrives.
|
|
750
|
+
- **Keys and pointer.** `ColorArea` is one Tab stop: Left and Right change saturation (mirrored in RTL), Up and Down brightness, Shift with an arrow by 10, Page Up and Page Down brightness by 10, Home and End saturation to 0 and 100. `ColorSlider` and `AlphaSlider` take the arrows of their axis (by 1, Shift by 10), Page Up and Page Down (by 10), Home and End. A press on the area jumps there and drags, with a mouse, touch or a pen.
|
|
751
|
+
- **Assistive technology.** The area is a group of two sliders, "Saturation" and "Brightness": desktop screen readers list one 2-D slider, and touch screen readers can swipe each. Each value text starts with its own channel and names the color ("Saturation 72%, Brightness 74%, dark blue"); a hue slider says "210°, blue". `labels.colorName(color)` replaces the English color names (return `''` to leave them out), and `labels.valueText(color, channel)` a whole value text, for languages whose word order differs.
|
|
752
|
+
- `ColorSlider` takes `channel` (`'hue'`, `'saturation'` or `'value'`) and `orientation` (`'vertical'` draws the rail bottom to top); `AlphaSlider` shows opacity, or with `transparency` transparency (the value's `a` is still the opacity). The roots carry `data-orientation`, `data-channel` (ColorSlider) and, while disabled, `data-disabled`. `shape="square"` removes the corner radius. Inside a `Field`, a standalone slider is named by the Field's label, else by its `labels` ("Hue", "Saturation", "Brightness").
|
|
753
|
+
|
|
643
754
|
### Menus
|
|
644
755
|
|
|
645
756
|
```tsx
|
|
@@ -864,6 +975,7 @@ export function DeleteDialog({ onDelete }: { onDelete: () => void }) {
|
|
|
864
975
|
- **Why it opens or closes.** `onOpenChange(open, details)` gets `details.reason` (`'trigger'`, `'close'` for a `.Close` part, `'close-button'` for the built-in Close button, `'escape'`, `'outside-press'` for the backdrop) and `details.event`, the DOM event. A controlled dialog or drawer can refuse some of them (below). `details` is typed optional until 1.0, when it becomes required: read it as `details?.reason`. A render-prop trigger or close part that calls `onClick()` without its event reports a new `click` event with no target, so pass the event on.
|
|
865
976
|
- On close, focus goes to `finalFocusRef` when you pass one. Otherwise it returns to the element that had focus when the modal opened (a trigger's wrapper span resolves to the element inside it, see the trigger bullet above): the trigger for a trigger click, but also the parent's own button of a controlled Dialog, or a text field that had focus when a keyboard shortcut opened it. When nothing had focus (a modal opened from code, or a click in Safari, which does not focus the clicked button), it goes to the trigger that opened the modal, else the first mounted trigger. When that element is gone, focus falls back to a mounted trigger, then to an element next to where the opener was (the next row's action after a delete), then to the overlay below.
|
|
866
977
|
- An `autoFocus` element inside a Dialog or Drawer keeps focus, also in a dialog opened over another one or nested in its content, and in a `Popover.Content` opened from a dialog. `Dialog.Content` and `Drawer` take `closeLabel` (default `'Close'`) to localize the Close button's name.
|
|
978
|
+
- **Size.** `size` caps the dialog's width at 400px (`'small'`) or 600px (`'medium'`, the default). Below the cap it takes the full width, and it never exceeds the viewport: a 1rem margin keeps it off the viewport edges, its height is capped, and the body scrolls (sideways too, for content wider than the dialog). Because the margin and the caps are on the surface, a `className` on `Dialog.Content` replaces them: `max-w-[72rem]` for a wider dialog, `m-0 h-dvh max-h-none max-w-none rounded-none border-0` for a full-screen one, and the same classes behind a breakpoint (`max-[48rem]:m-0 max-[48rem]:h-dvh …`) for a dialog that goes full screen only on narrow screens. These are Tailwind classes for your own Tailwind build. The precompiled `./styles` contains only the utilities Wave uses: there, give `Dialog.Content` a class of your own that sets the same properties, in unlayered CSS loaded after Wave's, because Wave's classes are unlayered and as specific as yours ([Cascade](#styles)).
|
|
867
979
|
|
|
868
980
|
```tsx
|
|
869
981
|
import * as React from 'react';
|
|
@@ -977,41 +1089,119 @@ export function SavedNotice() {
|
|
|
977
1089
|
- **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.
|
|
978
1090
|
- **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.
|
|
979
1091
|
- **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.
|
|
980
|
-
- `Menu.Popover`
|
|
1092
|
+
- `Menu.Popover` and the toasts in a `Toaster` mount through the presence core, so menus and toasts take exit classes too (see [Menus](#menus) and [Toasts](#toasts)). An enter class for them takes the same gate; a menu that is open when it mounts (`defaultOpen`) mounts `entered`, so it does not animate that first time. The other overlays get their motion in a later release.
|
|
981
1093
|
|
|
982
1094
|
### Toasts
|
|
983
1095
|
|
|
984
|
-
Wrap the app (or the part that shows toasts) in `<Toaster>`; `useToastController()` throws in development outside it.
|
|
1096
|
+
Wrap the app (or the part that shows toasts) in `<Toaster>`; `useToastController()` throws in development outside it. Dispatch a `<Toast>` with its parts:
|
|
985
1097
|
|
|
986
1098
|
```tsx
|
|
987
|
-
import { Button, Toaster, useToastController } from '@mortenbrudvik/waveui';
|
|
1099
|
+
import { Button, Toast, Toaster, useToastController } from '@mortenbrudvik/waveui';
|
|
988
1100
|
|
|
989
|
-
function
|
|
1101
|
+
function SendButton({ onUndo }: { onUndo: () => void }) {
|
|
990
1102
|
const { dispatchToast } = useToastController();
|
|
991
1103
|
return (
|
|
992
1104
|
<Button
|
|
993
1105
|
onClick={() =>
|
|
994
|
-
dispatchToast(
|
|
1106
|
+
dispatchToast(
|
|
1107
|
+
<Toast status="success">
|
|
1108
|
+
<Toast.Title
|
|
1109
|
+
action={
|
|
1110
|
+
<Toast.Close>
|
|
1111
|
+
<Button size="small" appearance="transparent" onClick={onUndo}>
|
|
1112
|
+
Undo
|
|
1113
|
+
</Button>
|
|
1114
|
+
</Toast.Close>
|
|
1115
|
+
}
|
|
1116
|
+
>
|
|
1117
|
+
Email sent
|
|
1118
|
+
</Toast.Title>
|
|
1119
|
+
<Toast.Body subtitle="To 3 people">Your message was delivered.</Toast.Body>
|
|
1120
|
+
</Toast>,
|
|
1121
|
+
)
|
|
995
1122
|
}
|
|
996
1123
|
>
|
|
997
|
-
|
|
1124
|
+
Send
|
|
998
1125
|
</Button>
|
|
999
1126
|
);
|
|
1000
1127
|
}
|
|
1001
1128
|
|
|
1002
|
-
export function App() {
|
|
1129
|
+
export function App({ undoSend }: { undoSend: () => void }) {
|
|
1003
1130
|
return (
|
|
1004
|
-
<Toaster
|
|
1005
|
-
|
|
1131
|
+
<Toaster
|
|
1132
|
+
position="bottom-end"
|
|
1133
|
+
shortcuts={{ focus: (event) => event.altKey && event.shiftKey && event.code === 'KeyN' }}
|
|
1134
|
+
>
|
|
1135
|
+
<SendButton onUndo={undoSend} />
|
|
1006
1136
|
</Toaster>
|
|
1007
1137
|
);
|
|
1008
1138
|
}
|
|
1009
1139
|
```
|
|
1010
1140
|
|
|
1011
|
-
`dispatchToast` returns the toast id; `dismissToast(id)` removes
|
|
1141
|
+
`dispatchToast` returns the toast's id; `dismissToast(id)` removes the toast and `dismissAllToasts()` every toast. The 0.5 form builds a toast from text: `dispatchToast({ status: 'success', title: 'Saved', body: 'Your changes are saved.' })`. A plain object is read as options; anything else (an element, a string, an array) is content.
|
|
1142
|
+
|
|
1143
|
+
- **Parts.** `Toast.Title` holds the title text, which names the toast, and an `action` at the end of its row; `Toast.Body` holds the body text and a `subtitle`, which describe it; `Toast.Footer` is a row of actions below. Make them direct children of `Toast` (plain children are the body, and the `title` prop renders a title, as in 0.5). `icon` replaces the status icon: only `undefined` keeps it, and `null` shows none. Flat names for Server Components: `ToastTitle`, `ToastBody`, `ToastFooter`, `ToastClose`.
|
|
1144
|
+
- **`Toast.Close`** makes its single child dismiss the toast: it calls the Toast's `onDismiss` (when there is one), then dismisses the toast in the Toaster. Your `onClick` runs first, and `preventDefault()` keeps the toast. It also works in content without a `Toast`. Every Toast in a Toaster shows the Dismiss button as well.
|
|
1145
|
+
- **Severity.** The rendered Toast's `status` sets the announcement's politeness (`error` is assertive, the others polite) and its spoken prefix ("Error:", translated with `statusLabel`). For content without a `Toast`, pass `status` (and `statusLabel`) in the options: `dispatchToast(<UploadProgress />, { status: 'info' })`. A `status` in the options that differs from the Toast's own warns in development, and the Toast's wins.
|
|
1146
|
+
- **What is announced.** The Toaster reads each toast once, through permanent live regions that need no provider: the severity, the title, the body with its subtitle, then the labels of its actions ("Success: Email sent Your message was delivered. To 3 people Undo"), never the Dismiss button. Hidden text is skipped, and an image's `alt` is read. `announcement` in the options replaces everything but the severity, for a toast whose visible text is no good announcement (a progress toast). Dispatching the same `toastId` again replaces the toast and announces it again.
|
|
1147
|
+
- **Timing.** A toast times out after 5 seconds (`timeout` in the options). Every toast pauses while the pointer or focus is anywhere in the toast region, and while the page is in the background (the window has lost focus, or the tab is hidden, also when the Toaster mounted there). A toast with an action (a button, a link or a field besides its Dismiss button) stays until it is dismissed: people who use a screen reader, a switch or voice control need time to reach it. Pass `timeout` to change that deliberately.
|
|
1148
|
+
- **Keyboard.** `shortcuts.focus` returns `true` for the key press that moves focus to the newest toast; use a modifier (or let people change the shortcut), since a single-character shortcut fails WCAG 2.1.4. A focused toast is announced by its severity and title. Tab and Shift+Tab move through the toasts' controls; Escape dismisses every toast and returns focus to where it was (a tooltip or menu opened from a toast closes first); Delete or Backspace dismisses the focused toast (the keys are ignored in editable fields), and focus moves to the next toast, or the previous one, else back to where it was before the toasts. An Escape in the toasts never closes a Dialog below them: the next Escape does, once focus is back in the dialog.
|
|
1149
|
+
- Toasts stay reachable by Tab over an open Dialog and move out of an open Drawer's way. `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.
|
|
1150
|
+
- **`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.
|
|
1151
|
+
- **`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.
|
|
1152
|
+
- **Motion.** Each toast mounts through the presence core inside a container with the class `wave-toaster__toast`: style its `data-presence` phases for an enter and exit motion of yours (see [Enter and exit motion](#enter-and-exit-motion)). A dismissed toast stays mounted and `inert` until the exit ends; focus has already moved on. Wave ships no toast motion.
|
|
1153
|
+
|
|
1154
|
+
**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:
|
|
1155
|
+
|
|
1156
|
+
```tsx
|
|
1157
|
+
import * as React from 'react';
|
|
1158
|
+
import { Toast, Toaster, createToaster } from '@mortenbrudvik/waveui';
|
|
1159
|
+
|
|
1160
|
+
/** Module scope is safe: on the server a dispatch does nothing. */
|
|
1161
|
+
export const toaster = createToaster();
|
|
1162
|
+
|
|
1163
|
+
/** Callable from anywhere: a query cache's onError, an event handler, a helper. */
|
|
1164
|
+
export function notifyUploadFailed(message: string) {
|
|
1165
|
+
return toaster.dispatchToast(
|
|
1166
|
+
<Toast status="error">
|
|
1167
|
+
<Toast.Title>Upload failed</Toast.Title>
|
|
1168
|
+
<Toast.Body>{message}</Toast.Body>
|
|
1169
|
+
</Toast>,
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
export function Notifications({ children }: { children: React.ReactNode }) {
|
|
1174
|
+
return <Toaster toaster={toaster}>{children}</Toaster>;
|
|
1175
|
+
}
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
A handle's toasts get everything above (the announcements, the pausing, `limit`, the focus handling, the Drawer offset, reachability over modals), and their content reads React context from where the Toaster is mounted, so a router link works. Dispatches before a Toaster binds wait in the handle's queue and show once it binds; `dispatchToast` returns the id at once, so `dismissToast(id)` also removes a waiting toast. After the Toaster unmounts, the handle queues again until the next one binds. A handle binds one Toaster (a second one does not bind, and warns in development): give each Toaster its own handle to run several (Fluent's `toasterId`). `useToastController()` keeps working inside a bound Toaster. Keep notifications in the Toaster rather than in an area of your own: an open modal `Dialog` or `Drawer` makes the rest of the page `inert`, so your own area would take no clicks, drop out of the accessibility tree and sit under the dialog, while the Toaster's region stays usable over modals.
|
|
1179
|
+
|
|
1180
|
+
### Message bars
|
|
1012
1181
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1182
|
+
```tsx
|
|
1183
|
+
import { Button, MessageBar } from '@mortenbrudvik/waveui';
|
|
1184
|
+
|
|
1185
|
+
export function UpdateNotice({ onUpdate, onLater }: { onUpdate: () => void; onLater: () => void }) {
|
|
1186
|
+
return (
|
|
1187
|
+
<MessageBar status="warning" onDismiss={onLater}>
|
|
1188
|
+
<MessageBar.Title>Update available</MessageBar.Title>A new version is ready to install.
|
|
1189
|
+
<MessageBar.Actions>
|
|
1190
|
+
<Button size="small" onClick={onUpdate}>
|
|
1191
|
+
Update now
|
|
1192
|
+
</Button>
|
|
1193
|
+
<Button size="small" appearance="subtle" onClick={onLater}>
|
|
1194
|
+
Later
|
|
1195
|
+
</Button>
|
|
1196
|
+
</MessageBar.Actions>
|
|
1197
|
+
</MessageBar>
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
- **Parts.** `MessageBar.Title` is a semibold lead-in, read right after the severity; `MessageBar.Actions` holds buttons (Retry, Learn more) after the text. Both are direct children of the bar (Fragments count, and so do the flat `MessageBarTitle` and `MessageBarActions` written in a Server Component); one inside a component of yours renders where it is and warns in development. The built-in Dismiss button (`onDismiss`) stays the bar's own, after the actions.
|
|
1203
|
+
- **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`).
|
|
1204
|
+
- **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.
|
|
1015
1205
|
|
|
1016
1206
|
### Badges
|
|
1017
1207
|
|
|
@@ -1131,6 +1321,52 @@ export function People({ people }: { people: Person[] }) {
|
|
|
1131
1321
|
|
|
1132
1322
|
Row selection: with `selectionMode="single"`, pass at most one id in `selectedItems`/`defaultSelectedItems` (with several, the first one whose row is rendered is selected, 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.
|
|
1133
1323
|
|
|
1324
|
+
### Trees
|
|
1325
|
+
|
|
1326
|
+
```tsx
|
|
1327
|
+
import * as React from 'react';
|
|
1328
|
+
import { Button, Tree } from '@mortenbrudvik/waveui';
|
|
1329
|
+
|
|
1330
|
+
export function FileTree() {
|
|
1331
|
+
const [checked, setChecked] = React.useState<string[]>(['docs']);
|
|
1332
|
+
return (
|
|
1333
|
+
<Tree
|
|
1334
|
+
aria-label="Files"
|
|
1335
|
+
selectionMode="multiple"
|
|
1336
|
+
checkedItems={checked}
|
|
1337
|
+
onCheckedItemsChange={setChecked}
|
|
1338
|
+
>
|
|
1339
|
+
<Tree.Item value="docs" aside="3 files">
|
|
1340
|
+
Documents
|
|
1341
|
+
<Tree.Item value="cv">CV.pdf</Tree.Item>
|
|
1342
|
+
<Tree.Item value="letter">Letter.docx</Tree.Item>
|
|
1343
|
+
<Tree.Item value="notes">Notes.txt</Tree.Item>
|
|
1344
|
+
</Tree.Item>
|
|
1345
|
+
<Tree.Item
|
|
1346
|
+
value="photos"
|
|
1347
|
+
actions={
|
|
1348
|
+
<Button size="small" appearance="subtle">
|
|
1349
|
+
Share
|
|
1350
|
+
</Button>
|
|
1351
|
+
}
|
|
1352
|
+
>
|
|
1353
|
+
Photos
|
|
1354
|
+
<Tree.Item value="beach">Beach.jpg</Tree.Item>
|
|
1355
|
+
</Tree.Item>
|
|
1356
|
+
</Tree>
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
- **Selection modes.** `selectionMode` is `'none'` (the default: activation only, and `selected` highlights one item), `'single'` (a radio indicator on every item for `selected`/`onItemSelect`; pass `selected`, a development warning asks for it) or `'multiple'` (a checkbox on every item, bound to `checkedItems` or `defaultCheckedItems`; Fluent's `'multiselect'`). The root carries `data-selection-mode`.
|
|
1362
|
+
- **Checks cascade.** Checking an item checks it and all its descendants; unchecking does the reverse. A parent shows checked when all its descendants are checked, mixed when some are and unchecked when none are, and a listed parent also checks children that load later. `onCheckedItemsChange(checkedItems, details)` fires only on change, with a new array: every checked item in tree order (fully checked parents included, mixed ones not), then the listed values that match no item, unchanged, so values filtered out of the tree are not lost. `details` gives the toggled item's `value`, its new `checked` state and the DOM `event` (typed optional until 1.0).
|
|
1363
|
+
- **Lazy children.** A "Loading…" placeholder item is an item like any other: checking its parent lists its value too, and once the real children replace it, that value matches no item and stays in the list. Drop it from your state when the children arrive (a listed parent keeps checking them).
|
|
1364
|
+
- **Keys and clicks.** In a checkbox tree Space toggles the focused item and Enter activates it (`onItemSelect`); a click on the checkbox toggles, and a click elsewhere on the row activates. The checkbox is a real `role="checkbox"` named by the item, outside the Tab order, so touch screen readers and voice control reach it too. `*`, `current` and typeahead work as before (a Space typed within 500 ms of a letter continues the search).
|
|
1365
|
+
- **Row content.** `Tree.Item` takes `actions` (buttons at the end of the row, in a toolbar named by the item; shown while the row is hovered or has focus, and always in forced colors and on devices without hover), `aside` (a count or badge at the end of the row, part of the item's description; it gives way to the actions on hover and focus), `iconAfter` (decorative, after the label) and `expandIcon` (replaces the chevron: `null` or `undefined` keep it, `false` hides it, and `aria-expanded` still reports the state). Keys and clicks on the actions never activate, check or expand the item. Give an icon-only action an `aria-label`. Hidden actions keep their width, so a row does not shift when they show; prefer icon-only actions in a narrow tree.
|
|
1366
|
+
- **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).
|
|
1367
|
+
- **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.
|
|
1368
|
+
- **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.
|
|
1369
|
+
|
|
1134
1370
|
### Slots
|
|
1135
1371
|
|
|
1136
1372
|
Props such as `icon`, `contentBefore` and `dismiss` are slots. A slot accepts content (rendered inside the slot's element) or an object with `as`, `className`, `children` and any attribute of the slot's element:
|
|
@@ -1153,45 +1389,48 @@ Icon slots are decorative (`aria-hidden`). Content that renders nothing (`''`, `
|
|
|
1153
1389
|
|
|
1154
1390
|
Slots that replace a built-in glyph follow one rule per kind:
|
|
1155
1391
|
|
|
1156
|
-
- **Optional indicators** (`MenuButton.menuIcon`, the `expandIcon` of Combobox and
|
|
1392
|
+
- **Optional indicators** (`MenuButton.menuIcon`, the `expandIcon` of Combobox, TimePicker and `Tree.Item`): `null` or `undefined` keep the default glyph; `false` or any value that renders nothing hides it (for Combobox and TimePicker, the whole expand button).
|
|
1157
1393
|
- **Required indicators** (`SplitButton.menuIcon`): the indicator always shows. A value that renders nothing keeps the default glyph, with a development warning.
|
|
1158
|
-
- **Status icons** (`MessageBar.icon`, Field `validationMessageIcon`): only `undefined` keeps the default icon; `null` or a value that renders nothing shows none.
|
|
1394
|
+
- **Status icons** (`MessageBar.icon`, `Toast.icon`, Field `validationMessageIcon`): only `undefined` keeps the default icon; `null` or a value that renders nothing shows none.
|
|
1159
1395
|
- A glyph slot inside a built-in button (`expandIcon`, and the `menuIcon` of SplitButton and MenuButton) is decorative content. A `<button>` or `Button` passed there, or a slot object whose `as` is one, is not nested: its children become the glyph and its props are dropped, with a development warning.
|
|
1160
1396
|
|
|
1161
1397
|
### Built-in text
|
|
1162
1398
|
|
|
1163
1399
|
The names and hidden texts that components render themselves are English by default. These props translate them:
|
|
1164
1400
|
|
|
1165
|
-
| Component | Props
|
|
1166
|
-
| --------------------------- |
|
|
1167
|
-
| `Dialog.Content`, `Drawer` | `closeLabel` (the Close button)
|
|
1168
|
-
| `TeachingPopover` | `closeLabel` (the Close button), `labels` (`back`, `next`, `done`, `step(index, count)` with a zero-based index)
|
|
1169
|
-
| `MessageBar`, `Toast` | `statusLabel` (the hidden severity text); `Toast` also `dismissLabel` (also
|
|
1170
|
-
| `Tag` | `dismissLabel`
|
|
1171
|
-
| `Carousel` | `labels` (`previous`, `next`, `picker`, `slide(index, total)` with a zero-based index, `carouselRoleDescription`, `slideRoleDescription`), `autoPlayLabels`
|
|
1172
|
-
| `Stepper` | `statusLabels` (`completed`, `error`; `''` omits the text)
|
|
1173
|
-
| `Pagination` | `getItemAriaLabel(type, page, selected)`
|
|
1174
|
-
| `ColorPicker` | `labels`
|
|
1175
|
-
| `
|
|
1176
|
-
| `
|
|
1177
|
-
| `
|
|
1178
|
-
| `
|
|
1179
|
-
| `
|
|
1180
|
-
| `
|
|
1181
|
-
| `
|
|
1182
|
-
| `
|
|
1183
|
-
| `
|
|
1184
|
-
| `
|
|
1185
|
-
| `
|
|
1186
|
-
| `
|
|
1401
|
+
| Component | Props |
|
|
1402
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1403
|
+
| `Dialog.Content`, `Drawer` | `closeLabel` (the Close button) |
|
|
1404
|
+
| `TeachingPopover` | `closeLabel` (the Close button), `labels` (`back`, `next`, `done`, `step(index, count)` with a zero-based index) |
|
|
1405
|
+
| `MessageBar`, `Toast` | `statusLabel` (the hidden severity text; also an option of both `dispatchToast` forms); `Toast` also `dismissLabel` (also an option of the text form) |
|
|
1406
|
+
| `Tag` | `dismissLabel` |
|
|
1407
|
+
| `Carousel` | `labels` (`previous`, `next`, `picker`, `slide(index, total)` with a zero-based index, `carouselRoleDescription`, `slideRoleDescription`), `autoPlayLabels` |
|
|
1408
|
+
| `Stepper` | `statusLabels` (`completed`, `error`; `''` omits the text) |
|
|
1409
|
+
| `Pagination` | `getItemAriaLabel(type, page, selected)` |
|
|
1410
|
+
| `ColorPicker` | `labels` (the 0.7 strings, plus `area` (the name of a bound area's group, "Saturation and brightness"), `saturation`, `brightness`, `roleDescription`, `hue`, `transparency`, `colorName(color)` and `valueText(color, channel)` for bound parts) |
|
|
1411
|
+
| `ColorArea` | `labels` (`saturation`, `brightness`, `roleDescription` ("2D slider"), `colorName(color)`, `valueText(color, channel)`) |
|
|
1412
|
+
| `ColorSlider` | `labels` (`hue`, `saturation`, `brightness` (the `value` channel), `colorName(color)`, `valueText(color, channel)`) |
|
|
1413
|
+
| `AlphaSlider` | `labels` (`opacity`, `transparency`, `valueText(color, transparency)`) |
|
|
1414
|
+
| `Combobox` | `labels` (`noMatches`, `clear` (the clear button), `expand` (the chevron button)) |
|
|
1415
|
+
| `Dropdown` | `labels` (`clear`, the clear button) |
|
|
1416
|
+
| `DatePicker` | `labels` (`clear`, `openCalendar`, `previousMonth`, `nextMonth`, and the error texts `invalidDate(pattern)`, `outOfRange(min, max)`, `unavailableDate`) |
|
|
1417
|
+
| `TimePicker` | `labels` (`clear`, `expand`, `list`, `noTimes`, `noMatches`, and the error texts `invalidTime(format)`, `outOfRange(min, max)`) |
|
|
1418
|
+
| `TagPicker` | `labels` (`remove(label)`, `selected`, `summary(labels)`, `added(label, count)`, `removed(label, count)`, `noMatches`) |
|
|
1419
|
+
| `Rating` | `labels` (`star(value, max)`) |
|
|
1420
|
+
| `RatingDisplay` | `labels` (`rating(value, max, formattedValue)`, `count(count, formattedCount)`); `locale` formats the value and the count (pass it when rendering on the server) |
|
|
1421
|
+
| `SpinButton` | `labels` (`increment`, `decrement`) |
|
|
1422
|
+
| `DataGrid` | `labels` (`selectAll`, `selectionHeader`); each row's fallback name "Select row": `DataGrid.Row` `selectionLabel` |
|
|
1423
|
+
| `AvatarGroup` | `overflowLabel`, `unnamedMemberLabel` |
|
|
1424
|
+
| `InfoLabel`, `SplitButton` | `infoButtonLabel`, `menuButtonLabel` |
|
|
1425
|
+
| `Spinner`, `Skeleton.Group` | `label` |
|
|
1187
1426
|
|
|
1188
1427
|
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`.
|
|
1189
1428
|
|
|
1190
|
-
0.7
|
|
1429
|
+
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.
|
|
1191
1430
|
|
|
1192
1431
|
## Keyboard support
|
|
1193
1432
|
|
|
1194
|
-
As implemented in 0.
|
|
1433
|
+
As implemented in 0.8. 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.
|
|
1195
1434
|
|
|
1196
1435
|
| Component | Keys |
|
|
1197
1436
|
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -1199,7 +1438,7 @@ As implemented in 0.7. Buttons, links, checkboxes, switches and the trigger butt
|
|
|
1199
1438
|
| RadioGroup, SwatchPicker | 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. |
|
|
1200
1439
|
| Rating | Right/Up one star more, Left/Down one fewer (never below 1), Home/End first/last; mirrored in RTL. |
|
|
1201
1440
|
| 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. |
|
|
1202
|
-
| 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
|
|
1441
|
+
| 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. |
|
|
1203
1442
|
| List (selectable) | One Tab stop. Up/Down (wrapping), Home/End, Enter/Space toggle, typeahead above 7 items (a Space within 500 ms of a letter continues the search). With item actions (grid): Up/Down between rows, Left/Right into the actions, Enter/F2 into a text field, Escape back. |
|
|
1204
1443
|
| 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. |
|
|
1205
1444
|
| 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. |
|
|
@@ -1214,6 +1453,9 @@ As implemented in 0.7. Buttons, links, checkboxes, switches and the trigger butt
|
|
|
1214
1453
|
| Card (selectable) | With the default `selectionControl="card"`: Enter (key down) or Space (key up) selects. With `"checkbox"`: the built-in checkbox. |
|
|
1215
1454
|
| Stepper | Each reachable step is a Tab stop; Enter/Space activate. |
|
|
1216
1455
|
| Slider | Native range keys. |
|
|
1456
|
+
| Toaster | The app's `shortcuts.focus` key press focuses the newest toast; Tab and Shift+Tab move through the toasts' controls; Escape dismisses every toast and returns focus (a tooltip or menu of a toast closes first); Delete or Backspace dismisses the focused toast (the keys are ignored in editable fields), and focus moves to the next toast, or the previous one, else back to where it was before the toasts. |
|
|
1457
|
+
| ColorArea | One Tab stop. Left/Right change saturation (mirrored in RTL), Up/Down brightness; Shift with an arrow by 10; PageUp/PageDown brightness by 10; Home/End saturation to 0/100. |
|
|
1458
|
+
| ColorSlider, AlphaSlider | The arrows of their axis by 1 (mirrored in RTL when horizontal), Shift with an arrow by 10, PageUp/PageDown by 10, Home/End to the minimum/maximum. |
|
|
1217
1459
|
|
|
1218
1460
|
Accordion and Carousel have no arrow-key navigation: their triggers and controls are Tab stops.
|
|
1219
1461
|
|
|
@@ -1221,25 +1463,46 @@ Typeahead (Menu, selectable List, Tree, Dropdown) accepts characters typed with
|
|
|
1221
1463
|
|
|
1222
1464
|
## Hooks and utilities
|
|
1223
1465
|
|
|
1224
|
-
| Export | Purpose
|
|
1225
|
-
| ------------------------------------------------------------------------------- |
|
|
1226
|
-
| `useControllable(value, defaultValue, onChange)` | Controlled/uncontrolled state; returns `[value, setValue, isControlled]`; `setValue` accepts a value or an updater and skips no-op updates.
|
|
1227
|
-
| `useRovingTabIndex(options)` | Roving tab stop and arrow-key navigation for composite widgets; items marked `data-disabled-focusable` stay reachable,
|
|
1228
|
-
| `useId(prefix?)` | SSR-safe ids (`prefix-<react id>`; never parse the React part).
|
|
1229
|
-
| `useEventCallback(fn)` | Stable callback that always calls the latest `fn`.
|
|
1230
|
-
| `useMergedRefs(...refs)` | One stable callback ref for several refs.
|
|
1231
|
-
| `useIsClient()` | `false` on the server and during hydration.
|
|
1232
|
-
| `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)).
|
|
1233
|
-
| `useFieldControl(props, options?)` | Merge a custom control's labelling props with the surrounding `Field` (its label, validation message, hint and state).
|
|
1234
|
-
| `useAnnounce()`, `announce(message, politeness?)` | Screen-reader announcements through a shared live region.
|
|
1235
|
-
| `useWaveTheme()` | Current theme, direction and portal container.
|
|
1236
|
-
| `useToastController()` | `dispatchToast` / `dismissToast` / `dismissAllToasts` inside `<Toaster>`.
|
|
1237
|
-
| `
|
|
1238
|
-
| `
|
|
1239
|
-
| `
|
|
1240
|
-
| `
|
|
1241
|
-
| `
|
|
1242
|
-
| `
|
|
1466
|
+
| Export | Purpose |
|
|
1467
|
+
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1468
|
+
| `useControllable(value, defaultValue, onChange)` | Controlled/uncontrolled state; returns `[value, setValue, isControlled]`; `setValue` accepts a value or an updater and skips no-op updates. |
|
|
1469
|
+
| `useRovingTabIndex(options)` | Roving tab stop and arrow-key navigation for composite widgets; items marked `data-disabled-focusable` stay reachable, a composite marked `data-roving-transparent` (a toolbar radio group) adds its items to the enclosing arrow order, and `tabStop: 'none'` leaves every item out of the Tab order (a composite reached by arrow keys only). |
|
|
1470
|
+
| `useId(prefix?)` | SSR-safe ids (`prefix-<react id>`; never parse the React part). |
|
|
1471
|
+
| `useEventCallback(fn)` | Stable callback that always calls the latest `fn`. |
|
|
1472
|
+
| `useMergedRefs(...refs)` | One stable callback ref for several refs. |
|
|
1473
|
+
| `useIsClient()` | `false` on the server and during hydration. |
|
|
1474
|
+
| `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)). |
|
|
1475
|
+
| `useFieldControl(props, options?)` | Merge a custom control's labelling props with the surrounding `Field` (its label, validation message, hint and state). |
|
|
1476
|
+
| `useAnnounce()`, `announce(message, politeness?)` | Screen-reader announcements through a shared live region. |
|
|
1477
|
+
| `useWaveTheme()` | Current theme, direction and portal container. |
|
|
1478
|
+
| `useToastController()` | `dispatchToast` (content or options) / `dismissToast` / `dismissAllToasts` inside `<Toaster>`. |
|
|
1479
|
+
| `createToaster()` | A toast controller for code outside components; bind it with `<Toaster toaster={handle}>` (see [Toasts](#toasts)). Server-safe. |
|
|
1480
|
+
| `useIsOverflowing(target)`, `useOverflowMenu()`, `useIsOverflowItemVisible(id)` | Overflow detection. |
|
|
1481
|
+
| `hsvToHex(color)`, `hexToHsv(hex)` | Convert between an `HsvColor` and a hex string (`#rrggbb`, or `#rrggbbaa` below full opacity); `hexToHsv` returns `null` for other text. Server-safe. |
|
|
1482
|
+
| `buttonClassNames`, `toastTitleClassNames`, … | The stable class names of every component and part (see [Styling hooks](#styling-hooks)). Server-safe. |
|
|
1483
|
+
| `cn(...classes)` | clsx + tailwind-merge that knows Wave's type ramp and shadows; the last class wins over a conflicting class of the same variant. |
|
|
1484
|
+
| `composeEventHandlers(theirs, ours)` | Run a consumer handler, then yours unless it called `preventDefault()`. |
|
|
1485
|
+
| `mergeRefs(...refs)` | Merge refs outside components. |
|
|
1486
|
+
| `resolveSlot`, `renderSlot` | The slot helpers the components use. |
|
|
1487
|
+
| `getThemeClassName(theme)` | The theme classes of a theme. |
|
|
1488
|
+
|
|
1489
|
+
## Upgrading from 0.7
|
|
1490
|
+
|
|
1491
|
+
0.8 removes nothing public; these changes can still affect your tests and styles:
|
|
1492
|
+
|
|
1493
|
+
1. `Dialog.Content`'s 1rem gap to the viewport is now the surface's own margin: a margin class of yours replaces it on its sides, a percentage width or height resolves against the whole viewport, and a surface you position with `fixed` or `absolute` needs `m-0`.
|
|
1494
|
+
2. Muted `Dialog.Content` body text becomes the primary text colour, as in Drawer and Popover: put text that should stay secondary in an element with `text-muted-foreground`.
|
|
1495
|
+
3. Every component root and part carries a stable class (`wave-button`, `wave-button__icon`, …): assertions on a complete class string change.
|
|
1496
|
+
4. A toast in a Toaster renders inside a `role="group"` container named by its severity and title; the container now carries `data-wave-toast` and `data-presence`; the 0.5 `title` and body render as the `Toast.Title` and `Toast.Body` elements.
|
|
1497
|
+
5. Escape inside the toast region dismisses the toasts and no longer closes a Dialog or Drawer below them.
|
|
1498
|
+
6. Every toast pauses while the pointer or focus is anywhere in the toast region (0.7 paused the hovered or focused toast only).
|
|
1499
|
+
7. A toast dispatched with content that holds an action stays until it is dismissed, unless the dispatch passes `timeout`; the options form is unchanged.
|
|
1500
|
+
8. MessageBar renders `data-layout`, and Tree `data-selection-mode`.
|
|
1501
|
+
9. The default ColorPicker gains a color area and a hue slider, its opacity slider becomes an alpha slider (still named "Opacity"), and ColorPicker `children` now render a custom layout.
|
|
1502
|
+
10. `ToastController['dispatchToast']` is overloaded: a test double that implements it must accept content too.
|
|
1503
|
+
11. A `dispatchToast` options argument that is not a plain object (a class instance, `Object.create(defaults)`) is now read as React content and throws when it renders: pass a plain object (spread it: `{ ...defaults, title }`).
|
|
1504
|
+
|
|
1505
|
+
The CHANGELOG's [0.8.0 "Changed" section](CHANGELOG.md#changed) lists every behaviour, DOM and type change.
|
|
1243
1506
|
|
|
1244
1507
|
## Upgrading from 0.6
|
|
1245
1508
|
|
|
@@ -1255,7 +1518,7 @@ Typeahead (Menu, selectable List, Tree, Dropdown) accepts characters typed with
|
|
|
1255
1518
|
8. Wave now requires tailwind-merge 3.7, so an axis shorthand in your `className` (`px-6`) replaces a component's own logical side class of the same variant (`pe-8` on `Dialog.Title`) instead of losing to it in the CSS: write `px-6 pe-8` to keep the side.
|
|
1256
1519
|
9. Wave requires Node.js 22.12 or later (`engines`; Node.js 20 reached its end of life on 2026-04-30). npm and pnpm warn on an older Node.js (npm stops with `engine-strict`), and Yarn 1 refuses to install.
|
|
1257
1520
|
|
|
1258
|
-
The CHANGELOG's [0.7.0 "Changed" section](CHANGELOG.md#changed) lists every behaviour, DOM and type change.
|
|
1521
|
+
The CHANGELOG's [0.7.0 "Changed" section](CHANGELOG.md#changed-1) lists every behaviour, DOM and type change.
|
|
1259
1522
|
|
|
1260
1523
|
## Upgrading from 0.5
|
|
1261
1524
|
|
|
@@ -1270,11 +1533,11 @@ The CHANGELOG's [0.7.0 "Changed" section](CHANGELOG.md#changed) lists every beha
|
|
|
1270
1533
|
7. RatingDisplay formats its accessible name with the runtime locale when `locale` is omitted: pass `locale` when rendering on the server.
|
|
1271
1534
|
8. Badge `color="important"` becomes a neutral color in 1.0: switch to `color="severe"` to keep the orange look.
|
|
1272
1535
|
|
|
1273
|
-
The CHANGELOG's [0.6.0 "Changed" section](CHANGELOG.md#changed-
|
|
1536
|
+
The CHANGELOG's [0.6.0 "Changed" section](CHANGELOG.md#changed-2) lists every behaviour, DOM and type change.
|
|
1274
1537
|
|
|
1275
1538
|
## Upgrading from 0.4
|
|
1276
1539
|
|
|
1277
|
-
npm went from 0.4.0 to 0.7.0: 0.5.0 and 0.6.0 were never published, and their changes ship in 0.7.0. From 0.4.0, take these steps, then those of [Upgrading from 0.5](#upgrading-from-05)
|
|
1540
|
+
npm went from 0.4.0 to 0.7.0: 0.5.0 and 0.6.0 were never published, and their changes ship in 0.7.0. From 0.4.0, take these steps, then those of [Upgrading from 0.5](#upgrading-from-05), [Upgrading from 0.6](#upgrading-from-06) and [Upgrading from 0.7](#upgrading-from-07).
|
|
1278
1541
|
|
|
1279
1542
|
1. The npm package name is unchanged (`@mortenbrudvik/waveui`, as in 0.4.0). Replace `waveui` imports copied from the 0.4 guide, and a git or local dependency named `waveui`, with `@mortenbrudvik/waveui`.
|
|
1280
1543
|
2. Styles: without Tailwind, keep `import '@mortenbrudvik/waveui/styles'` and make sure a `WaveProvider` wraps the app; with Tailwind 4, switch to `@import '@mortenbrudvik/waveui/tailwind';` after `@import 'tailwindcss';`. Import `preflight.css` if you relied on Wave's Preflight.
|