@mortenbrudvik/waveui 0.7.0 → 0.9.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 +564 -155
- 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/InfoButton.cjs +2 -0
- package/dist/components/data-display/InfoButton.cjs.map +1 -0
- package/dist/components/data-display/InfoButton.mjs +173 -0
- package/dist/components/data-display/InfoButton.mjs.map +1 -0
- 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 +36 -92
- 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 +87 -70
- 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 +22 -22
- package/dist/components/input/Combobox.expand.mjs.map +1 -1
- package/dist/components/input/Combobox.mjs +151 -141
- 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 +239 -234
- 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 +109 -100
- 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 +139 -105
- 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 +80 -55
- 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 +19 -12
- 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.item.cjs +2 -0
- package/dist/components/input/Rating.item.cjs.map +1 -0
- package/dist/components/input/Rating.item.mjs +255 -0
- package/dist/components/input/Rating.item.mjs.map +1 -0
- package/dist/components/input/Rating.mjs +152 -152
- 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 +162 -132
- 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 +34 -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 +78 -54
- 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 +202 -99
- 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 +75 -58
- 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 +199 -169
- 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 +31 -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 +185 -177
- 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/input/fieldLabel.cjs +2 -0
- package/dist/components/input/fieldLabel.cjs.map +1 -0
- package/dist/components/input/fieldLabel.mjs +17 -0
- package/dist/components/input/fieldLabel.mjs.map +1 -0
- package/dist/components/input/inputLook.cjs +2 -0
- package/dist/components/input/inputLook.cjs.map +1 -0
- package/dist/components/input/inputLook.mjs +28 -0
- package/dist/components/input/inputLook.mjs.map +1 -0
- package/dist/components/input/pickerStyles.cjs +1 -1
- package/dist/components/input/pickerStyles.cjs.map +1 -1
- package/dist/components/input/pickerStyles.mjs +42 -4
- package/dist/components/input/pickerStyles.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 +114 -113
- 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 +148 -151
- package/dist/components/overlays/Popover.mjs.map +1 -1
- package/dist/components/overlays/Popover.shared.cjs +1 -1
- package/dist/components/overlays/Popover.shared.cjs.map +1 -1
- package/dist/components/overlays/Popover.shared.mjs +82 -77
- package/dist/components/overlays/Popover.shared.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 +36 -29
- 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/useFieldControl.cjs.map +1 -1
- package/dist/hooks/useFieldControl.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 +2890 -197
- package/dist/index.d.ts +2890 -197
- package/dist/index.mjs +101 -87
- package/dist/lib/classNames.cjs +2 -0
- package/dist/lib/classNames.cjs.map +1 -0
- package/dist/lib/classNames.mjs +423 -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/styles.cjs +1 -1
- package/dist/lib/styles.cjs.map +1 -1
- package/dist/lib/styles.mjs +21 -4
- package/dist/lib/styles.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/src/styles/tokens.css +13 -0
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: 69 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.8](#upgrading-from-08) · [Upgrading from 0.7](#upgrading-from-07) · [Upgrading from 0.6](#upgrading-from-06) · [Upgrading from 0.5](#upgrading-from-05) · [Upgrading from 0.4](#upgrading-from-04) · [Development](#development)
|
|
11
11
|
|
|
12
12
|
## Requirements
|
|
13
13
|
|
|
@@ -120,10 +120,50 @@ export function Shell({ children }: { children: React.ReactNode }) {
|
|
|
120
120
|
- A nested provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the enclosing provider, so the light panel above stays right-to-left. `portalContainer={null}` sends a nested subtree's overlays to `document.body`.
|
|
121
121
|
- The provider renders `<div class="wave-root wave-<theme>" dir data-wave-theme>` with `bg-background text-foreground font-wave text-body-1`; a `className` you pass wins.
|
|
122
122
|
- Portaled overlays (Dialog, Drawer, Popover, Menu, listboxes, Tooltip, Toaster) render in a `wave-portal` wrapper that carries the provider's theme classes, direction and font.
|
|
123
|
-
- `
|
|
123
|
+
- `inputDefaults`: `{ size?, appearance? }`, the default `size` and `appearance` of the text inputs and pickers below it (see [Sizes and appearances](#sizes-and-appearances)). A nested provider merges its own defined keys over the enclosing provider's, so `<WaveProvider inputDefaults={{ size: 'small' }}>` inside an app with `appearance: 'filled-darker'` gives small filled-darker fields; omitting the prop inherits the enclosing value.
|
|
124
|
+
- `useWaveTheme()` returns `{ theme, dir, themeClassName, portalContainer, inputDefaults, hasProvider }` (`inputDefaults` is `{}` outside a provider).
|
|
124
125
|
|
|
125
126
|
**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
127
|
|
|
128
|
+
**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.
|
|
129
|
+
|
|
130
|
+
**Following the operating system.** `theme` takes one of the named themes, so resolve the OS setting in your app and pass the result:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import * as React from 'react';
|
|
134
|
+
import { WaveProvider, getThemeClassName, type WaveTheme } from '@mortenbrudvik/waveui';
|
|
135
|
+
|
|
136
|
+
const DARK = '(prefers-color-scheme: dark)';
|
|
137
|
+
|
|
138
|
+
function subscribe(onChange: () => void) {
|
|
139
|
+
const query = window.matchMedia(DARK);
|
|
140
|
+
query.addEventListener('change', onChange);
|
|
141
|
+
return () => query.removeEventListener('change', onChange);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function useSystemTheme(): WaveTheme {
|
|
145
|
+
const dark = React.useSyncExternalStore(
|
|
146
|
+
subscribe,
|
|
147
|
+
() => window.matchMedia(DARK).matches,
|
|
148
|
+
() => false, // the server renders light
|
|
149
|
+
);
|
|
150
|
+
return dark ? 'dark' : 'light';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function Shell({ children }: { children: React.ReactNode }) {
|
|
154
|
+
const theme = useSystemTheme();
|
|
155
|
+
// The page outside the provider: its scrollbar and canvas.
|
|
156
|
+
React.useLayoutEffect(() => {
|
|
157
|
+
const classes = getThemeClassName(theme).split(' ');
|
|
158
|
+
document.documentElement.classList.add(...classes);
|
|
159
|
+
return () => document.documentElement.classList.remove(...classes);
|
|
160
|
+
}, [theme]);
|
|
161
|
+
return <WaveProvider theme={theme}>{children}</WaveProvider>;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
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).
|
|
166
|
+
|
|
127
167
|
### Customizing tokens
|
|
128
168
|
|
|
129
169
|
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.
|
|
@@ -169,7 +209,7 @@ Never load `legacy-tokens.css` in an app that defines shadcn/ui-style variables
|
|
|
169
209
|
Generated from `src/styles/tokens.css`. A value followed by a ramp or token name is a reference (`var(--wave-brand-80)`); the hex is its default.
|
|
170
210
|
|
|
171
211
|
<details>
|
|
172
|
-
<summary>All
|
|
212
|
+
<summary>All 64 color tokens per theme</summary>
|
|
173
213
|
|
|
174
214
|
| Token (`--wave-…`) | Light | Dark | High contrast |
|
|
175
215
|
| ------------------------- | ------------------------------ | ------------------------------ | ------------------------------ |
|
|
@@ -198,6 +238,8 @@ Generated from `src/styles/tokens.css`. A value followed by a ramp or token name
|
|
|
198
238
|
| `stroke-hover` | `#c7c7c7` | `#757575` | `#ffffff` |
|
|
199
239
|
| `stroke-accessible` | `#616161` | `#adadad` | `#ffffff` |
|
|
200
240
|
| `input` | `#d1d1d1` | `#666666` | `#ffffff` |
|
|
241
|
+
| `input-filled-darker` | `#f5f5f5` | `#141414` | `#000000` |
|
|
242
|
+
| `input-filled-stroke` | `transparent` | `transparent` | `#ffffff` |
|
|
201
243
|
| `ring` | `#0f6cbd` (brand-80) | `#479ef5` (brand-100) | `#ffff00` |
|
|
202
244
|
| `success` | `#107c10` | `#5db55d` | `#3ff23f` |
|
|
203
245
|
| `success-foreground` | `#ffffff` | `#000000` | `#000000` |
|
|
@@ -234,11 +276,14 @@ Generated from `src/styles/tokens.css`. A value followed by a ramp or token name
|
|
|
234
276
|
| `secondary-foreground` | `#242424` (foreground) | `#ffffff` (foreground) | `#ffffff` (foreground) |
|
|
235
277
|
| `accent` | `#0f6cbd` (primary) | `#62abf5` (primary) | `#1aebff` (primary) |
|
|
236
278
|
| `accent-foreground` | `#ffffff` (primary-foreground) | `#000000` (primary-foreground) | `#000000` (primary-foreground) |
|
|
279
|
+
| `input-filled-lighter` | `#ffffff` (background) | `#292929` (background) | `#000000` (background) |
|
|
237
280
|
|
|
238
281
|
</details>
|
|
239
282
|
|
|
240
283
|
Theme-independent variables on `:root`: `--wave-brand-10` … `--wave-brand-160` (also utilities such as `bg-brand-80`), `--wave-grey-2` … `--wave-grey-98`, `--wave-font-family` (utility `font-wave`), the stacking layers `--wave-z-overlay` (1000), `--wave-z-toast` (1100) and `--wave-z-tooltip` (1200), and the motion durations `--wave-duration-*` and curves `--wave-curve-*` (see [Enter and exit motion](#enter-and-exit-motion)).
|
|
241
284
|
|
|
285
|
+
The three `input-filled-*` tokens (utilities `bg-input-filled-darker`, `bg-input-filled-lighter`, `border-input-filled-stroke`) draw the `filled-darker` and `filled-lighter` appearances of the text inputs and pickers (see [Sizes and appearances](#sizes-and-appearances)); `input-filled-lighter` follows `background`, and the stroke is transparent except in the high-contrast theme.
|
|
286
|
+
|
|
242
287
|
Contrast of every text and non-text pair is checked per theme by `src/styles/__tests__/tokens.test.ts`. One known limit: the `warning` fill (`#fde300` in light) is a background and accent color only; use `warning-tint-foreground` for warning text and icons.
|
|
243
288
|
|
|
244
289
|
Other Wave theme values: the type ramp `text-caption-2`, `text-caption-1`, `text-body-1`, `text-body-2`, `text-subtitle-2`, `text-subtitle-1`, `text-title-3`, `text-title-2`, `text-title-1`, `text-large-title`, `text-display`; shadows `shadow-2`, `shadow-4`, `shadow-8`, `shadow-16`, `shadow-28`, `shadow-64`; animations `animate-wave-spin`, `animate-wave-spin-slow`, `animate-wave-pulse`, `animate-wave-indeterminate`, `animate-wave-indeterminate-rtl`; on the Tailwind path, the motion utilities `duration-wave-ultra-fast` … `duration-wave-ultra-slow` and `ease-wave-accelerate-max` … `ease-wave-linear`. Border radius uses Tailwind's default scale (Wave no longer overrides it).
|
|
@@ -265,7 +310,7 @@ What adding Wave changes outside its own components:
|
|
|
265
310
|
**Colliding utility names.** Wave's Tailwind color names are unprefixed, because they are the utility vocabulary 0.4 documented and that consumer code uses in `className` overrides. In a shared Tailwind build, or next to another stylesheet that defines the same class names, each of the following names resolves to a single definition in every utility that takes a color (`bg-`, `text-`, `border-`, `ring-`, `outline-`, `fill-`, `stroke-`, `accent-`, `caret-`, `decoration-`, `divide-`, `placeholder-`, `shadow-`, `from-`/`via-`/`to-`, …):
|
|
266
311
|
|
|
267
312
|
- Shared with shadcn/ui: `background`, `foreground`, `card`, `card-foreground`, `primary`, `primary-foreground`, `secondary`, `secondary-foreground`, `muted`, `muted-foreground`, `accent`, `accent-foreground`, `destructive`, `destructive-foreground`, `border`, `input`, `ring`.
|
|
268
|
-
- Wave's other names: `primary-hover`, `primary-pressed`, `error`, `error-foreground`, `subtle`, `subtle-hover`, `subtle-pressed`, `subtle-selected`, `selected`, `selected-foreground`, `stroke`, `stroke-hover`, `stroke-accessible`, `success`, `success-foreground`, `success-tint`, `success-tint-foreground`, `warning`, `warning-foreground`, `warning-tint`, `warning-tint-foreground`, `error-tint`, `error-tint-foreground`, `severe`, `severe-foreground`, `severe-tint`, `severe-tint-foreground`, `info`, `info-foreground`, `info-tint`, `info-tint-foreground`, `inverted`, `inverted-foreground`, `inverted-border`, `track`, `skeleton`, `rating`, `presence-available`, `presence-busy`, `presence-away`, `presence-offline`, `presence-oof`, `presence-glyph`, `backdrop`, `brand-10` … `brand-160`.
|
|
313
|
+
- Wave's other names: `primary-hover`, `primary-pressed`, `error`, `error-foreground`, `subtle`, `subtle-hover`, `subtle-pressed`, `subtle-selected`, `selected`, `selected-foreground`, `stroke`, `stroke-hover`, `stroke-accessible`, `success`, `success-foreground`, `success-tint`, `success-tint-foreground`, `warning`, `warning-foreground`, `warning-tint`, `warning-tint-foreground`, `error-tint`, `error-tint-foreground`, `severe`, `severe-foreground`, `severe-tint`, `severe-tint-foreground`, `info`, `info-foreground`, `info-tint`, `info-tint-foreground`, `inverted`, `inverted-foreground`, `inverted-border`, `input-filled-darker`, `input-filled-lighter`, `input-filled-stroke`, `track`, `skeleton`, `rating`, `presence-available`, `presence-busy`, `presence-away`, `presence-offline`, `presence-oof`, `presence-glyph`, `backdrop`, `brand-10` … `brand-160`.
|
|
269
314
|
|
|
270
315
|
Workarounds when your app has its own design system:
|
|
271
316
|
|
|
@@ -284,11 +329,46 @@ Workarounds when your app has its own design system:
|
|
|
284
329
|
|
|
285
330
|
Namespaced Wave utilities are being considered for 1.0.
|
|
286
331
|
|
|
332
|
+
## Styling hooks
|
|
333
|
+
|
|
334
|
+
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.
|
|
335
|
+
|
|
336
|
+
```css
|
|
337
|
+
/* app.css */
|
|
338
|
+
.wave-button__icon {
|
|
339
|
+
color: var(--wave-primary);
|
|
340
|
+
}
|
|
341
|
+
.wave-tree-item__actions {
|
|
342
|
+
gap: 2px;
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
The package exports the same names as typed constants, one per component (`as const`), so a selector built from them is checked by TypeScript:
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
import { buttonClassNames, treeItemClassNames } from '@mortenbrudvik/waveui';
|
|
350
|
+
|
|
351
|
+
// `.${buttonClassNames.icon}` is '.wave-button__icon'.
|
|
352
|
+
export const iconSelector = `.${buttonClassNames.icon}`;
|
|
353
|
+
|
|
354
|
+
export function checkedRows(tree: HTMLElement) {
|
|
355
|
+
return tree.querySelectorAll(`.${treeItemClassNames.root}[aria-checked='true']`);
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
- **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.
|
|
360
|
+
- **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', … }}`).
|
|
361
|
+
- **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`.
|
|
362
|
+
- **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`.
|
|
363
|
+
- **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.
|
|
364
|
+
- **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)).
|
|
365
|
+
- **Server Components.** The constants come from a server-safe module: a Server Component can import them from the entry.
|
|
366
|
+
|
|
287
367
|
## React Server Components
|
|
288
368
|
|
|
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
|
|
369
|
+
- 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
370
|
- 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.
|
|
371
|
+
- 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 stars of a Rating or RatingDisplay, the parts of Toast and MessageBar, the ColorPicker parts, `Tree.ItemPersonaLayout`, a `Label` or `InfoLabel` passed as the `label` of a Field, and a `Button` passed as a MessageBar, SearchBox or Tag dismiss slot.
|
|
292
372
|
- Event handlers and other functions cannot be passed from a Server Component; put interactive parts in a `'use client'` component.
|
|
293
373
|
- 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
374
|
|
|
@@ -358,33 +438,38 @@ export function SaveArea() {
|
|
|
358
438
|
}
|
|
359
439
|
```
|
|
360
440
|
|
|
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
|
-
| `
|
|
441
|
+
| Compound | Flat names |
|
|
442
|
+
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
443
|
+
| `Accordion` | `AccordionItem`, `AccordionTrigger`, `AccordionPanel` |
|
|
444
|
+
| `Breadcrumb` | `BreadcrumbItem` |
|
|
445
|
+
| `Card` | `CardHeader`, `CardBody`, `CardFooter` |
|
|
446
|
+
| `Carousel` | `CarouselItem` |
|
|
447
|
+
| `ColorPicker` | `ColorPickerHexField`, `ColorPickerSwatches`, `ColorPickerPreview` |
|
|
448
|
+
| `Combobox` | `ComboboxOption`, `ComboboxOptionGroup` (also `Option`, `OptionGroup`) |
|
|
449
|
+
| `DataGrid` | `DataGridHeader`, `DataGridHeaderCell`, `DataGridBody`, `DataGridRow`, `DataGridCell` |
|
|
450
|
+
| `Dialog` | `DialogTrigger`, `DialogContent`, `DialogTitle`, `DialogFooter`, `DialogClose` |
|
|
451
|
+
| `Drawer` | `DrawerTrigger`, `DrawerTitle`, `DrawerClose` |
|
|
452
|
+
| `Dropdown` | `DropdownOption`, `DropdownOptionGroup` |
|
|
453
|
+
| `List` | `ListItem` |
|
|
454
|
+
| `Menu` | `MenuTrigger`, `MenuPopover`, `MenuItem`, `MenuDivider`, `MenuItemCheckbox`, `MenuItemRadio`, `MenuItemSwitch`, `MenuItemLink`, `MenuGroup`, `MenuGroupHeader`, `MenuSplitGroup` |
|
|
455
|
+
| `MessageBar` | `MessageBarTitle`, `MessageBarActions` |
|
|
456
|
+
| `Nav` | `NavCategory`, `NavItem`, `NavSubItem` |
|
|
457
|
+
| `Overflow` | `OverflowItem` |
|
|
458
|
+
| `Popover` | `PopoverTrigger`, `PopoverContent` |
|
|
459
|
+
| `RadioGroup` | `RadioGroupItem` (also `RadioItem`) |
|
|
460
|
+
| `Rating` | `RatingItem` |
|
|
461
|
+
| `RatingDisplay` | `RatingDisplayItem` (also `RatingItem`) |
|
|
462
|
+
| `Skeleton` | `SkeletonGroup` |
|
|
463
|
+
| `Stepper` | `StepperStep` |
|
|
464
|
+
| `Table` | `TableHeader`, `TableHeaderCell`, `TableBody`, `TableRow`, `TableCell` |
|
|
465
|
+
| `TabList` | `TabListTab`, `TabListPanel`, `TabListPanels` |
|
|
466
|
+
| `Toast` | `ToastTitle`, `ToastBody`, `ToastFooter`, `ToastClose` |
|
|
467
|
+
| `Toolbar` | `ToolbarButton`, `ToolbarToggleButton`, `ToolbarRadioGroup`, `ToolbarRadioButton`, `ToolbarGroup`, `ToolbarDivider` |
|
|
468
|
+
| `Tree` | `TreeItem`, `TreeItemPersonaLayout` |
|
|
384
469
|
|
|
385
470
|
## Components
|
|
386
471
|
|
|
387
|
-
|
|
472
|
+
69 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
473
|
|
|
389
474
|
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
475
|
|
|
@@ -402,43 +487,47 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
402
487
|
|
|
403
488
|
### Inputs and forms
|
|
404
489
|
|
|
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
|
|
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
|
-
| `
|
|
490
|
+
| Component | Description |
|
|
491
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
492
|
+
| `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; `size` sets the label's size and the default `size` of the text inputs and pickers inside; `label` also takes a `Label` or `InfoLabel` element, rendered in place of Field's own `<label>` (see [Forms and `Field`](#forms-and-field)). |
|
|
493
|
+
| `Label` | Form label with a required indicator (`required`: the decorative asterisk, or your own content), `size` and `weight`. |
|
|
494
|
+
| `Input` | Text input with `size` and `appearance`, `contentBefore`/`contentAfter` slots, `onValueChange(value)` next to the native `onChange`, and an `error` message; `htmlSize` is the native `size` attribute. |
|
|
495
|
+
| `Textarea` | Multi-line text input with `size`, `appearance` and an `error` message. |
|
|
496
|
+
| `Select` | Styled native `<select>` with `size`, `appearance`, `htmlSize` (visible rows) and an `error` message. |
|
|
497
|
+
| `Checkbox` | `checked`/`defaultChecked`/`onCheckedChange`, `indeterminate`, `size` (`medium`, `large`) and `shape` (`square`, `circular`), a rich `label` before or after the box (`labelPosition`), `disabledFocusable`, native form support. |
|
|
498
|
+
| `Switch` | On/off toggle (`role="switch"`), `onCheckedChange`, `size` (`small`, `medium`), a rich `label` before, after or above it (`labelPosition`), `disabledFocusable`, native form support. |
|
|
499
|
+
| `RadioGroup` | Single choice with `RadioGroup.Item` items (a rich `label` each); `orientation`, `disabled`, `onValueChange`, native form support. |
|
|
500
|
+
| `SearchBox` | Search input with a clear button; `size`, `appearance`, `onValueChange`; input attributes go to the `<input>`. The root draws the field, so `className` and `style` style the field box, and `contentBefore`/`contentAfter` sit beside the text. |
|
|
501
|
+
| `Slider` | Styled native range input, filled up to the thumb; `size` (`small`, `medium`); `onValueChange(number)`. |
|
|
502
|
+
| `SpinButton` | Numeric input with step buttons; `size`, `appearance`; typed text is a draft committed on blur or Enter; `min`, `max`, `step`, `largeStep`, `precision`; `displayValue` shows formatted text; `allowEmpty` allows a `null` value; holding a step button repeats it (see [SpinButton](#spinbutton)). |
|
|
503
|
+
| `Combobox` | Editable combobox: typing filters the options; `freeform` makes the typed text the value; a chevron (`expandIcon`) and `clearable`. `Combobox.Option`, `Combobox.OptionGroup`. |
|
|
504
|
+
| `Dropdown` | Select-only combobox (a button) with typeahead; `clearable`. `Dropdown.Option`, `Dropdown.OptionGroup`. |
|
|
505
|
+
| `TagPicker` | Multi-select combobox that shows the selection as removable tags (a list named "Selected"); `size` (`medium`, `large`, `extra-large`), `appearance`; the input is described by a summary of the selection ("Selected: Apple, Banana"), and additions and removals are announced. |
|
|
506
|
+
| `DatePicker` | Date input with a calendar dialog; `size`, `appearance`; locale-aware format and parse, `minDate`, `maxDate`, `disabledDates`, `onInvalidInput`. |
|
|
507
|
+
| `TimePicker` | Time combobox (`12h`/`24h`, `step`, `minTime`, `maxTime`); `size`, `appearance`; value is `HH:mm`; `open`/`defaultOpen`/`onOpenChange` control the list; rejected text is kept and flagged (`onInvalidInput`). |
|
|
508
|
+
| `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). |
|
|
509
|
+
| `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. |
|
|
510
|
+
| `ColorSlider` | Hue (default), saturation or brightness slider (`channel`), horizontal or vertical. |
|
|
511
|
+
| `AlphaSlider` | Opacity slider over a checkerboard; `transparency` shows transparency. |
|
|
512
|
+
| `SwatchPicker` | Radio group of color swatches (`items` with a `label` each). |
|
|
513
|
+
| `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
514
|
|
|
427
515
|
### Data display
|
|
428
516
|
|
|
429
|
-
| Component | Description
|
|
430
|
-
| --------------- |
|
|
431
|
-
| `Avatar` | Image, initials or icon, with an image-failure fallback and an optional `badge`.
|
|
432
|
-
| `AvatarGroup` | Overlapping avatars; `max` adds an overflow button that lists the hidden members.
|
|
433
|
-
| `Badge` | Status or category label: `appearance` `filled`, `tint`, `outline`; semantic `color` (see [Badges](#badges)).
|
|
434
|
-
| `CounterBadge` | Count pill (`99+` above `overflowCount`) or, with `dot`, an unread dot; Badge's `color` palette; `showZero`.
|
|
435
|
-
| `PresenceBadge` | Availability badge with a distinct shape per status.
|
|
436
|
-
| `Tag` | Chip with an optional dismiss button (`dismissible`, `onDismiss`, `dismissLabel`); `onDismiss` and `dismissIcon` need `dismissible` (a development warning says so). Move focus when you remove a tag (see [Tags](#tags)).
|
|
437
|
-
| `InfoLabel` |
|
|
438
|
-
| `
|
|
439
|
-
| `
|
|
440
|
-
| `
|
|
441
|
-
| `
|
|
517
|
+
| Component | Description |
|
|
518
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
519
|
+
| `Avatar` | Image, initials or icon, with an image-failure fallback and an optional `badge`. |
|
|
520
|
+
| `AvatarGroup` | Overlapping avatars; `max` adds an overflow button that lists the hidden members. |
|
|
521
|
+
| `Badge` | Status or category label: `appearance` `filled`, `tint`, `outline`; semantic `color` (see [Badges](#badges)). |
|
|
522
|
+
| `CounterBadge` | Count pill (`99+` above `overflowCount`) or, with `dot`, an unread dot; Badge's `color` palette; `showZero`. |
|
|
523
|
+
| `PresenceBadge` | Availability badge with a distinct shape per status. |
|
|
524
|
+
| `Tag` | Chip with an optional dismiss button (`dismissible`, `onDismiss`, `dismissLabel`); `onDismiss` and `dismissIcon` need `dismissible` (a development warning says so). Move focus when you remove a tag (see [Tags](#tags)). |
|
|
525
|
+
| `InfoLabel` | A real `<label>` (`children`, `htmlFor`, `required`, `size`, `weight`, `disabled`) with an info button after it that shows `info` (text, links or other content) in a note; `openOnHover`, `infoButtonLabel`. Usable as the `label` of a Field. |
|
|
526
|
+
| `InfoButton` | A button with the info glyph that shows `info` in a `role="note"` next to it, without moving focus: opens on click, on mouse hover and on keyboard focus (`openOnHover={false}`: click only); Tab from the button enters the note; `size`. Named "Information" (`aria-label`). |
|
|
527
|
+
| `Persona` | Avatar with name, secondary text and presence. |
|
|
528
|
+
| `Divider` | Horizontal or vertical separator, optionally labelled. |
|
|
529
|
+
| `Image` | `<img>` with `fit`, `shape`, `shadow`, `bordered`; warns in development when `alt` is missing. Never wider than its parent; with the default `fit` its height follows its width. `fit="none"` keeps the top-left corner in view, `"center"` the middle. |
|
|
530
|
+
| `List` | Plain list, selectable listbox (single or multiple) or, with item `action`s, a grid; in a selectable List item values are unique (development warning). |
|
|
442
531
|
|
|
443
532
|
### Typography
|
|
444
533
|
|
|
@@ -448,27 +537,27 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
448
537
|
|
|
449
538
|
### Layout
|
|
450
539
|
|
|
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`).
|
|
540
|
+
| Component | Description |
|
|
541
|
+
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
542
|
+
| `Card` | Surface with `Card.Header`, `Card.Body`, `Card.Footer`; optionally selectable (`onSelect`, `selected`, `selectionControl`). |
|
|
543
|
+
| `Accordion` | Disclosure sections (`Accordion.Item`, `.Trigger`, `.Panel`); single (default) or `type="multiple"`; `headingLevel`; item values are unique (development warning). |
|
|
544
|
+
| `TabList` | Tabs (`TabList.Tab`, `.Panel`, `.Panels`) with automatic activation, or manual with `selectTabOnFocus={false}`; `orientation`; tab values are unique (development warning). |
|
|
545
|
+
| `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. |
|
|
546
|
+
| `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). |
|
|
547
|
+
| `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. |
|
|
548
|
+
| `Grid` | CSS grid with a column count and token gaps. |
|
|
549
|
+
| `Stack` | Vertical or horizontal stack (`orientation`, `gap`). |
|
|
550
|
+
| `Flex` | Flexbox container (`direction`, `wrap`, `align`, `justify`, `gap`). |
|
|
462
551
|
|
|
463
552
|
### Feedback
|
|
464
553
|
|
|
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()
|
|
554
|
+
| Component | Description |
|
|
555
|
+
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
556
|
+
| `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)). |
|
|
557
|
+
| `ProgressBar` | Determinate or indeterminate progress; name it with `label` (inside a `Field`, the Field's label names it); `color`. |
|
|
558
|
+
| `Skeleton` | Loading placeholder (`width`, `height`, `shape`); `Skeleton.Group` marks the loading region busy. |
|
|
559
|
+
| `Spinner` | Loading indicator announced as "Loading" (localize with `label`); `appearance="inverted"` for brand surfaces; `delay`. |
|
|
560
|
+
| `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
561
|
|
|
473
562
|
### Navigation
|
|
474
563
|
|
|
@@ -499,9 +588,9 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
499
588
|
|
|
500
589
|
### Provider
|
|
501
590
|
|
|
502
|
-
| Component | Description
|
|
503
|
-
| -------------- |
|
|
504
|
-
| `WaveProvider` | Theme (`light`, `dark`, `high-contrast`), direction and
|
|
591
|
+
| Component | Description |
|
|
592
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
593
|
+
| `WaveProvider` | Theme (`light`, `dark`, `high-contrast`), direction, portal container and `inputDefaults` (the default `size` and `appearance` of the text inputs and pickers). |
|
|
505
594
|
|
|
506
595
|
`Portal` renders its children into `document.body` (or the provider's `portalContainer`) inside a themed `wave-portal` wrapper; use it for your own overlays.
|
|
507
596
|
|
|
@@ -609,17 +698,138 @@ export function AccountSettings({ backedUp }: { backedUp: number }) {
|
|
|
609
698
|
}
|
|
610
699
|
```
|
|
611
700
|
|
|
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.
|
|
701
|
+
- 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
702
|
- **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
703
|
- **`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
704
|
- **The hint** stays visible below the message, after it; the control is described by the message, then the hint.
|
|
616
|
-
- **`orientation="horizontal"`** puts the label in a start column (a third of the width) beside the control; the message and the hint stay below the control. The label's first line lines up with the control's first line: a Checkbox, Switch or RadioGroup as the first child (or a plain wrapper around them) gets
|
|
705
|
+
- **`orientation="horizontal"`** puts the label in a start column (a third of the width) beside the control; the message and the hint stay below the control. The label's first line lines up with the control's first line: a Checkbox, Switch or RadioGroup as the first child (or a plain wrapper around them) gets padding above and below its first row (6px at `medium`, 2px at `small`, 10px at `large`), as tall as an Input. The root carries `data-orientation`. The horizontal layout wraps the control, so changing `orientation` on a mounted Field remounts the control (an uncontrolled control loses its state and focus).
|
|
617
706
|
- **ProgressBar** reads the Field: the Field's label names a bar that has no name of its own, the message and the hint describe it, and the validation state (error, warning, success) colors its fill unless you set `color`. A progress bar is never invalid or required. Next to another control in a Field's wrapper element, the bar leaves the Field's label to that control (it gets an id of its own).
|
|
618
707
|
- **Choice labels.** Checkbox, Switch and `RadioGroup.Item` take any phrasing content as `label` (a link to the terms, a second line of subtext; no other form controls). It names the control; clicking its text toggles the control, and clicking a link inside it follows the link. The box, track or radio lines up with the first line of a label that wraps or has a second line (`className="items-center"` centres it instead). Arrow keys, Home and End pressed on a link inside a `RadioGroup.Item` label stay the link's. `children` are not rendered (a development warning says so). `labelPosition` puts the label `"after"` (default) or `"before"` the Checkbox and Switch, or `"above"` the Switch (settings lists). Checkbox and Switch take `disabledFocusable` (focusable, but not toggled and not submitted) and route your `aria-disabled` to the control.
|
|
619
708
|
- 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.
|
|
709
|
+
- **Field `size`** (`'small'`, `'medium'`, `'large'`) sets the label's size and the default `size` of the text inputs and pickers inside it (see [Sizes and appearances](#sizes-and-appearances)). The root renders `data-size`.
|
|
710
|
+
- **Label elements.** `label` also takes a `Label` or an `InfoLabel` element, which Field renders in place of its own `<label>`. Field points it at the control (`htmlFor`) and gives it the Field's label id, `required`, label `size` and `weight="semibold"`, each only where the element leaves it unset; the element shows its own required indicator, so Field adds no asterisk. The control is named by the label text alone, since an InfoLabel's info button sits outside the `<label>`; a `RadioGroup` is named through `aria-labelledby`. Only the element itself counts, not one inside a Fragment or another element.
|
|
711
|
+
|
|
712
|
+
```tsx
|
|
713
|
+
import { Field, InfoLabel, Input, Label } from '@mortenbrudvik/waveui';
|
|
714
|
+
|
|
715
|
+
export function Credentials() {
|
|
716
|
+
return (
|
|
717
|
+
<>
|
|
718
|
+
<Field label={<InfoLabel info="Use at least 12 characters.">Password</InfoLabel>} required>
|
|
719
|
+
<Input type="password" />
|
|
720
|
+
</Field>
|
|
721
|
+
<Field label={<Label required="(required)">Email</Label>}>
|
|
722
|
+
<Input type="email" />
|
|
723
|
+
</Field>
|
|
724
|
+
</>
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
620
729
|
- 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
730
|
- `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.
|
|
731
|
+
- 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.
|
|
732
|
+
|
|
733
|
+
### Sizes and appearances
|
|
734
|
+
|
|
735
|
+
The text inputs and pickers (`Input`, `Textarea`, `Select`, `SearchBox`, `SpinButton`, `Combobox`, `Dropdown`, `DatePicker`, `TimePicker` and `TagPicker`) take `size` and `appearance`; `Checkbox`, `Switch` and `Slider` take a `size` of their own.
|
|
736
|
+
|
|
737
|
+
| Prop | Values | Default |
|
|
738
|
+
| ------------ | ----------------------------------------------------------------------------------------- | --------- |
|
|
739
|
+
| `size` | `'small'` (24px tall), `'medium'` (32px), `'large'` (40px); type `CoreSize` | `medium` |
|
|
740
|
+
| `appearance` | `'outline'`, `'underline'`, `'filled-darker'`, `'filled-lighter'`; type `InputAppearance` | `outline` |
|
|
741
|
+
|
|
742
|
+
`medium` and `outline` are the 0.7 look. `Textarea` sets minimum heights (64, 80 and 96px) instead of a height. `TagPicker` has no `small` (`TagPickerSize`: `'medium'`, `'large'`, `'extra-large'`). The choice controls have their own unions: `Checkbox` `size` `'medium'` (an 18px box) or `'large'` (22px) and `shape` `'square'` or `'circular'`; `Switch` `size` `'small'` (a 32×16px track) or `'medium'` (40×20px); `Slider` `size` `'small'` (a 16px thumb on a 2px rail) or `'medium'` (a 20px thumb on a 4px rail). The icon buttons of the pickers (clear, expand, calendar) scale with `size` and keep a 24×24px target at every size; the step buttons of `SpinButton` are 24, 32 and 40px wide.
|
|
743
|
+
|
|
744
|
+
```tsx
|
|
745
|
+
import { Field, Input, WaveProvider } from '@mortenbrudvik/waveui';
|
|
746
|
+
|
|
747
|
+
export function CompactForm() {
|
|
748
|
+
return (
|
|
749
|
+
// Small filled fields below this provider, unless a Field or a control says otherwise.
|
|
750
|
+
<WaveProvider inputDefaults={{ size: 'small', appearance: 'filled-darker' }}>
|
|
751
|
+
<Field label="Name">
|
|
752
|
+
<Input />
|
|
753
|
+
</Field>
|
|
754
|
+
<Field label="Email" size="large">
|
|
755
|
+
<Input type="email" appearance="outline" />
|
|
756
|
+
</Field>
|
|
757
|
+
</WaveProvider>
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
- **Resolution order.** `size`: the control's own prop, then the surrounding `Field`'s `size`, then `WaveProvider` `inputDefaults.size`, then `medium`. `appearance`: the control's own prop, then `inputDefaults.appearance`, then `outline`. A Field or provider size that the control does not offer is skipped: a `TagPicker` in a `small` Field is `medium`. `Checkbox`, `Switch` and `Slider` read neither the Field's `size` nor `inputDefaults`.
|
|
763
|
+
- **`inputDefaults`.** `WaveProvider` takes `{ size?, appearance? }` (`InputDefaults`). A nested provider merges its own keys over the enclosing provider's, and `useWaveTheme()` returns the result.
|
|
764
|
+
- **Field `size`.** It sets the label's type ramp (`caption-1`, `body-1`, `body-2`) and, with `orientation="horizontal"`, the label's top padding, so its first line stays centred on a 24, 32 or 40px control. The validation message and the hint keep their size. Without a `size`, the label follows `inputDefaults.size`.
|
|
765
|
+
- **Filled appearances.** `filled-darker` and `filled-lighter` draw a fill from the `input-filled-*` [tokens](#color-tokens) and no visible stroke, so a filled field needs a visible label (a placeholder is not one). Put `filled-darker` on the page background and `filled-lighter` on a surface other than the page background (a card, a panel): `filled-lighter` is the page background color. Windows High Contrast (forced colors) and the high-contrast theme draw a boundary on every appearance. `underline` has only a bottom stroke.
|
|
766
|
+
- **The focus and error looks** are the same in every appearance: the bottom border is 2px in the focus color while the field has focus, and an invalid field keeps its other borders red, also while it is focused (`underline` shows the focus indicator alone).
|
|
767
|
+
- **`htmlSize`.** The native `size` attribute of `Input` (the width in characters) and `Select` (the number of visible rows) is `htmlSize`. A number as `size` still sets the attribute, but is deprecated: it warns once in development and is removed in 1.0. On `Slider` a numeric `size` has no effect and warns once.
|
|
768
|
+
- **Styling hooks.** Every one of these controls renders `data-size`, and the text inputs and pickers `data-appearance`, with the resolved value, on the element that receives your `className` (the `<input>`, `<textarea>` or `<select>`, or the wrapper for `Input` with `contentBefore`/`contentAfter` and for the composite controls). `Checkbox` also renders `data-shape`. Target them with `[data-appearance=underline]` in CSS or `data-[appearance=underline]:` in Tailwind.
|
|
769
|
+
|
|
770
|
+
### SpinButton
|
|
771
|
+
|
|
772
|
+
```tsx
|
|
773
|
+
import * as React from 'react';
|
|
774
|
+
import { SpinButton } from '@mortenbrudvik/waveui';
|
|
775
|
+
|
|
776
|
+
export function Prices() {
|
|
777
|
+
const [price, setPrice] = React.useState(12.5);
|
|
778
|
+
const [quantity, setQuantity] = React.useState<number | null>(null);
|
|
779
|
+
return (
|
|
780
|
+
<>
|
|
781
|
+
<SpinButton
|
|
782
|
+
aria-label="Price"
|
|
783
|
+
value={price}
|
|
784
|
+
onValueChange={setPrice}
|
|
785
|
+
step={0.5}
|
|
786
|
+
precision={2}
|
|
787
|
+
displayValue={`$${price.toFixed(2)}`}
|
|
788
|
+
className="w-full"
|
|
789
|
+
/>
|
|
790
|
+
<SpinButton
|
|
791
|
+
aria-label="Quantity"
|
|
792
|
+
allowEmpty
|
|
793
|
+
min={1}
|
|
794
|
+
value={quantity}
|
|
795
|
+
onValueChange={setQuantity}
|
|
796
|
+
/>
|
|
797
|
+
</>
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
- **`displayValue`** is the text shown for the value while the field is not being edited (`$12.50`), and its `aria-valuetext`. While the field has focus and can be edited it shows the plain number, so typing edits `12.5`, not `$12.50`. It applies only to a controlled `value`: an uncontrolled SpinButton ignores it and warns in development.
|
|
803
|
+
- **`precision`** (0 to 20) rounds every committed value, typed or stepped, to that many decimals. It does not pad: `precision={2}` shows `12.5`, so use `displayValue` for `12.50`.
|
|
804
|
+
- **`allowEmpty`** lets the value be `null`: clearing the text and committing it (blur or Enter) empties the field, `onValueChange` receives `null`, and `aria-valuenow` is omitted. The prop takes the literal `true`; a `boolean` variable fits neither prop type, so branch the JSX or spread `{ allowEmpty: true, value }`. The deprecated `onChange` is not offered in this mode. A `required` SpinButton (or in a required Field) blocks submitting an empty value. Stepping from an empty value starts at 0 and then clamps to `min`/`max`. Without `allowEmpty`, clearing the text still restores the last value.
|
|
805
|
+
- **Holding a step button** repeats the step after 300 ms, faster after that, with a mouse, a finger or a pen. A short press steps once, when it is released, so a finger that starts a scroll on a button changes nothing. The step buttons never take focus.
|
|
806
|
+
- **Width.** An unsized SpinButton is as wide as in 0.7 (114px at `medium`); a root you size (`className="w-full"`) widens the input. Its height follows `size` like the other fields.
|
|
807
|
+
|
|
808
|
+
### Rating
|
|
809
|
+
|
|
810
|
+
```tsx
|
|
811
|
+
import { Rating, RatingDisplay } from '@mortenbrudvik/waveui';
|
|
812
|
+
|
|
813
|
+
export function Reviews() {
|
|
814
|
+
return (
|
|
815
|
+
<>
|
|
816
|
+
<Rating aria-label="Your rating" step={0.5} color="brand" defaultValue={3.5} />
|
|
817
|
+
<RatingDisplay value={4.6} color="neutral" showValue count={1160} />
|
|
818
|
+
{/* One Rating.Item per value from 1 to max draws the stars yourself. */}
|
|
819
|
+
<Rating aria-label="Your mood" max={3} defaultValue={2}>
|
|
820
|
+
<Rating.Item value={1} />
|
|
821
|
+
<Rating.Item value={2} />
|
|
822
|
+
<Rating.Item value={3} iconFilled="♥" iconOutline="♡" />
|
|
823
|
+
</Rating>
|
|
824
|
+
</>
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
- **Half stars.** With `step={0.5}` each star is still one pointer target (24×24px or larger): a click or tap chooses the half or the whole star by the pointer's position over it (the start half mirrors in RTL). Two visually hidden radios per star serve the keyboard and screen readers, named "2.5 stars" and "3 stars" (`labels.star`). The arrow keys move by half a star. With a `name`, the hidden input submits `2.5`. A controlled value between two steps (2.7) checks no radio and is drawn as the step below it; the arrow keys move it to the next or previous step.
|
|
830
|
+
- **`color`** colors the filled stars: `'marigold'` (the default), `'brand'` or `'neutral'` (`RatingColor`); unfilled stars keep a 3:1 outline in every color. The root renders `data-color`. On `Rating` and `RatingDisplay` this narrows the `color` string that `React.HTMLAttributes` declares.
|
|
831
|
+
- **Icons.** `iconFilled` and `iconOutline` replace the star glyphs, as a pair (a filled and an unfilled star must differ by shape): set only one and a development warning asks for the other. They are decorative. A partly filled star in `RatingDisplay` clips the filled glyph over the outline glyph.
|
|
832
|
+
- **`Rating.Item`** (`RatingDisplay.Item`, `RatingItem`) renders one star from its root's context. When `children` render content, they replace the generated stars: pass one item per value from 1 to `max`. An item's own icon pair wins over its root's. A duplicated value, a value that is not a whole number from 1 to `max` and a missing value each warn once in development. A `compact` RatingDisplay ignores its children.
|
|
623
833
|
|
|
624
834
|
### Value callbacks
|
|
625
835
|
|
|
@@ -631,8 +841,9 @@ export function AccountSettings({ backedUp }: { backedUp: number }) {
|
|
|
631
841
|
| checked | `checked` / `defaultChecked` / `onCheckedChange(checked)` |
|
|
632
842
|
| open | `open` / `defaultOpen` / `onOpenChange(open)` |
|
|
633
843
|
| checked values (Menu, Toolbar) | `checkedValues` / `defaultCheckedValues` / `onCheckedValuesChange(checkedValues, details)` |
|
|
844
|
+
| checked items (Tree) | `checkedItems` / `defaultCheckedItems` / `onCheckedItemsChange(checkedItems, details)` |
|
|
634
845
|
|
|
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)),
|
|
846
|
+
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
847
|
|
|
637
848
|
### Composite controls
|
|
638
849
|
|
|
@@ -640,6 +851,36 @@ Checkbox, Switch, SearchBox, SpinButton, Combobox, Dropdown, TagPicker, DatePick
|
|
|
640
851
|
|
|
641
852
|
`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
853
|
|
|
854
|
+
`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.
|
|
855
|
+
|
|
856
|
+
### Color
|
|
857
|
+
|
|
858
|
+
```tsx
|
|
859
|
+
import * as React from 'react';
|
|
860
|
+
import { AlphaSlider, ColorArea, ColorPicker, ColorSlider, Field } from '@mortenbrudvik/waveui';
|
|
861
|
+
|
|
862
|
+
export function AccentPicker() {
|
|
863
|
+
const [accent, setAccent] = React.useState('#0f6cbd');
|
|
864
|
+
return (
|
|
865
|
+
<Field label="Accent color">
|
|
866
|
+
<ColorPicker value={accent} onValueChange={setAccent} name="accent">
|
|
867
|
+
<ColorArea />
|
|
868
|
+
<ColorSlider />
|
|
869
|
+
<AlphaSlider />
|
|
870
|
+
<ColorPicker.HexField />
|
|
871
|
+
<ColorPicker.Swatches />
|
|
872
|
+
</ColorPicker>
|
|
873
|
+
</Field>
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
- **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.
|
|
879
|
+
- **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.
|
|
880
|
+
- **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.
|
|
881
|
+
- **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.
|
|
882
|
+
- `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").
|
|
883
|
+
|
|
643
884
|
### Menus
|
|
644
885
|
|
|
645
886
|
```tsx
|
|
@@ -864,6 +1105,7 @@ export function DeleteDialog({ onDelete }: { onDelete: () => void }) {
|
|
|
864
1105
|
- **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
1106
|
- 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
1107
|
- 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.
|
|
1108
|
+
- **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
1109
|
|
|
868
1110
|
```tsx
|
|
869
1111
|
import * as React from 'react';
|
|
@@ -977,41 +1219,119 @@ export function SavedNotice() {
|
|
|
977
1219
|
- **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
1220
|
- **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
1221
|
- **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`
|
|
1222
|
+
- `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
1223
|
|
|
982
1224
|
### Toasts
|
|
983
1225
|
|
|
984
|
-
Wrap the app (or the part that shows toasts) in `<Toaster>`; `useToastController()` throws in development outside it.
|
|
1226
|
+
Wrap the app (or the part that shows toasts) in `<Toaster>`; `useToastController()` throws in development outside it. Dispatch a `<Toast>` with its parts:
|
|
985
1227
|
|
|
986
1228
|
```tsx
|
|
987
|
-
import { Button, Toaster, useToastController } from '@mortenbrudvik/waveui';
|
|
1229
|
+
import { Button, Toast, Toaster, useToastController } from '@mortenbrudvik/waveui';
|
|
988
1230
|
|
|
989
|
-
function
|
|
1231
|
+
function SendButton({ onUndo }: { onUndo: () => void }) {
|
|
990
1232
|
const { dispatchToast } = useToastController();
|
|
991
1233
|
return (
|
|
992
1234
|
<Button
|
|
993
1235
|
onClick={() =>
|
|
994
|
-
dispatchToast(
|
|
1236
|
+
dispatchToast(
|
|
1237
|
+
<Toast status="success">
|
|
1238
|
+
<Toast.Title
|
|
1239
|
+
action={
|
|
1240
|
+
<Toast.Close>
|
|
1241
|
+
<Button size="small" appearance="transparent" onClick={onUndo}>
|
|
1242
|
+
Undo
|
|
1243
|
+
</Button>
|
|
1244
|
+
</Toast.Close>
|
|
1245
|
+
}
|
|
1246
|
+
>
|
|
1247
|
+
Email sent
|
|
1248
|
+
</Toast.Title>
|
|
1249
|
+
<Toast.Body subtitle="To 3 people">Your message was delivered.</Toast.Body>
|
|
1250
|
+
</Toast>,
|
|
1251
|
+
)
|
|
995
1252
|
}
|
|
996
1253
|
>
|
|
997
|
-
|
|
1254
|
+
Send
|
|
998
1255
|
</Button>
|
|
999
1256
|
);
|
|
1000
1257
|
}
|
|
1001
1258
|
|
|
1002
|
-
export function App() {
|
|
1259
|
+
export function App({ undoSend }: { undoSend: () => void }) {
|
|
1003
1260
|
return (
|
|
1004
|
-
<Toaster
|
|
1005
|
-
|
|
1261
|
+
<Toaster
|
|
1262
|
+
position="bottom-end"
|
|
1263
|
+
shortcuts={{ focus: (event) => event.altKey && event.shiftKey && event.code === 'KeyN' }}
|
|
1264
|
+
>
|
|
1265
|
+
<SendButton onUndo={undoSend} />
|
|
1006
1266
|
</Toaster>
|
|
1007
1267
|
);
|
|
1008
1268
|
}
|
|
1009
1269
|
```
|
|
1010
1270
|
|
|
1011
|
-
`dispatchToast` returns the toast id; `dismissToast(id)` removes
|
|
1271
|
+
`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.
|
|
1012
1272
|
|
|
1013
|
-
-
|
|
1014
|
-
- **`
|
|
1273
|
+
- **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`.
|
|
1274
|
+
- **`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.
|
|
1275
|
+
- **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.
|
|
1276
|
+
- **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.
|
|
1277
|
+
- **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.
|
|
1278
|
+
- **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.
|
|
1279
|
+
- 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.
|
|
1280
|
+
- **`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.
|
|
1281
|
+
- **`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.
|
|
1282
|
+
- **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.
|
|
1283
|
+
|
|
1284
|
+
**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:
|
|
1285
|
+
|
|
1286
|
+
```tsx
|
|
1287
|
+
import * as React from 'react';
|
|
1288
|
+
import { Toast, Toaster, createToaster } from '@mortenbrudvik/waveui';
|
|
1289
|
+
|
|
1290
|
+
/** Module scope is safe: on the server a dispatch does nothing. */
|
|
1291
|
+
export const toaster = createToaster();
|
|
1292
|
+
|
|
1293
|
+
/** Callable from anywhere: a query cache's onError, an event handler, a helper. */
|
|
1294
|
+
export function notifyUploadFailed(message: string) {
|
|
1295
|
+
return toaster.dispatchToast(
|
|
1296
|
+
<Toast status="error">
|
|
1297
|
+
<Toast.Title>Upload failed</Toast.Title>
|
|
1298
|
+
<Toast.Body>{message}</Toast.Body>
|
|
1299
|
+
</Toast>,
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
export function Notifications({ children }: { children: React.ReactNode }) {
|
|
1304
|
+
return <Toaster toaster={toaster}>{children}</Toaster>;
|
|
1305
|
+
}
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
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.
|
|
1309
|
+
|
|
1310
|
+
### Message bars
|
|
1311
|
+
|
|
1312
|
+
```tsx
|
|
1313
|
+
import { Button, MessageBar } from '@mortenbrudvik/waveui';
|
|
1314
|
+
|
|
1315
|
+
export function UpdateNotice({ onUpdate, onLater }: { onUpdate: () => void; onLater: () => void }) {
|
|
1316
|
+
return (
|
|
1317
|
+
<MessageBar status="warning" onDismiss={onLater}>
|
|
1318
|
+
<MessageBar.Title>Update available</MessageBar.Title>A new version is ready to install.
|
|
1319
|
+
<MessageBar.Actions>
|
|
1320
|
+
<Button size="small" onClick={onUpdate}>
|
|
1321
|
+
Update now
|
|
1322
|
+
</Button>
|
|
1323
|
+
<Button size="small" appearance="subtle" onClick={onLater}>
|
|
1324
|
+
Later
|
|
1325
|
+
</Button>
|
|
1326
|
+
</MessageBar.Actions>
|
|
1327
|
+
</MessageBar>
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
- **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.
|
|
1333
|
+
- **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`).
|
|
1334
|
+
- **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
1335
|
|
|
1016
1336
|
### Badges
|
|
1017
1337
|
|
|
@@ -1131,6 +1451,52 @@ export function People({ people }: { people: Person[] }) {
|
|
|
1131
1451
|
|
|
1132
1452
|
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
1453
|
|
|
1454
|
+
### Trees
|
|
1455
|
+
|
|
1456
|
+
```tsx
|
|
1457
|
+
import * as React from 'react';
|
|
1458
|
+
import { Button, Tree } from '@mortenbrudvik/waveui';
|
|
1459
|
+
|
|
1460
|
+
export function FileTree() {
|
|
1461
|
+
const [checked, setChecked] = React.useState<string[]>(['docs']);
|
|
1462
|
+
return (
|
|
1463
|
+
<Tree
|
|
1464
|
+
aria-label="Files"
|
|
1465
|
+
selectionMode="multiple"
|
|
1466
|
+
checkedItems={checked}
|
|
1467
|
+
onCheckedItemsChange={setChecked}
|
|
1468
|
+
>
|
|
1469
|
+
<Tree.Item value="docs" aside="3 files">
|
|
1470
|
+
Documents
|
|
1471
|
+
<Tree.Item value="cv">CV.pdf</Tree.Item>
|
|
1472
|
+
<Tree.Item value="letter">Letter.docx</Tree.Item>
|
|
1473
|
+
<Tree.Item value="notes">Notes.txt</Tree.Item>
|
|
1474
|
+
</Tree.Item>
|
|
1475
|
+
<Tree.Item
|
|
1476
|
+
value="photos"
|
|
1477
|
+
actions={
|
|
1478
|
+
<Button size="small" appearance="subtle">
|
|
1479
|
+
Share
|
|
1480
|
+
</Button>
|
|
1481
|
+
}
|
|
1482
|
+
>
|
|
1483
|
+
Photos
|
|
1484
|
+
<Tree.Item value="beach">Beach.jpg</Tree.Item>
|
|
1485
|
+
</Tree.Item>
|
|
1486
|
+
</Tree>
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
- **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`.
|
|
1492
|
+
- **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).
|
|
1493
|
+
- **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).
|
|
1494
|
+
- **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).
|
|
1495
|
+
- **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.
|
|
1496
|
+
- **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).
|
|
1497
|
+
- **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.
|
|
1498
|
+
- **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.
|
|
1499
|
+
|
|
1134
1500
|
### Slots
|
|
1135
1501
|
|
|
1136
1502
|
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,67 +1519,74 @@ Icon slots are decorative (`aria-hidden`). Content that renders nothing (`''`, `
|
|
|
1153
1519
|
|
|
1154
1520
|
Slots that replace a built-in glyph follow one rule per kind:
|
|
1155
1521
|
|
|
1156
|
-
- **Optional indicators** (`MenuButton.menuIcon`, the `expandIcon` of Combobox and
|
|
1522
|
+
- **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
1523
|
- **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.
|
|
1524
|
+
- **Status icons** (`MessageBar.icon`, `Toast.icon`, Field `validationMessageIcon`): only `undefined` keeps the default icon; `null` or a value that renders nothing shows none.
|
|
1159
1525
|
- 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
1526
|
|
|
1161
1527
|
### Built-in text
|
|
1162
1528
|
|
|
1163
1529
|
The names and hidden texts that components render themselves are English by default. These props translate them:
|
|
1164
1530
|
|
|
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
|
-
| `
|
|
1531
|
+
| Component | Props |
|
|
1532
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1533
|
+
| `Dialog.Content`, `Drawer` | `closeLabel` (the Close button) |
|
|
1534
|
+
| `TeachingPopover` | `closeLabel` (the Close button), `labels` (`back`, `next`, `done`, `step(index, count)` with a zero-based index) |
|
|
1535
|
+
| `MessageBar`, `Toast` | `statusLabel` (the hidden severity text; also an option of both `dispatchToast` forms); `Toast` also `dismissLabel` (also an option of the text form) |
|
|
1536
|
+
| `Tag` | `dismissLabel` |
|
|
1537
|
+
| `Carousel` | `labels` (`previous`, `next`, `picker`, `slide(index, total)` with a zero-based index, `carouselRoleDescription`, `slideRoleDescription`), `autoPlayLabels` |
|
|
1538
|
+
| `Stepper` | `statusLabels` (`completed`, `error`; `''` omits the text) |
|
|
1539
|
+
| `Pagination` | `getItemAriaLabel(type, page, selected)` |
|
|
1540
|
+
| `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) |
|
|
1541
|
+
| `ColorArea` | `labels` (`saturation`, `brightness`, `roleDescription` ("2D slider"), `colorName(color)`, `valueText(color, channel)`) |
|
|
1542
|
+
| `ColorSlider` | `labels` (`hue`, `saturation`, `brightness` (the `value` channel), `colorName(color)`, `valueText(color, channel)`) |
|
|
1543
|
+
| `AlphaSlider` | `labels` (`opacity`, `transparency`, `valueText(color, transparency)`) |
|
|
1544
|
+
| `Combobox` | `labels` (`noMatches`, `clear` (the clear button), `expand` (the chevron button)) |
|
|
1545
|
+
| `Dropdown` | `labels` (`clear`, the clear button) |
|
|
1546
|
+
| `DatePicker` | `labels` (`clear`, `openCalendar`, `previousMonth`, `nextMonth`, and the error texts `invalidDate(pattern)`, `outOfRange(min, max)`, `unavailableDate`) |
|
|
1547
|
+
| `TimePicker` | `labels` (`clear`, `expand`, `list`, `noTimes`, `noMatches`, and the error texts `invalidTime(format)`, `outOfRange(min, max)`) |
|
|
1548
|
+
| `TagPicker` | `labels` (`remove(label)`, `selected`, `summary(labels)`, `added(label, count)`, `removed(label, count)`, `noMatches`) |
|
|
1549
|
+
| `Rating` | `labels` (`star(value, max)`; it also names the half values of a `step={0.5}` Rating, "2.5 stars") |
|
|
1550
|
+
| `RatingDisplay` | `labels` (`rating(value, max, formattedValue)`, `count(count, formattedCount)`); `locale` formats the value and the count (pass it when rendering on the server) |
|
|
1551
|
+
| `SpinButton` | `labels` (`increment`, `decrement`) |
|
|
1552
|
+
| `DataGrid` | `labels` (`selectAll`, `selectionHeader`); each row's fallback name "Select row": `DataGrid.Row` `selectionLabel` |
|
|
1553
|
+
| `AvatarGroup` | `overflowLabel`, `unnamedMemberLabel` |
|
|
1554
|
+
| `InfoButton` | `aria-label` (the button and its note; default "Information") |
|
|
1555
|
+
| `InfoLabel`, `SplitButton` | `infoButtonLabel` (InfoLabel's button is named "‹label› ‹infoButtonLabel›"), `menuButtonLabel` |
|
|
1556
|
+
| `Spinner`, `Skeleton.Group` | `label` |
|
|
1187
1557
|
|
|
1188
1558
|
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
1559
|
|
|
1190
|
-
0.7
|
|
1560
|
+
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
1561
|
|
|
1192
1562
|
## Keyboard support
|
|
1193
1563
|
|
|
1194
|
-
As implemented in 0.
|
|
1564
|
+
As implemented in 0.9. 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
1565
|
|
|
1196
1566
|
| Component | Keys |
|
|
1197
1567
|
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1198
1568
|
| Toolbar | One Tab stop (the last focused control; focusing a nested composite, such as a radio group or tab list, or a text field, select, slider or spin button keeps it where it was). Left/Right (Up/Down when vertical) move and wrap, Home/End jump; mirrored in RTL; natively disabled controls and controls hidden with CSS are skipped, while `disabledFocusable` controls stay reachable (a Tooltip on them opens on focus). Text fields, selects, sliders, spin buttons and editable comboboxes keep their own arrow keys; a Dropdown keeps Up/Down, Home and End for its list, but Left/Right move past it. A nested composite keeps its own Tab stop and arrow keys; a `Toolbar.RadioGroup` is none: its radios are part of the arrow order, and Up/Down (Left/Right when vertical, mirrored in RTL) also move among them, wrapping inside the group. The arrows never press a toggle or check a radio: Space and Enter do. |
|
|
1199
1569
|
| 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
|
-
| Rating | Right/Up one
|
|
1570
|
+
| Rating | Right/Up one step more (a star, or half a star with `step={0.5}`), Left/Down one fewer (never below `step`), Home/End `step`/`max`; mirrored in RTL. A pointer click on a half-star Rating chooses the half or the whole star by position; Space and Enter on a focused radio choose that radio's own value. |
|
|
1201
1571
|
| 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
|
|
1572
|
+
| 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
1573
|
| 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
1574
|
| 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
1575
|
| 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. |
|
|
1206
1576
|
| Combobox, TagPicker, TimePicker | Down/Up open and move, Alt+Down opens, typing makes the first match active (not in a `freeform` Combobox), Enter commits the active option (with no active option, Enter submits the form; TimePicker instead never submits while its text is edited: a complete time within the bounds is committed, erased text clears the value, other text is kept and flagged invalid, and the list closes; leaving the input with edited text settles it the same way and closes the list), Alt+Up and Tab close, Escape closes and then discards typed text. The chevron button (Combobox, TimePicker) is not a Tab stop; the clear button (`clearable`) is a Tab stop after the input. Tab from erased TimePicker text clears the value first, so focus moves past the clear button that goes with it. TagPicker: Backspace in the empty input focuses the last tag, Backspace/Delete removes it. |
|
|
1207
1577
|
| Dropdown | Down/Up/Home/End and typing open and move, PageUp/PageDown move by 10, Enter/Space commit (a Space within 500 ms of a letter continues the search), Alt+Up and Tab commit and close, Escape closes. The clear button (`clearable`) is a Tab stop after the combobox button. |
|
|
1208
|
-
| SpinButton | Up/Down step, PageUp/PageDown large step, Home/End jump to a finite min/max (otherwise they move the caret); Enter commits typed text, Escape reverts it.
|
|
1578
|
+
| SpinButton | Up/Down step, PageUp/PageDown large step, Home/End jump to a finite min/max (otherwise they move the caret; with Shift they select text); Enter commits typed text, Escape reverts it. With the pointer, holding a step button repeats the step; a short press steps once on release. |
|
|
1209
1579
|
| SearchBox | Escape clears the text and keeps focus (the key is consumed, so an enclosing Dialog, Drawer or Popover stays open); in an empty field Escape reaches the enclosing overlay. |
|
|
1210
1580
|
| DatePicker | Alt+Down opens the calendar. The clear button (`clearable`) is a Tab stop between the input and the calendar button; Tab from erased text clears the value first and moves on to the calendar button. Calendar: arrows by day/week (mirrored in RTL), PageUp/PageDown by month, Shift+PageUp/PageDown by year, Home/End week start/end, Enter/Space select, Escape closes; Tab stays inside. |
|
|
1211
1581
|
| Dialog, Drawer | Focus is trapped; Escape closes; focus returns to the first of these that can take focus: `finalFocusRef`, the element that had focus when the modal opened (a trigger's wrapper span resolves to the element inside it), the trigger, an element next to where that opener was. |
|
|
1212
1582
|
| Popover, TeachingPopover | Escape closes. When focus was inside (or lost to `<body>`), Popover returns it to its trigger, TeachingPopover to where focus was before it opened; a close by hover moves no focus. In the Tab order the content follows its trigger (TeachingPopover with `target`: the target): Tab from the trigger enters it, Tab past its end continues after the trigger, Shift+Tab from the element after the trigger enters it at its last element. For a trigger outside the tab order it follows the tab stop before the trigger; with no tab stop before the trigger or target, it is reached at the end of the page. A Tab lap visits it once: Tab from the last element of the page leaves the page, and Shift+Tab from outside the page reaches the page's last element. Focus leaving it does not close it. A context popover (`openOnContext`): Shift+F10 or the ContextMenu key on the region opens it and moves focus into the content, and Escape returns focus to the element that had it; opened by a right click, it is reached by Tab from that element. |
|
|
1213
|
-
| Tooltip, InfoLabel
|
|
1583
|
+
| Tooltip, InfoButton, InfoLabel | Open on keyboard focus (Tooltip after `openDelay`); Escape closes. InfoButton and InfoLabel: a click pins the note; focus stays on the button when the note opens, Tab from the button enters the note's first focusable element (a link) and Tab past its last element continues after the button; Shift+Tab returns; Escape closes it and returns focus to the button when it was in the note. `openOnHover={false}` opens it on a click (Enter, Space) only. |
|
|
1214
1584
|
| Card (selectable) | With the default `selectionControl="card"`: Enter (key down) or Space (key up) selects. With `"checkbox"`: the built-in checkbox. |
|
|
1215
1585
|
| Stepper | Each reachable step is a Tab stop; Enter/Space activate. |
|
|
1216
1586
|
| Slider | Native range keys. |
|
|
1587
|
+
| 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. |
|
|
1588
|
+
| 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. |
|
|
1589
|
+
| 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
1590
|
|
|
1218
1591
|
Accordion and Carousel have no arrow-key navigation: their triggers and controls are Tab stops.
|
|
1219
1592
|
|
|
@@ -1221,25 +1594,61 @@ Typeahead (Menu, selectable List, Tree, Dropdown) accepts characters typed with
|
|
|
1221
1594
|
|
|
1222
1595
|
## Hooks and utilities
|
|
1223
1596
|
|
|
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
|
|
1236
|
-
| `useToastController()` | `dispatchToast` / `dismissToast` / `dismissAllToasts` inside `<Toaster>`.
|
|
1237
|
-
| `
|
|
1238
|
-
| `
|
|
1239
|
-
| `
|
|
1240
|
-
| `
|
|
1241
|
-
| `
|
|
1242
|
-
| `
|
|
1597
|
+
| Export | Purpose |
|
|
1598
|
+
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1599
|
+
| `useControllable(value, defaultValue, onChange)` | Controlled/uncontrolled state; returns `[value, setValue, isControlled]`; `setValue` accepts a value or an updater and skips no-op updates. |
|
|
1600
|
+
| `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). |
|
|
1601
|
+
| `useId(prefix?)` | SSR-safe ids (`prefix-<react id>`; never parse the React part). |
|
|
1602
|
+
| `useEventCallback(fn)` | Stable callback that always calls the latest `fn`. |
|
|
1603
|
+
| `useMergedRefs(...refs)` | One stable callback ref for several refs. |
|
|
1604
|
+
| `useIsClient()` | `false` on the server and during hydration. |
|
|
1605
|
+
| `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)). |
|
|
1606
|
+
| `useFieldControl(props, options?)` | Merge a custom control's labelling props with the surrounding `Field` (its label, validation message, hint and state). |
|
|
1607
|
+
| `useAnnounce()`, `announce(message, politeness?)` | Screen-reader announcements through a shared live region. |
|
|
1608
|
+
| `useWaveTheme()` | Current theme, direction, portal container and `inputDefaults`. |
|
|
1609
|
+
| `useToastController()` | `dispatchToast` (content or options) / `dismissToast` / `dismissAllToasts` inside `<Toaster>`. |
|
|
1610
|
+
| `createToaster()` | A toast controller for code outside components; bind it with `<Toaster toaster={handle}>` (see [Toasts](#toasts)). Server-safe. |
|
|
1611
|
+
| `useIsOverflowing(target)`, `useOverflowMenu()`, `useIsOverflowItemVisible(id)` | Overflow detection. |
|
|
1612
|
+
| `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. |
|
|
1613
|
+
| `buttonClassNames`, `toastTitleClassNames`, … | The stable class names of every component and part (see [Styling hooks](#styling-hooks)). Server-safe. |
|
|
1614
|
+
| `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. |
|
|
1615
|
+
| `composeEventHandlers(theirs, ours)` | Run a consumer handler, then yours unless it called `preventDefault()`. |
|
|
1616
|
+
| `mergeRefs(...refs)` | Merge refs outside components. |
|
|
1617
|
+
| `resolveSlot`, `renderSlot` | The slot helpers the components use. |
|
|
1618
|
+
| `getThemeClassName(theme)` | The theme classes of a theme. |
|
|
1619
|
+
|
|
1620
|
+
## Upgrading from 0.8
|
|
1621
|
+
|
|
1622
|
+
0.9 removes nothing public; these changes can still affect your setup, tests and styles:
|
|
1623
|
+
|
|
1624
|
+
1. Text controls, pickers, choice controls, Field, Rating and RatingDisplay render new data attributes (`data-size`, `data-appearance`, `data-shape`, `data-color`): a test that asserts a control's exact attributes changes. Nothing changes visually at the defaults, except items 4 and 6.
|
|
1625
|
+
2. A numeric `size` on `Input` and `Select` is deprecated: use `htmlSize` (a number still sets the native attribute and warns once in development). On `Slider` a numeric `size` has no effect: it is no longer rendered and warns once.
|
|
1626
|
+
3. `InfoLabel` renders a real `<label>` (point it at a control with `htmlFor`), and its `id` moves from the root to that `<label>`. The label content moves to `children` (`label` still works and warns once), and the button is named "‹label› Information". The popup is a `role="note"` with a beak that can hold links, and it is no longer `aria-hidden`: Tab from the open button enters it. The label text takes the `Label` type ramp and color, so a `className` on the root that changes the text size or color no longer reaches it.
|
|
1627
|
+
4. `SpinButton` is 32px tall like the other fields (0.7: 34px). A root you size (`className="w-full"`) now widens the input; an unsized one keeps its width. A step button steps on click (not on the press) and repeats while it is held, and Shift+Home and Shift+End select text instead of jumping to the bounds.
|
|
1628
|
+
5. `Field` renders a `Label` or `InfoLabel` element passed as `label` itself (0.7 wrapped it in a second `<label>`).
|
|
1629
|
+
6. A focused invalid field draws its bottom border in the focus color; the other borders stay red (0.7: all red).
|
|
1630
|
+
7. `Rating` and `RatingDisplay` `color` is a `RatingColor` (`'neutral' | 'brand' | 'marigold'`): it narrows the `color?: string` inherited from `React.HTMLAttributes`. Both gain the static member `Item` and render their `children` (0.7 ignored them).
|
|
1631
|
+
8. `WaveContextValue` (the result of `useWaveTheme()`) gains `inputDefaults`: code that builds one (a test double) adds it. `InputProps`, `SelectProps` and `SliderProps` declare their own `size`, and `SpinButtonProps` gains `allowEmpty?: false` next to the new `SpinButtonAllowEmptyProps`.
|
|
1632
|
+
|
|
1633
|
+
The CHANGELOG's [0.9.0 "Upgrading from 0.8"](CHANGELOG.md#upgrading-from-08) and [0.9.0 "Changed" section](CHANGELOG.md#changed) list every behaviour, DOM, visual and type change.
|
|
1634
|
+
|
|
1635
|
+
## Upgrading from 0.7
|
|
1636
|
+
|
|
1637
|
+
0.8 removes nothing public; these changes can still affect your tests and styles:
|
|
1638
|
+
|
|
1639
|
+
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`.
|
|
1640
|
+
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`.
|
|
1641
|
+
3. Every component root and part carries a stable class (`wave-button`, `wave-button__icon`, …): assertions on a complete class string change.
|
|
1642
|
+
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.
|
|
1643
|
+
5. Escape inside the toast region dismisses the toasts and no longer closes a Dialog or Drawer below them.
|
|
1644
|
+
6. Every toast pauses while the pointer or focus is anywhere in the toast region (0.7 paused the hovered or focused toast only).
|
|
1645
|
+
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.
|
|
1646
|
+
8. MessageBar renders `data-layout`, and Tree `data-selection-mode`.
|
|
1647
|
+
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.
|
|
1648
|
+
10. `ToastController['dispatchToast']` is overloaded: a test double that implements it must accept content too.
|
|
1649
|
+
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 }`).
|
|
1650
|
+
|
|
1651
|
+
The CHANGELOG's [0.8.0 "Changed" section](CHANGELOG.md#changed-1) lists every behaviour, DOM and type change.
|
|
1243
1652
|
|
|
1244
1653
|
## Upgrading from 0.6
|
|
1245
1654
|
|
|
@@ -1255,7 +1664,7 @@ Typeahead (Menu, selectable List, Tree, Dropdown) accepts characters typed with
|
|
|
1255
1664
|
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
1665
|
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
1666
|
|
|
1258
|
-
The CHANGELOG's [0.7.0 "Changed" section](CHANGELOG.md#changed) lists every behaviour, DOM and type change.
|
|
1667
|
+
The CHANGELOG's [0.7.0 "Changed" section](CHANGELOG.md#changed-2) lists every behaviour, DOM and type change.
|
|
1259
1668
|
|
|
1260
1669
|
## Upgrading from 0.5
|
|
1261
1670
|
|
|
@@ -1270,11 +1679,11 @@ The CHANGELOG's [0.7.0 "Changed" section](CHANGELOG.md#changed) lists every beha
|
|
|
1270
1679
|
7. RatingDisplay formats its accessible name with the runtime locale when `locale` is omitted: pass `locale` when rendering on the server.
|
|
1271
1680
|
8. Badge `color="important"` becomes a neutral color in 1.0: switch to `color="severe"` to keep the orange look.
|
|
1272
1681
|
|
|
1273
|
-
The CHANGELOG's [0.6.0 "Changed" section](CHANGELOG.md#changed-
|
|
1682
|
+
The CHANGELOG's [0.6.0 "Changed" section](CHANGELOG.md#changed-3) lists every behaviour, DOM and type change.
|
|
1274
1683
|
|
|
1275
1684
|
## Upgrading from 0.4
|
|
1276
1685
|
|
|
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)
|
|
1686
|
+
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), [Upgrading from 0.7](#upgrading-from-07) and, for 0.9, [Upgrading from 0.8](#upgrading-from-08).
|
|
1278
1687
|
|
|
1279
1688
|
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
1689
|
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.
|