@primitiv-ui/react 0.1.27 → 0.1.29
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 +4 -0
- package/package.json +6 -1
- package/src/AccessibleIcon/AccessibleIcon.tsx +32 -9
- package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +5 -0
- package/src/AccessibleIcon/types.ts +15 -2
- package/src/Accordion/Accordion.tsx +25 -4
- package/src/Accordion/__tests__/Accordion.asChild.test.tsx +46 -0
- package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +15 -0
- package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +42 -0
- package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +33 -0
- package/src/Accordion/hooks/useAccordionRoot.ts +44 -7
- package/src/Accordion/hooks/useAccordionTrigger.ts +9 -1
- package/src/Accordion/types.ts +72 -21
- package/src/Alert/Alert.tsx +36 -7
- package/src/Alert/__tests__/Alert.test.tsx +5 -0
- package/src/Alert/types.ts +18 -2
- package/src/Avatar/Avatar.tsx +99 -15
- package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +10 -0
- package/src/Avatar/__tests__/Avatar.fallback.test.tsx +30 -0
- package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +35 -0
- package/src/Avatar/hooks/useAvatarImage.ts +16 -0
- package/src/Avatar/types.ts +15 -3
- package/src/Breadcrumb/Breadcrumb.tsx +136 -18
- package/src/Breadcrumb/README.md +47 -0
- package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +11 -0
- package/src/Breadcrumb/__tests__/Breadcrumb.ellipsis.test.tsx +110 -0
- package/src/Breadcrumb/types.ts +73 -7
- package/src/Button/Button.tsx +2 -0
- package/src/Button/__tests__/Button.basic-rendering.test.tsx +6 -0
- package/src/Button/types.ts +21 -0
- package/src/Carousel/Carousel.tsx +263 -15
- package/src/Carousel/README.md +749 -72
- package/src/Carousel/__tests__/Carousel.autoplay-status.test.tsx +239 -0
- package/src/Carousel/__tests__/Carousel.drag-status.test.tsx +292 -0
- package/src/Carousel/__tests__/Carousel.dynamic-slides.test.tsx +38 -0
- package/src/Carousel/__tests__/Carousel.indicators.test.tsx +101 -0
- package/src/Carousel/__tests__/Carousel.infinite.test.tsx +1453 -0
- package/src/Carousel/__tests__/Carousel.instant-scroll.test.tsx +124 -0
- package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +88 -0
- package/src/Carousel/__tests__/Carousel.loop.test.tsx +163 -0
- package/src/Carousel/__tests__/Carousel.loopEngine.test.ts +77 -0
- package/src/Carousel/__tests__/Carousel.mouse-drag.test.tsx +504 -0
- package/src/Carousel/__tests__/Carousel.multi-slide-bounds.test.tsx +91 -0
- package/src/Carousel/__tests__/Carousel.orientation.test.tsx +207 -0
- package/src/Carousel/__tests__/Carousel.overscroll.test.tsx +532 -0
- package/src/Carousel/__tests__/Carousel.page-snap-points.test.tsx +50 -0
- package/src/Carousel/__tests__/Carousel.progress-text.test.tsx +76 -0
- package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +3 -6
- package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +2 -5
- package/src/Carousel/__tests__/Carousel.scroll-margin.test.tsx +167 -0
- package/src/Carousel/__tests__/Carousel.scroll-progress.test.tsx +518 -0
- package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +32 -11
- package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +237 -24
- package/src/Carousel/__tests__/Carousel.scroll-to-index.test.tsx +115 -0
- package/src/Carousel/__tests__/Carousel.slide-snap.test.tsx +172 -0
- package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +179 -1
- package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +91 -7
- package/src/Carousel/__tests__/Carousel.snap-type.test.tsx +35 -0
- package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +30 -5
- package/src/Carousel/__tests__/Carousel.translations.test.tsx +23 -0
- package/src/Carousel/__tests__/Carousel.wheel.test.tsx +160 -0
- package/src/Carousel/hooks/index.ts +1 -0
- package/src/Carousel/hooks/useCarouselLoop.ts +869 -0
- package/src/Carousel/hooks/useCarouselRoot.ts +338 -42
- package/src/Carousel/hooks/useCarouselSlide.ts +44 -9
- package/src/Carousel/hooks/useCarouselViewport.ts +649 -46
- package/src/Carousel/loopEngine.ts +54 -0
- package/src/Carousel/types.ts +457 -35
- package/src/Checkbox/Checkbox.tsx +9 -1
- package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +14 -0
- package/src/Checkbox/hooks/useCheckboxInput.ts +4 -0
- package/src/Checkbox/types.ts +19 -0
- package/src/CheckboxCard/CheckboxCard.tsx +13 -4
- package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +7 -0
- package/src/CheckboxCard/types.ts +65 -17
- package/src/Collapsible/Collapsible.tsx +49 -3
- package/src/Collapsible/README.md +40 -0
- package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +89 -0
- package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +27 -0
- package/src/Collapsible/__tests__/Collapsible.collapsedHeight.test.tsx +140 -0
- package/src/Collapsible/hooks/useCollapsibleTrigger.ts +3 -1
- package/src/Collapsible/types.ts +89 -22
- package/src/ContextMenu/ContextMenu.tsx +270 -4
- package/src/ContextMenu/types.ts +148 -6
- package/src/DirectionProvider/DirectionProvider.tsx +41 -9
- package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +5 -0
- package/src/DirectionProvider/types.ts +28 -3
- package/src/Divider/Divider.tsx +24 -10
- package/src/Divider/__tests__/Divider.test.tsx +5 -0
- package/src/Divider/types.ts +10 -3
- package/src/Drawer/Drawer.tsx +315 -0
- package/src/Drawer/README.md +124 -0
- package/src/Drawer/__tests__/Drawer.behavior.test.tsx +100 -0
- package/src/Drawer/__tests__/Drawer.focus-trap.test.tsx +55 -0
- package/src/Drawer/__tests__/Drawer.rendering.test.tsx +111 -0
- package/src/Drawer/__tests__/Drawer.side.test.tsx +69 -0
- package/src/Drawer/__tests__/dialog-polyfill.ts +39 -0
- package/src/Drawer/index.ts +2 -0
- package/src/Drawer/types.ts +66 -0
- package/src/Dropdown/Dropdown.tsx +117 -0
- package/src/Dropdown/types.ts +241 -11
- package/src/EmptyState/EmptyState.tsx +74 -4
- package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +7 -2
- package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +3 -1
- package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +10 -0
- package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +3 -1
- package/src/EmptyState/types.ts +49 -6
- package/src/Field/Field.tsx +23 -0
- package/src/Field/__tests__/Field.basic-rendering.test.tsx +11 -0
- package/src/Field/__tests__/Field.error-handling.test.tsx +23 -0
- package/src/Field/__tests__/Field.state-cascade.test.tsx +21 -0
- package/src/Field/hooks/__tests__/useFieldProps.test.tsx +104 -9
- package/src/Field/types.ts +60 -12
- package/src/Fieldset/Fieldset.tsx +54 -18
- package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +9 -0
- package/src/Fieldset/types.ts +20 -2
- package/src/Input/Input.tsx +14 -9
- package/src/Input/__tests__/Input.basic-rendering.test.tsx +5 -0
- package/src/Input/types.ts +19 -2
- package/src/InputGroup/InputGroup.tsx +11 -3
- package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +14 -0
- package/src/InputGroup/types.ts +26 -5
- package/src/MillerColumns/MillerColumns.tsx +86 -19
- package/src/MillerColumns/types.ts +83 -27
- package/src/Modal/Modal.tsx +31 -4
- package/src/Modal/README.md +19 -10
- package/src/Modal/__tests__/Modal.focus-trap.test.tsx +162 -0
- package/src/Modal/hooks/useModalContent.ts +32 -0
- package/src/Modal/types.ts +110 -29
- package/src/NavigationMenu/NavigationMenu.tsx +890 -0
- package/src/NavigationMenu/NavigationMenuContext.ts +67 -0
- package/src/NavigationMenu/README.md +355 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.anchor-positioning.test.tsx +132 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.asChild.test.tsx +73 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.basic-rendering.test.tsx +92 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.contract.test.tsx +45 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.controlled-state.test.tsx +134 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.error-handling.test.tsx +62 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.fixtures.tsx +79 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.forceMount.test.tsx +52 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.hover-interaction.test.tsx +254 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.indicator.test.tsx +252 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-interaction.test.tsx +294 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-navigation.test.tsx +126 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.link.test.tsx +139 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.motion.test.tsx +568 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.mouse-interaction.test.tsx +76 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.uncontrolled-state.test.tsx +30 -0
- package/src/NavigationMenu/__tests__/NavigationMenu.viewport.test.tsx +113 -0
- package/src/NavigationMenu/constants.ts +5 -0
- package/src/NavigationMenu/hooks/index.ts +7 -0
- package/src/NavigationMenu/hooks/useNavigationMenuEntry.ts +33 -0
- package/src/NavigationMenu/hooks/useNavigationMenuIndicator.ts +70 -0
- package/src/NavigationMenu/hooks/useNavigationMenuLink.ts +72 -0
- package/src/NavigationMenu/hooks/useNavigationMenuRoot.ts +301 -0
- package/src/NavigationMenu/hooks/useNavigationMenuTopLevelEntry.ts +65 -0
- package/src/NavigationMenu/hooks/useNavigationMenuTrigger.ts +158 -0
- package/src/NavigationMenu/hooks/useNavigationMenuViewport.ts +101 -0
- package/src/NavigationMenu/index.ts +3 -0
- package/src/NavigationMenu/types.ts +259 -0
- package/src/NavigationMenu/utils.ts +89 -0
- package/src/Popover/Popover.tsx +427 -0
- package/src/Popover/PopoverContext.ts +13 -0
- package/src/Popover/README.md +106 -0
- package/src/Popover/__tests__/Popover.anchor.test.tsx +38 -0
- package/src/Popover/__tests__/Popover.asChild.test.tsx +160 -0
- package/src/Popover/__tests__/Popover.basic-rendering.test.tsx +106 -0
- package/src/Popover/__tests__/Popover.click-outside.test.tsx +149 -0
- package/src/Popover/__tests__/Popover.close.test.tsx +70 -0
- package/src/Popover/__tests__/Popover.content.test.tsx +58 -0
- package/src/Popover/__tests__/Popover.controlled-state.test.tsx +45 -0
- package/src/Popover/__tests__/Popover.error-handling.test.tsx +51 -0
- package/src/Popover/__tests__/Popover.focus.test.tsx +43 -0
- package/src/Popover/__tests__/Popover.keyboard-interaction.test.tsx +71 -0
- package/src/Popover/__tests__/Popover.mouse-interaction.test.tsx +69 -0
- package/src/Popover/__tests__/Popover.uncontrolled-state.test.tsx +44 -0
- package/src/Popover/hooks/index.ts +3 -0
- package/src/Popover/hooks/usePopoverContent.ts +98 -0
- package/src/Popover/hooks/usePopoverRoot.ts +84 -0
- package/src/Popover/hooks/usePopoverTrigger.ts +33 -0
- package/src/Popover/index.ts +2 -0
- package/src/Popover/types.ts +197 -0
- package/src/Portal/Portal.tsx +50 -10
- package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +5 -0
- package/src/Portal/types.ts +5 -0
- package/src/Progress/Progress.tsx +33 -1
- package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +10 -0
- package/src/Progress/__tests__/Progress.value.test.tsx +21 -0
- package/src/Progress/types.ts +20 -4
- package/src/Radio/Radio.tsx +9 -1
- package/src/Radio/__tests__/Radio.basic-rendering.test.tsx +14 -0
- package/src/Radio/hooks/useRadioRoot.ts +3 -0
- package/src/Radio/types.ts +20 -0
- package/src/RadioCard/RadioCard.tsx +11 -0
- package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +8 -0
- package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +27 -0
- package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +53 -0
- package/src/RadioCard/__tests__/RadioCard.ref-forwarding.test.tsx +54 -0
- package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +21 -0
- package/src/RadioCard/hooks/useRadioCardRoot.ts +11 -0
- package/src/RadioCard/types.ts +95 -22
- package/src/RadioGroup/RadioGroup.tsx +11 -0
- package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +8 -0
- package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +27 -0
- package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +29 -0
- package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +31 -0
- package/src/RadioGroup/hooks/useRadioGroupRoot.ts +11 -0
- package/src/RadioGroup/types.ts +73 -9
- package/src/SegmentedControl/README.md +154 -0
- package/src/SegmentedControl/SegmentedControl.tsx +309 -0
- package/src/SegmentedControl/SegmentedControlContext.ts +28 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.asChild.test.tsx +50 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.basic-rendering.test.tsx +100 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.controlled-state.test.tsx +107 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.disabled-keydown-guards.test.tsx +62 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.disabled.test.tsx +151 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.error-handling.test.tsx +17 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.keyboard-interaction.test.tsx +117 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.orientation.test.tsx +54 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.reading-direction.test.tsx +56 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.ref-forwarding.test.tsx +45 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.roving-tabindex.test.tsx +104 -0
- package/src/SegmentedControl/__tests__/SegmentedControl.uncontrolled-state.test.tsx +94 -0
- package/src/SegmentedControl/hooks/index.ts +2 -0
- package/src/SegmentedControl/hooks/useSegmentedControlContext.ts +1 -0
- package/src/SegmentedControl/hooks/useSegmentedControlRoot.ts +86 -0
- package/src/SegmentedControl/index.ts +2 -0
- package/src/SegmentedControl/types.ts +138 -0
- package/src/Select/README.md +122 -157
- package/src/Select/Select.tsx +595 -68
- package/src/Select/SelectContext.ts +38 -0
- package/src/Select/SelectItemIndicatorContext.ts +29 -0
- package/src/Select/__tests__/Select.asChild.test.tsx +2 -2
- package/src/Select/__tests__/Select.basic-rendering.test.tsx +22 -3
- package/src/Select/__tests__/Select.controlled-state.test.tsx +6 -6
- package/src/Select/__tests__/Select.data-attributes.test.tsx +4 -4
- package/src/Select/__tests__/Select.field-integration.test.tsx +11 -11
- package/src/Select/__tests__/Select.form.test.tsx +38 -0
- package/src/Select/__tests__/Select.group.test.tsx +6 -6
- package/src/Select/__tests__/Select.keyboard-edge.test.tsx +95 -0
- package/src/Select/__tests__/Select.keyboard.test.tsx +124 -0
- package/src/Select/__tests__/Select.mutation-hardening.test.tsx +317 -0
- package/src/Select/__tests__/Select.native-item.test.tsx +30 -0
- package/src/Select/__tests__/Select.open-close.test.tsx +57 -0
- package/src/Select/__tests__/Select.placeholder.test.tsx +4 -4
- package/src/Select/__tests__/Select.rich-asChild.test.tsx +60 -0
- package/src/Select/__tests__/Select.rich-mode.test.tsx +16 -0
- package/src/Select/__tests__/Select.selection.test.tsx +104 -0
- package/src/Select/__tests__/Select.separator.test.tsx +71 -0
- package/src/Select/__tests__/Select.strict-context.test.tsx +36 -0
- package/src/Select/__tests__/Select.typeahead.test.tsx +156 -0
- package/src/Select/__tests__/Select.value-mirror.test.tsx +240 -0
- package/src/Select/constants.ts +8 -0
- package/src/Select/hooks/index.ts +2 -0
- package/src/Select/hooks/useSelectContent.ts +149 -0
- package/src/Select/hooks/useSelectRoot.ts +149 -0
- package/src/Select/types.ts +199 -31
- package/src/SkipNav/SkipNav.tsx +69 -29
- package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +6 -0
- package/src/SkipNav/types.ts +17 -9
- package/src/Slider/Slider.tsx +33 -1
- package/src/Slider/SliderContext.ts +28 -1
- package/src/Slider/types.ts +157 -9
- package/src/Slot/Slot.tsx +94 -19
- package/src/Slot/__tests__/Slot.test.tsx +110 -0
- package/src/Slot/composeEventHandlers.ts +26 -2
- package/src/Slot/types.ts +16 -0
- package/src/Status/Status.tsx +36 -15
- package/src/Status/__tests__/Status.test.tsx +5 -0
- package/src/Status/types.ts +12 -1
- package/src/Switch/Switch.tsx +15 -2
- package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +7 -0
- package/src/Switch/hooks/useSwitchRoot.ts +3 -0
- package/src/Switch/types.ts +49 -22
- package/src/Table/Table.tsx +44 -10
- package/src/Table/types.ts +66 -17
- package/src/Tabs/Tabs.tsx +8 -0
- package/src/Tabs/types.ts +29 -4
- package/src/Textarea/Textarea.tsx +9 -1
- package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +5 -0
- package/src/Textarea/types.ts +20 -3
- package/src/Toggle/Toggle.tsx +51 -13
- package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +5 -0
- package/src/Toggle/types.ts +54 -4
- package/src/ToggleGroup/ToggleGroup.tsx +14 -1
- package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +20 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +9 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.dynamic-items.test.tsx +70 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.error-handling.test.tsx +23 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +23 -1
- package/src/ToggleGroup/__tests__/ToggleGroup.ref-forwarding.test.tsx +58 -0
- package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +21 -1
- package/src/ToggleGroup/types.ts +55 -2
- package/src/Tooltip/Tooltip.tsx +98 -20
- package/src/Tooltip/hooks/__tests__/useTooltipProvider.test.tsx +40 -0
- package/src/Tooltip/hooks/useTooltipProvider.ts +13 -1
- package/src/Tooltip/types.ts +89 -8
- package/src/Tree/Tree.tsx +187 -33
- package/src/Tree/types.ts +54 -12
- package/src/VisuallyHidden/VisuallyHidden.tsx +33 -8
- package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +15 -1
- package/src/VisuallyHidden/types.ts +11 -1
- package/src/index.ts +4 -0
- package/src/test/intersectionObserverPolyfill.ts +7 -0
- package/src/test/resizeObserverPolyfill.ts +80 -0
- package/src/utils/focusable.ts +15 -0
- package/src/utils/index.ts +1 -0
package/src/Dropdown/types.ts
CHANGED
|
@@ -8,6 +8,12 @@ import { Direction } from "../DirectionProvider/index.ts";
|
|
|
8
8
|
* the reading-direction control.
|
|
9
9
|
*/
|
|
10
10
|
export type DropdownRootBaseProps = {
|
|
11
|
+
/**
|
|
12
|
+
* The menu's sub-components — typically a single {@link DropdownTriggerProps |
|
|
13
|
+
* `Dropdown.Trigger`} followed by a {@link DropdownContentProps |
|
|
14
|
+
* `Dropdown.Content`}. Root renders no DOM of its own; it only provides
|
|
15
|
+
* context to these descendants.
|
|
16
|
+
*/
|
|
11
17
|
children?: ReactNode;
|
|
12
18
|
/**
|
|
13
19
|
* Reading direction for the menu. Affects which arrow key opens / closes
|
|
@@ -24,8 +30,19 @@ export type DropdownRootBaseProps = {
|
|
|
24
30
|
* and `open` is forbidden.
|
|
25
31
|
*/
|
|
26
32
|
export type DropdownRootUncontrolledProps = DropdownRootBaseProps & {
|
|
33
|
+
/**
|
|
34
|
+
* Open state on first render. Omit to start closed. The component owns
|
|
35
|
+
* the state thereafter.
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
27
38
|
defaultOpen?: boolean;
|
|
39
|
+
/** Forbidden in uncontrolled mode — use `defaultOpen` instead. */
|
|
28
40
|
open?: never;
|
|
41
|
+
/**
|
|
42
|
+
* Called whenever a user-driven transition opens or closes the menu
|
|
43
|
+
* (trigger click, Escape, outside click, selection). Optional in
|
|
44
|
+
* uncontrolled mode.
|
|
45
|
+
*/
|
|
29
46
|
onOpenChange?: (open: boolean) => void;
|
|
30
47
|
};
|
|
31
48
|
|
|
@@ -35,8 +52,18 @@ export type DropdownRootUncontrolledProps = DropdownRootBaseProps & {
|
|
|
35
52
|
* forbidden.
|
|
36
53
|
*/
|
|
37
54
|
export type DropdownRootControlledProps = DropdownRootBaseProps & {
|
|
55
|
+
/** Forbidden in controlled mode — use `open` instead. */
|
|
38
56
|
defaultOpen?: never;
|
|
57
|
+
/**
|
|
58
|
+
* The current open state. Must be kept in sync by the parent via
|
|
59
|
+
* `onOpenChange`; the component never mutates it internally.
|
|
60
|
+
*/
|
|
39
61
|
open: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Called whenever a user-driven transition would open or close the menu.
|
|
64
|
+
* The parent is responsible for reflecting the new value back into
|
|
65
|
+
* `open`. Required in controlled mode.
|
|
66
|
+
*/
|
|
40
67
|
onOpenChange: (open: boolean) => void;
|
|
41
68
|
};
|
|
42
69
|
|
|
@@ -58,8 +85,17 @@ export type DropdownTriggerProps = Omit<
|
|
|
58
85
|
ComponentProps<"button">,
|
|
59
86
|
"aria-haspopup" | "aria-expanded" | "aria-controls"
|
|
60
87
|
> & {
|
|
88
|
+
/** The trigger's visible label (or an element, with `asChild`). */
|
|
61
89
|
children?: ReactNode;
|
|
90
|
+
/** Forwarded to the underlying `HTMLButtonElement`. */
|
|
62
91
|
ref?: Ref<HTMLButtonElement>;
|
|
92
|
+
/**
|
|
93
|
+
* Render the composed child element instead of the default
|
|
94
|
+
* `<button type="button">`. The trigger's ARIA contract
|
|
95
|
+
* (`aria-haspopup`, `aria-expanded`, `aria-controls`) and its click
|
|
96
|
+
* handler are merged onto the child via the {@link Slot} pattern.
|
|
97
|
+
* @default false
|
|
98
|
+
*/
|
|
63
99
|
asChild?: boolean;
|
|
64
100
|
};
|
|
65
101
|
|
|
@@ -70,10 +106,26 @@ export type DropdownTriggerProps = Omit<
|
|
|
70
106
|
*/
|
|
71
107
|
export type DropdownContentProps = Omit<
|
|
72
108
|
ComponentProps<"menu">,
|
|
73
|
-
"role" | "popover" | "id"
|
|
109
|
+
"role" | "popover" | "id" | "ref"
|
|
74
110
|
> & {
|
|
111
|
+
/**
|
|
112
|
+
* The menu items — any mix of {@link DropdownItemProps | `Dropdown.Item`},
|
|
113
|
+
* {@link DropdownCheckboxItemProps | `Dropdown.CheckboxItem`},
|
|
114
|
+
* {@link DropdownRadioGroupProps | `Dropdown.RadioGroup`},
|
|
115
|
+
* {@link DropdownGroupProps | `Dropdown.Group`},
|
|
116
|
+
* {@link DropdownSeparatorProps | `Dropdown.Separator`}, and
|
|
117
|
+
* {@link DropdownSubProps | `Dropdown.Sub`}.
|
|
118
|
+
*/
|
|
75
119
|
children?: ReactNode;
|
|
120
|
+
/** Forwarded to the underlying `HTMLMenuElement`. */
|
|
76
121
|
ref?: Ref<HTMLMenuElement>;
|
|
122
|
+
/**
|
|
123
|
+
* Render the composed child element (with menu semantics) instead of the
|
|
124
|
+
* default `<menu role="menu" popover="auto">`. The managed `role`,
|
|
125
|
+
* `popover`, and `id` attributes and the keyboard handler are merged onto
|
|
126
|
+
* the child via the {@link Slot} pattern.
|
|
127
|
+
* @default false
|
|
128
|
+
*/
|
|
77
129
|
asChild?: boolean;
|
|
78
130
|
};
|
|
79
131
|
|
|
@@ -86,14 +138,26 @@ export type DropdownItemProps = Omit<
|
|
|
86
138
|
ComponentProps<"li">,
|
|
87
139
|
"role" | "tabIndex" | "onSelect"
|
|
88
140
|
> & {
|
|
141
|
+
/** The item's visible content (or an element, with `asChild`). */
|
|
89
142
|
children?: ReactNode;
|
|
143
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
90
144
|
ref?: Ref<HTMLLIElement>;
|
|
145
|
+
/**
|
|
146
|
+
* Render the composed child element (with menuitem semantics) instead of
|
|
147
|
+
* the default `<li role="menuitem">`.
|
|
148
|
+
* @default false
|
|
149
|
+
*/
|
|
91
150
|
asChild?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Mark the item non-interactive. Sets `aria-disabled="true"` and skips
|
|
153
|
+
* the item during arrow navigation, typeahead, and activation.
|
|
154
|
+
* @default false
|
|
155
|
+
*/
|
|
92
156
|
disabled?: boolean;
|
|
93
157
|
/**
|
|
94
158
|
* Fires when the item is activated (click, Enter, or Space). Called
|
|
95
|
-
* with
|
|
96
|
-
* Dropdown performs after selection.
|
|
159
|
+
* with a cancellable event whose `preventDefault()` skips the auto-close
|
|
160
|
+
* that Dropdown performs after selection.
|
|
97
161
|
*/
|
|
98
162
|
onSelect?: (event: Event) => void;
|
|
99
163
|
};
|
|
@@ -103,8 +167,15 @@ export type DropdownItemProps = Omit<
|
|
|
103
167
|
* groups of items. Extends the native `<li>` props and adds `asChild`.
|
|
104
168
|
*/
|
|
105
169
|
export type DropdownSeparatorProps = Omit<ComponentProps<"li">, "role"> & {
|
|
170
|
+
/** Optional content; separators are usually empty. */
|
|
106
171
|
children?: ReactNode;
|
|
172
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
107
173
|
ref?: Ref<HTMLLIElement>;
|
|
174
|
+
/**
|
|
175
|
+
* Render the composed child element (with separator semantics) instead of
|
|
176
|
+
* the default `<li role="separator">`.
|
|
177
|
+
* @default false
|
|
178
|
+
*/
|
|
108
179
|
asChild?: boolean;
|
|
109
180
|
};
|
|
110
181
|
|
|
@@ -114,14 +185,21 @@ export type DropdownSeparatorProps = Omit<ComponentProps<"li">, "role"> & {
|
|
|
114
185
|
* `forceMount` to keep it mounted while unchecked for enter/exit animation.
|
|
115
186
|
*/
|
|
116
187
|
export type DropdownItemIndicatorProps = ComponentProps<"span"> & {
|
|
188
|
+
/** The mark to render — commonly an SVG check or bullet icon. */
|
|
117
189
|
children?: ReactNode;
|
|
190
|
+
/** Forwarded to the underlying `HTMLSpanElement`. */
|
|
118
191
|
ref?: Ref<HTMLSpanElement>;
|
|
192
|
+
/**
|
|
193
|
+
* Render the composed child element instead of the default `<span>`.
|
|
194
|
+
* @default false
|
|
195
|
+
*/
|
|
119
196
|
asChild?: boolean;
|
|
120
197
|
/**
|
|
121
198
|
* Render the indicator even when its parent item is unchecked. The
|
|
122
199
|
* `data-state` attribute still reflects the live state (`"checked"` /
|
|
123
200
|
* `"unchecked"` / `"indeterminate"`), so consumers can animate the
|
|
124
201
|
* indicator in and out instead of mounting / unmounting it.
|
|
202
|
+
* @default false
|
|
125
203
|
*/
|
|
126
204
|
forceMount?: boolean;
|
|
127
205
|
};
|
|
@@ -131,8 +209,18 @@ export type DropdownItemIndicatorProps = ComponentProps<"span"> & {
|
|
|
131
209
|
* Extends the native `<li>` props and adds `asChild`.
|
|
132
210
|
*/
|
|
133
211
|
export type DropdownGroupProps = Omit<ComponentProps<"li">, "role"> & {
|
|
212
|
+
/**
|
|
213
|
+
* The grouped items, optionally led by a {@link DropdownLabelProps |
|
|
214
|
+
* `Dropdown.Label`} that names the group.
|
|
215
|
+
*/
|
|
134
216
|
children?: ReactNode;
|
|
217
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
135
218
|
ref?: Ref<HTMLLIElement>;
|
|
219
|
+
/**
|
|
220
|
+
* Render the composed child element (with `role="group"`) instead of the
|
|
221
|
+
* default `<li role="group">` wrapping `<ul role="none">`.
|
|
222
|
+
* @default false
|
|
223
|
+
*/
|
|
136
224
|
asChild?: boolean;
|
|
137
225
|
};
|
|
138
226
|
|
|
@@ -141,8 +229,14 @@ export type DropdownGroupProps = Omit<ComponentProps<"li">, "role"> & {
|
|
|
141
229
|
* {@link Dropdown.Group}. Extends the native `<li>` props and adds `asChild`.
|
|
142
230
|
*/
|
|
143
231
|
export type DropdownLabelProps = ComponentProps<"li"> & {
|
|
232
|
+
/** The label text. */
|
|
144
233
|
children?: ReactNode;
|
|
234
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
145
235
|
ref?: Ref<HTMLLIElement>;
|
|
236
|
+
/**
|
|
237
|
+
* Render the composed child element instead of the default `<li>`.
|
|
238
|
+
* @default false
|
|
239
|
+
*/
|
|
146
240
|
asChild?: boolean;
|
|
147
241
|
};
|
|
148
242
|
|
|
@@ -154,13 +248,29 @@ export type DropdownCheckboxItemBaseProps = Omit<
|
|
|
154
248
|
ComponentProps<"li">,
|
|
155
249
|
"role" | "tabIndex" | "aria-checked" | "defaultChecked" | "onSelect"
|
|
156
250
|
> & {
|
|
251
|
+
/**
|
|
252
|
+
* The item's content — typically a {@link DropdownItemIndicatorProps |
|
|
253
|
+
* `Dropdown.ItemIndicator`} plus a text label.
|
|
254
|
+
*/
|
|
157
255
|
children?: ReactNode;
|
|
256
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
158
257
|
ref?: Ref<HTMLLIElement>;
|
|
258
|
+
/**
|
|
259
|
+
* Render the composed child element (with `role="menuitemcheckbox"`)
|
|
260
|
+
* instead of the default `<li role="menuitemcheckbox">`.
|
|
261
|
+
* @default false
|
|
262
|
+
*/
|
|
159
263
|
asChild?: boolean;
|
|
264
|
+
/**
|
|
265
|
+
* Mark the item non-interactive. Sets `aria-disabled="true"`; activation
|
|
266
|
+
* is a no-op.
|
|
267
|
+
* @default false
|
|
268
|
+
*/
|
|
160
269
|
disabled?: boolean;
|
|
161
270
|
/**
|
|
162
|
-
* Fires
|
|
163
|
-
* `event.preventDefault()` to keep the menu open after
|
|
271
|
+
* Fires after activation toggles the checked state, with a cancellable
|
|
272
|
+
* event. Call `event.preventDefault()` to keep the menu open after
|
|
273
|
+
* toggling — useful for flipping several checkboxes in a row.
|
|
164
274
|
*/
|
|
165
275
|
onSelect?: (event: Event) => void;
|
|
166
276
|
};
|
|
@@ -172,8 +282,18 @@ export type DropdownCheckboxItemBaseProps = Omit<
|
|
|
172
282
|
*/
|
|
173
283
|
export type DropdownCheckboxItemUncontrolledProps =
|
|
174
284
|
DropdownCheckboxItemBaseProps & {
|
|
285
|
+
/**
|
|
286
|
+
* Checked state on first render — `true`, `false`, or `"indeterminate"`
|
|
287
|
+
* (rendered as `aria-checked="mixed"`). Omit to start unchecked.
|
|
288
|
+
* @default false
|
|
289
|
+
*/
|
|
175
290
|
defaultChecked?: CheckedState;
|
|
291
|
+
/** Forbidden in uncontrolled mode — use `defaultChecked` instead. */
|
|
176
292
|
checked?: never;
|
|
293
|
+
/**
|
|
294
|
+
* Called with the new boolean checked state whenever the user toggles
|
|
295
|
+
* the item. Optional in uncontrolled mode.
|
|
296
|
+
*/
|
|
177
297
|
onCheckedChange?: (checked: boolean) => void;
|
|
178
298
|
};
|
|
179
299
|
|
|
@@ -184,8 +304,18 @@ export type DropdownCheckboxItemUncontrolledProps =
|
|
|
184
304
|
*/
|
|
185
305
|
export type DropdownCheckboxItemControlledProps =
|
|
186
306
|
DropdownCheckboxItemBaseProps & {
|
|
307
|
+
/** Forbidden in controlled mode — use `checked` instead. */
|
|
187
308
|
defaultChecked?: never;
|
|
309
|
+
/**
|
|
310
|
+
* The current checked state — `true`, `false`, or `"indeterminate"`.
|
|
311
|
+
* Must be kept in sync by the parent via `onCheckedChange`. An
|
|
312
|
+
* indeterminate value resolves to `true` on the next activation.
|
|
313
|
+
*/
|
|
188
314
|
checked: CheckedState;
|
|
315
|
+
/**
|
|
316
|
+
* Called with the new boolean checked state whenever the user toggles
|
|
317
|
+
* the item. Required in controlled mode.
|
|
318
|
+
*/
|
|
189
319
|
onCheckedChange: (checked: boolean) => void;
|
|
190
320
|
};
|
|
191
321
|
|
|
@@ -201,11 +331,25 @@ export type DropdownCheckboxItemProps =
|
|
|
201
331
|
|
|
202
332
|
/**
|
|
203
333
|
* Shared base for both {@link DropdownRadioGroupProps} variants — the native
|
|
204
|
-
* `<li>` props
|
|
334
|
+
* `<li>` props (with the native ordinal `value` omitted so the controlled
|
|
335
|
+
* variant can narrow it to a `string`) plus `asChild`.
|
|
205
336
|
*/
|
|
206
|
-
export type DropdownRadioGroupBaseProps = Omit<
|
|
337
|
+
export type DropdownRadioGroupBaseProps = Omit<
|
|
338
|
+
ComponentProps<"li">,
|
|
339
|
+
"role" | "value" | "defaultValue"
|
|
340
|
+
> & {
|
|
341
|
+
/**
|
|
342
|
+
* The radio choices — a set of {@link DropdownRadioItemProps |
|
|
343
|
+
* `Dropdown.RadioItem`} elements.
|
|
344
|
+
*/
|
|
207
345
|
children?: ReactNode;
|
|
346
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
208
347
|
ref?: Ref<HTMLLIElement>;
|
|
348
|
+
/**
|
|
349
|
+
* Render the composed child element (with `role="group"`) instead of the
|
|
350
|
+
* default `<li role="group">` wrapping `<ul role="none">`.
|
|
351
|
+
* @default false
|
|
352
|
+
*/
|
|
209
353
|
asChild?: boolean;
|
|
210
354
|
};
|
|
211
355
|
|
|
@@ -216,8 +360,17 @@ export type DropdownRadioGroupBaseProps = Omit<ComponentProps<"li">, "role"> & {
|
|
|
216
360
|
*/
|
|
217
361
|
export type DropdownRadioGroupUncontrolledProps =
|
|
218
362
|
DropdownRadioGroupBaseProps & {
|
|
363
|
+
/**
|
|
364
|
+
* The item `value` selected on first render. Omit for no initial
|
|
365
|
+
* selection.
|
|
366
|
+
*/
|
|
219
367
|
defaultValue?: string;
|
|
368
|
+
/** Forbidden in uncontrolled mode — use `defaultValue` instead. */
|
|
220
369
|
value?: never;
|
|
370
|
+
/**
|
|
371
|
+
* Called with the newly selected item `value` whenever the user picks a
|
|
372
|
+
* different radio item. Optional in uncontrolled mode.
|
|
373
|
+
*/
|
|
221
374
|
onValueChange?: (value: string) => void;
|
|
222
375
|
};
|
|
223
376
|
|
|
@@ -227,8 +380,18 @@ export type DropdownRadioGroupUncontrolledProps =
|
|
|
227
380
|
* forbidden.
|
|
228
381
|
*/
|
|
229
382
|
export type DropdownRadioGroupControlledProps = DropdownRadioGroupBaseProps & {
|
|
383
|
+
/** Forbidden in controlled mode — use `value` instead. */
|
|
230
384
|
defaultValue?: never;
|
|
385
|
+
/**
|
|
386
|
+
* The `value` of the currently selected {@link DropdownRadioItemProps |
|
|
387
|
+
* `Dropdown.RadioItem`}. Must be kept in sync by the parent via
|
|
388
|
+
* `onValueChange`.
|
|
389
|
+
*/
|
|
231
390
|
value: string;
|
|
391
|
+
/**
|
|
392
|
+
* Called with the newly selected item `value` whenever the user picks a
|
|
393
|
+
* different radio item. Required in controlled mode.
|
|
394
|
+
*/
|
|
232
395
|
onValueChange: (value: string) => void;
|
|
233
396
|
};
|
|
234
397
|
|
|
@@ -244,18 +407,43 @@ export type DropdownRadioGroupProps =
|
|
|
244
407
|
|
|
245
408
|
/**
|
|
246
409
|
* Props for {@link Dropdown.RadioItem} — one option within a
|
|
247
|
-
* {@link Dropdown.RadioGroup}. Extends the native `<li>` props
|
|
248
|
-
*
|
|
410
|
+
* {@link Dropdown.RadioGroup}. Extends the native `<li>` props (with the
|
|
411
|
+
* native ordinal `value` omitted so it can be narrowed to a required `string`)
|
|
412
|
+
* and adds `value`, plus `disabled`, `asChild`, and `onSelect`.
|
|
249
413
|
*/
|
|
250
414
|
export type DropdownRadioItemProps = Omit<
|
|
251
415
|
ComponentProps<"li">,
|
|
252
|
-
"role" | "tabIndex" | "aria-checked" | "onSelect"
|
|
416
|
+
"role" | "tabIndex" | "aria-checked" | "onSelect" | "value"
|
|
253
417
|
> & {
|
|
418
|
+
/**
|
|
419
|
+
* The item's content — typically a {@link DropdownItemIndicatorProps |
|
|
420
|
+
* `Dropdown.ItemIndicator`} plus a text label.
|
|
421
|
+
*/
|
|
254
422
|
children?: ReactNode;
|
|
423
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
255
424
|
ref?: Ref<HTMLLIElement>;
|
|
425
|
+
/**
|
|
426
|
+
* Render the composed child element (with `role="menuitemradio"`) instead
|
|
427
|
+
* of the default `<li role="menuitemradio">`.
|
|
428
|
+
* @default false
|
|
429
|
+
*/
|
|
256
430
|
asChild?: boolean;
|
|
431
|
+
/**
|
|
432
|
+
* Mark the item non-interactive. Sets `aria-disabled="true"`; activation
|
|
433
|
+
* is a no-op.
|
|
434
|
+
* @default false
|
|
435
|
+
*/
|
|
257
436
|
disabled?: boolean;
|
|
437
|
+
/**
|
|
438
|
+
* This item's identifier within the enclosing
|
|
439
|
+
* {@link DropdownRadioGroupProps | `Dropdown.RadioGroup`}. When it matches
|
|
440
|
+
* the group's active value the item is checked. Required.
|
|
441
|
+
*/
|
|
258
442
|
value: string;
|
|
443
|
+
/**
|
|
444
|
+
* Fires after activation selects this item, with a cancellable event.
|
|
445
|
+
* Call `event.preventDefault()` to keep the menu open after selecting.
|
|
446
|
+
*/
|
|
259
447
|
onSelect?: (event: Event) => void;
|
|
260
448
|
};
|
|
261
449
|
|
|
@@ -264,6 +452,11 @@ export type DropdownRadioItemProps = Omit<
|
|
|
264
452
|
* `children`.
|
|
265
453
|
*/
|
|
266
454
|
export type DropdownSubBaseProps = {
|
|
455
|
+
/**
|
|
456
|
+
* The submenu boundary's content — a {@link DropdownSubTriggerProps |
|
|
457
|
+
* `Dropdown.SubTrigger`} and its sibling {@link DropdownSubContentProps |
|
|
458
|
+
* `Dropdown.SubContent`}.
|
|
459
|
+
*/
|
|
267
460
|
children?: ReactNode;
|
|
268
461
|
};
|
|
269
462
|
|
|
@@ -273,8 +466,17 @@ export type DropdownSubBaseProps = {
|
|
|
273
466
|
* optional and the controlled `open` prop is forbidden.
|
|
274
467
|
*/
|
|
275
468
|
export type DropdownSubUncontrolledProps = DropdownSubBaseProps & {
|
|
469
|
+
/**
|
|
470
|
+
* Submenu open state on first render. Omit to start closed.
|
|
471
|
+
* @default false
|
|
472
|
+
*/
|
|
276
473
|
defaultOpen?: boolean;
|
|
474
|
+
/** Forbidden in uncontrolled mode — use `defaultOpen` instead. */
|
|
277
475
|
open?: never;
|
|
476
|
+
/**
|
|
477
|
+
* Called whenever the submenu opens or closes. Optional in uncontrolled
|
|
478
|
+
* mode.
|
|
479
|
+
*/
|
|
278
480
|
onOpenChange?: (open: boolean) => void;
|
|
279
481
|
};
|
|
280
482
|
|
|
@@ -284,8 +486,17 @@ export type DropdownSubUncontrolledProps = DropdownSubBaseProps & {
|
|
|
284
486
|
* forbidden.
|
|
285
487
|
*/
|
|
286
488
|
export type DropdownSubControlledProps = DropdownSubBaseProps & {
|
|
489
|
+
/** Forbidden in controlled mode — use `open` instead. */
|
|
287
490
|
defaultOpen?: never;
|
|
491
|
+
/**
|
|
492
|
+
* The current submenu open state. Must be kept in sync by the parent via
|
|
493
|
+
* `onOpenChange`.
|
|
494
|
+
*/
|
|
288
495
|
open: boolean;
|
|
496
|
+
/**
|
|
497
|
+
* Called whenever the submenu would open or close. The parent reflects
|
|
498
|
+
* the new value back into `open`. Required in controlled mode.
|
|
499
|
+
*/
|
|
289
500
|
onOpenChange: (open: boolean) => void;
|
|
290
501
|
};
|
|
291
502
|
|
|
@@ -307,9 +518,21 @@ export type DropdownSubTriggerProps = Omit<
|
|
|
307
518
|
ComponentProps<"li">,
|
|
308
519
|
"role" | "tabIndex" | "aria-haspopup" | "aria-expanded" | "aria-controls"
|
|
309
520
|
> & {
|
|
521
|
+
/** The trigger's visible label (or an element, with `asChild`). */
|
|
310
522
|
children?: ReactNode;
|
|
523
|
+
/** Forwarded to the underlying `HTMLLIElement`. */
|
|
311
524
|
ref?: Ref<HTMLLIElement>;
|
|
525
|
+
/**
|
|
526
|
+
* Render the composed child element (with menuitem + submenu-trigger
|
|
527
|
+
* semantics) instead of the default `<li role="menuitem">`.
|
|
528
|
+
* @default false
|
|
529
|
+
*/
|
|
312
530
|
asChild?: boolean;
|
|
531
|
+
/**
|
|
532
|
+
* Mark the trigger non-interactive. Sets `aria-disabled="true"` and
|
|
533
|
+
* ignores both click and the open arrow key.
|
|
534
|
+
* @default false
|
|
535
|
+
*/
|
|
313
536
|
disabled?: boolean;
|
|
314
537
|
};
|
|
315
538
|
|
|
@@ -320,9 +543,16 @@ export type DropdownSubTriggerProps = Omit<
|
|
|
320
543
|
*/
|
|
321
544
|
export type DropdownSubContentProps = Omit<
|
|
322
545
|
ComponentProps<"menu">,
|
|
323
|
-
"role" | "popover" | "id"
|
|
546
|
+
"role" | "popover" | "id" | "ref"
|
|
324
547
|
> & {
|
|
548
|
+
/** The submenu's items. */
|
|
325
549
|
children?: ReactNode;
|
|
550
|
+
/** Forwarded to the underlying `HTMLMenuElement`. */
|
|
326
551
|
ref?: Ref<HTMLMenuElement>;
|
|
552
|
+
/**
|
|
553
|
+
* Render the composed child element (with menu semantics) instead of the
|
|
554
|
+
* default `<menu role="menu" popover="auto">`.
|
|
555
|
+
* @default false
|
|
556
|
+
*/
|
|
327
557
|
asChild?: boolean;
|
|
328
558
|
};
|
|
@@ -22,11 +22,16 @@ import {
|
|
|
22
22
|
* Opt out of the live region by passing `role={undefined}` for an empty
|
|
23
23
|
* state that is part of the initial, static page.
|
|
24
24
|
*
|
|
25
|
+
* **Styling hooks.** `EmptyState.Root` emits no `data-*` attributes — it is a
|
|
26
|
+
* static layout component. Target it via `className` or element selectors.
|
|
27
|
+
*
|
|
25
28
|
* **`asChild` composition.** Renders the consumer's element instead of a
|
|
26
29
|
* `<div>`, merging `role="status"` and all other props in via the
|
|
27
30
|
* {@link Slot} utility.
|
|
28
31
|
*
|
|
29
|
-
* @
|
|
32
|
+
* @extends HTMLDivElement
|
|
33
|
+
*
|
|
34
|
+
* @example Conditional empty state (live region)
|
|
30
35
|
* ```tsx
|
|
31
36
|
* {results.length === 0 && (
|
|
32
37
|
* <EmptyState.Root>
|
|
@@ -35,6 +40,22 @@ import {
|
|
|
35
40
|
* </EmptyState.Root>
|
|
36
41
|
* )}
|
|
37
42
|
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example Static empty state (opt out of live region)
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <EmptyState.Root role={undefined}>
|
|
47
|
+
* <EmptyState.Title>No projects yet</EmptyState.Title>
|
|
48
|
+
* </EmptyState.Root>
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @example asChild — render a `<section>` instead of a `<div>`
|
|
52
|
+
* ```tsx
|
|
53
|
+
* <EmptyState.Root asChild>
|
|
54
|
+
* <section aria-label="Empty inbox">
|
|
55
|
+
* <EmptyState.Title>Your inbox is empty</EmptyState.Title>
|
|
56
|
+
* </section>
|
|
57
|
+
* </EmptyState.Root>
|
|
58
|
+
* ```
|
|
38
59
|
*/
|
|
39
60
|
export function EmptyStateRoot({
|
|
40
61
|
asChild = false,
|
|
@@ -50,7 +71,11 @@ export function EmptyStateRoot({
|
|
|
50
71
|
return <div {...rootProps}>{children}</div>;
|
|
51
72
|
}
|
|
52
73
|
|
|
53
|
-
|
|
74
|
+
// Runtime-dead: the compound alias below (same object via Object.assign)
|
|
75
|
+
// overwrites this to "EmptyState" at load, so the value is never observable.
|
|
76
|
+
// The assignment stays because it declares `displayName` on
|
|
77
|
+
// `typeof EmptyStateRoot`, which EmptyStateCompound extends.
|
|
78
|
+
// Stryker disable next-line StringLiteral: overwritten by the compound alias — an equivalent mutant.
|
|
54
79
|
EmptyStateRoot.displayName = "EmptyStateRoot";
|
|
55
80
|
|
|
56
81
|
/**
|
|
@@ -64,16 +89,28 @@ EmptyStateRoot.displayName = "EmptyStateRoot";
|
|
|
64
89
|
* genuinely informative, pass `aria-hidden={false}` and give it an accessible
|
|
65
90
|
* name yourself.
|
|
66
91
|
*
|
|
92
|
+
* **Styling hooks.** `EmptyState.Media` emits no `data-*` attributes. Target it
|
|
93
|
+
* via `className` or the `[aria-hidden]` attribute selector.
|
|
94
|
+
*
|
|
67
95
|
* **`asChild` composition.** Renders the consumer's element instead of a
|
|
68
96
|
* `<div>`, merging `aria-hidden="true"` and all other props in via the
|
|
69
97
|
* {@link Slot} utility.
|
|
70
98
|
*
|
|
71
|
-
* @
|
|
99
|
+
* @extends HTMLDivElement
|
|
100
|
+
*
|
|
101
|
+
* @example Decorative icon (default)
|
|
72
102
|
* ```tsx
|
|
73
103
|
* <EmptyState.Media>
|
|
74
104
|
* <InboxIcon />
|
|
75
105
|
* </EmptyState.Media>
|
|
76
106
|
* ```
|
|
107
|
+
*
|
|
108
|
+
* @example Informative artwork — opt back in to accessibility tree
|
|
109
|
+
* ```tsx
|
|
110
|
+
* <EmptyState.Media aria-hidden={false}>
|
|
111
|
+
* <img src="/chart.svg" alt="Sales trending to zero" />
|
|
112
|
+
* </EmptyState.Media>
|
|
113
|
+
* ```
|
|
77
114
|
*/
|
|
78
115
|
export function EmptyStateMedia({
|
|
79
116
|
asChild = false,
|
|
@@ -101,9 +138,19 @@ EmptyStateMedia.displayName = "EmptyStateMedia";
|
|
|
101
138
|
* stands in for a titled section, promote the title to a real heading with
|
|
102
139
|
* `asChild` so it joins the page's heading hierarchy.
|
|
103
140
|
*
|
|
141
|
+
* **Styling hooks.** `EmptyState.Title` emits no `data-*` attributes. Target
|
|
142
|
+
* it via `className` or the `p` element selector.
|
|
143
|
+
*
|
|
104
144
|
* **`asChild` composition.** Renders the consumer's element instead of a
|
|
105
145
|
* `<p>`, merging all props in via the {@link Slot} utility.
|
|
106
146
|
*
|
|
147
|
+
* @extends HTMLParagraphElement
|
|
148
|
+
*
|
|
149
|
+
* @example Default paragraph headline
|
|
150
|
+
* ```tsx
|
|
151
|
+
* <EmptyState.Title>No results found</EmptyState.Title>
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
107
154
|
* @example Promote to a heading
|
|
108
155
|
* ```tsx
|
|
109
156
|
* <EmptyState.Title asChild>
|
|
@@ -134,9 +181,14 @@ EmptyStateTitle.displayName = "EmptyStateTitle";
|
|
|
134
181
|
* Keep it to guidance the user can act on; the actionable controls themselves
|
|
135
182
|
* belong in {@link EmptyStateActions | `Actions`}.
|
|
136
183
|
*
|
|
184
|
+
* **Styling hooks.** `EmptyState.Description` emits no `data-*` attributes.
|
|
185
|
+
* Target it via `className` or the `p` element selector.
|
|
186
|
+
*
|
|
137
187
|
* **`asChild` composition.** Renders the consumer's element instead of a
|
|
138
188
|
* `<p>`, merging all props in via the {@link Slot} utility.
|
|
139
189
|
*
|
|
190
|
+
* @extends HTMLParagraphElement
|
|
191
|
+
*
|
|
140
192
|
* @example
|
|
141
193
|
* ```tsx
|
|
142
194
|
* <EmptyState.Description>Try adjusting your filters.</EmptyState.Description>
|
|
@@ -167,9 +219,14 @@ EmptyStateDescription.displayName = "EmptyStateDescription";
|
|
|
167
219
|
* {@link EmptyStateRoot | `Root`}'s live region, the control labels are
|
|
168
220
|
* included when the empty state is announced.
|
|
169
221
|
*
|
|
222
|
+
* **Styling hooks.** `EmptyState.Actions` emits no `data-*` attributes. Target
|
|
223
|
+
* it via `className` or the `div` element selector.
|
|
224
|
+
*
|
|
170
225
|
* **`asChild` composition.** Renders the consumer's element instead of a
|
|
171
226
|
* `<div>`, merging all props in via the {@link Slot} utility.
|
|
172
227
|
*
|
|
228
|
+
* @extends HTMLDivElement
|
|
229
|
+
*
|
|
173
230
|
* @example
|
|
174
231
|
* ```tsx
|
|
175
232
|
* <EmptyState.Actions>
|
|
@@ -227,7 +284,7 @@ export type EmptyStateCompound = typeof EmptyStateRoot & {
|
|
|
227
284
|
* All sub-components are stateless and optional — compose only the parts a
|
|
228
285
|
* given empty state needs.
|
|
229
286
|
*
|
|
230
|
-
* @example
|
|
287
|
+
* @example Full composition
|
|
231
288
|
* ```tsx
|
|
232
289
|
* import { EmptyState } from "@primitiv-ui/react";
|
|
233
290
|
*
|
|
@@ -244,6 +301,19 @@ export type EmptyStateCompound = typeof EmptyStateRoot & {
|
|
|
244
301
|
* </EmptyState.Root>
|
|
245
302
|
* )}
|
|
246
303
|
* ```
|
|
304
|
+
*
|
|
305
|
+
* @example Minimal — title only
|
|
306
|
+
* ```tsx
|
|
307
|
+
* <EmptyState.Root>
|
|
308
|
+
* <EmptyState.Title>No messages</EmptyState.Title>
|
|
309
|
+
* </EmptyState.Root>
|
|
310
|
+
* ```
|
|
311
|
+
*
|
|
312
|
+
* @see {@link EmptyStateRoot} for the live-region opt-out and `asChild` details.
|
|
313
|
+
* @see {@link EmptyStateMedia} for the `aria-hidden` default and opt-back-in pattern.
|
|
314
|
+
* @see {@link EmptyStateTitle} for the `<p>` default and heading promotion via `asChild`.
|
|
315
|
+
* @see {@link EmptyStateDescription} for secondary copy placement guidance.
|
|
316
|
+
* @see {@link EmptyStateActions} for grouping recovery controls.
|
|
247
317
|
*/
|
|
248
318
|
const EmptyState: EmptyStateCompound = Object.assign(EmptyStateRoot, {
|
|
249
319
|
Root: EmptyStateRoot,
|
|
@@ -4,7 +4,7 @@ import { render, screen } from "@testing-library/react";
|
|
|
4
4
|
describe("EmptyState.Actions component", () => {
|
|
5
5
|
it("should render a div containing its children", () => {
|
|
6
6
|
// Arrange
|
|
7
|
-
render(
|
|
7
|
+
const { container } = render(
|
|
8
8
|
<EmptyState.Actions>
|
|
9
9
|
<button>Clear filters</button>
|
|
10
10
|
</EmptyState.Actions>,
|
|
@@ -13,11 +13,14 @@ describe("EmptyState.Actions component", () => {
|
|
|
13
13
|
// Assert
|
|
14
14
|
const button = screen.getByRole("button", { name: "Clear filters" });
|
|
15
15
|
expect(button.parentElement?.tagName).toBe("DIV");
|
|
16
|
+
// The wrapping <div> is Actions' own render root (default is not asChild),
|
|
17
|
+
// not the test container.
|
|
18
|
+
expect((container.firstChild as HTMLElement).tagName).toBe("DIV");
|
|
16
19
|
});
|
|
17
20
|
|
|
18
21
|
it("should render the consumer element with asChild", () => {
|
|
19
22
|
// Arrange
|
|
20
|
-
render(
|
|
23
|
+
const { container } = render(
|
|
21
24
|
<EmptyState.Actions asChild>
|
|
22
25
|
<nav>
|
|
23
26
|
<button>Clear filters</button>
|
|
@@ -28,5 +31,7 @@ describe("EmptyState.Actions component", () => {
|
|
|
28
31
|
// Assert
|
|
29
32
|
const button = screen.getByRole("button", { name: "Clear filters" });
|
|
30
33
|
expect(button.parentElement?.tagName).toBe("NAV");
|
|
34
|
+
// Slot renders the <nav> in place — no default <div> wrapper around it.
|
|
35
|
+
expect(container.querySelector("div")).toBeNull();
|
|
31
36
|
});
|
|
32
37
|
});
|
|
@@ -17,7 +17,7 @@ describe("EmptyState.Description component", () => {
|
|
|
17
17
|
|
|
18
18
|
it("should render the consumer element with asChild", () => {
|
|
19
19
|
// Arrange
|
|
20
|
-
render(
|
|
20
|
+
const { container } = render(
|
|
21
21
|
<EmptyState.Description asChild>
|
|
22
22
|
<span>Try adjusting your filters.</span>
|
|
23
23
|
</EmptyState.Description>,
|
|
@@ -26,5 +26,7 @@ describe("EmptyState.Description component", () => {
|
|
|
26
26
|
// Assert
|
|
27
27
|
const description = screen.getByText("Try adjusting your filters.");
|
|
28
28
|
expect(description.tagName).toBe("SPAN");
|
|
29
|
+
// Slot renders the child in place — no default <p> wrapper around it.
|
|
30
|
+
expect(container.querySelector("p")).toBeNull();
|
|
29
31
|
});
|
|
30
32
|
});
|
|
@@ -25,4 +25,14 @@ describe("EmptyState.Root component", () => {
|
|
|
25
25
|
expect(root.tagName).toBe("SECTION");
|
|
26
26
|
expect(root).toHaveTextContent("No projects yet");
|
|
27
27
|
});
|
|
28
|
+
|
|
29
|
+
it("sets a displayName on the compound and each sub-component", () => {
|
|
30
|
+
// Assert — empty displayNames would render each as anonymous in DevTools.
|
|
31
|
+
// Root aliases the compound (Object.assign), so its name is "EmptyState".
|
|
32
|
+
expect(EmptyState.displayName).toBe("EmptyState");
|
|
33
|
+
expect(EmptyState.Media.displayName).toBe("EmptyStateMedia");
|
|
34
|
+
expect(EmptyState.Title.displayName).toBe("EmptyStateTitle");
|
|
35
|
+
expect(EmptyState.Description.displayName).toBe("EmptyStateDescription");
|
|
36
|
+
expect(EmptyState.Actions.displayName).toBe("EmptyStateActions");
|
|
37
|
+
});
|
|
28
38
|
});
|