@primitiv-ui/react 0.1.28 → 0.1.30
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 +19 -2
- 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 +32 -11
- package/src/Accordion/README.md +2 -2
- 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 +139 -21
- 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/__tests__/Button.contract.test.tsx +1 -1
- package/src/Button/types.ts +21 -0
- package/src/Carousel/Carousel.tsx +268 -20
- package/src/Carousel/README.md +764 -87
- 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 +90 -2
- 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 +458 -36
- package/src/Checkbox/Checkbox.tsx +11 -3
- 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 +14 -5
- package/src/CheckboxCard/README.md +1 -1
- 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 +41 -1
- 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/Combobox/Combobox.tsx +508 -0
- package/src/Combobox/ComboboxContext.ts +23 -0
- package/src/Combobox/README.md +170 -0
- package/src/Combobox/__tests__/Combobox.asChild.test.tsx +62 -0
- package/src/Combobox/__tests__/Combobox.basic-rendering.test.tsx +16 -0
- package/src/Combobox/__tests__/Combobox.content.test.tsx +36 -0
- package/src/Combobox/__tests__/Combobox.controlled-state.test.tsx +88 -0
- package/src/Combobox/__tests__/Combobox.empty.test.tsx +42 -0
- package/src/Combobox/__tests__/Combobox.ids.test.tsx +66 -0
- package/src/Combobox/__tests__/Combobox.item.test.tsx +76 -0
- package/src/Combobox/__tests__/Combobox.keyboard-interaction.test.tsx +196 -0
- package/src/Combobox/__tests__/Combobox.light-dismiss.test.tsx +101 -0
- package/src/Combobox/__tests__/Combobox.query.test.tsx +105 -0
- package/src/Combobox/hooks/index.ts +1 -0
- package/src/Combobox/hooks/useComboboxRoot.ts +2 -0
- package/src/Combobox/index.ts +3 -0
- package/src/Combobox/types.ts +133 -0
- package/src/ContextMenu/ContextMenu.tsx +270 -4
- package/src/ContextMenu/README.md +1 -1
- package/src/ContextMenu/types.ts +148 -6
- package/src/DirectionProvider/DirectionProvider.tsx +41 -9
- package/src/DirectionProvider/README.md +4 -4
- 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.contract.test.tsx +1 -1
- 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 +119 -2
- package/src/Dropdown/README.md +7 -5
- package/src/Dropdown/types.ts +241 -11
- package/src/EmptyState/EmptyState.tsx +74 -4
- package/src/EmptyState/README.md +2 -2
- 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/README.md +1 -1
- 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/hooks/useFieldProps.ts +1 -1
- package/src/Field/types.ts +60 -12
- package/src/Fieldset/Fieldset.tsx +55 -19
- package/src/Fieldset/README.md +2 -2
- package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +9 -0
- package/src/Fieldset/types.ts +20 -2
- package/src/Input/Input.tsx +15 -10
- package/src/Input/README.md +2 -2
- 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/Listbox/Listbox.tsx +475 -0
- package/src/Listbox/ListboxContext.ts +48 -0
- package/src/Listbox/README.md +248 -0
- package/src/Listbox/__tests__/Listbox.active-descendant.test.tsx +128 -0
- package/src/Listbox/__tests__/Listbox.asChild.test.tsx +91 -0
- package/src/Listbox/__tests__/Listbox.basic-rendering.test.tsx +16 -0
- package/src/Listbox/__tests__/Listbox.controlled-state.test.tsx +114 -0
- package/src/Listbox/__tests__/Listbox.disabled.test.tsx +90 -0
- package/src/Listbox/__tests__/Listbox.fixtures.ts +27 -0
- package/src/Listbox/__tests__/Listbox.group-label.test.tsx +97 -0
- package/src/Listbox/__tests__/Listbox.group.test.tsx +45 -0
- package/src/Listbox/__tests__/Listbox.keyboard-edge-cases.test.tsx +144 -0
- package/src/Listbox/__tests__/Listbox.keyboard-interaction.test.tsx +112 -0
- package/src/Listbox/__tests__/Listbox.modifier-keys.test.tsx +86 -0
- package/src/Listbox/__tests__/Listbox.multi-select-keyboard.test.tsx +368 -0
- package/src/Listbox/__tests__/Listbox.multiple-mode.test.tsx +91 -0
- package/src/Listbox/__tests__/Listbox.mutation-hardening.test.tsx +480 -0
- package/src/Listbox/__tests__/Listbox.orientation.test.tsx +59 -0
- package/src/Listbox/__tests__/Listbox.reading-direction.test.tsx +65 -0
- package/src/Listbox/__tests__/Listbox.reorder.test.tsx +78 -0
- package/src/Listbox/__tests__/Listbox.selection-follows-focus.test.tsx +64 -0
- package/src/Listbox/__tests__/Listbox.strict-context.test.tsx +21 -0
- package/src/Listbox/__tests__/Listbox.typeahead.test.tsx +123 -0
- package/src/Listbox/__tests__/Listbox.uncontrolled-state.test.tsx +48 -0
- package/src/Listbox/constants.ts +2 -0
- package/src/Listbox/hooks/index.ts +1 -0
- package/src/Listbox/hooks/useListboxRoot.ts +407 -0
- package/src/Listbox/index.ts +3 -0
- package/src/Listbox/types.ts +238 -0
- package/src/MillerColumns/MillerColumns.tsx +186 -45
- package/src/MillerColumns/README.md +90 -20
- package/src/MillerColumns/__tests__/MillerColumns.aria.test.tsx +45 -0
- package/src/MillerColumns/__tests__/MillerColumns.auto-scroll.test.tsx +4 -2
- package/src/MillerColumns/__tests__/MillerColumns.basic-rendering.test.tsx +18 -3
- package/src/MillerColumns/__tests__/MillerColumns.data-attributes.test.tsx +48 -0
- package/src/MillerColumns/__tests__/MillerColumns.keyboard-interaction.test.tsx +35 -2
- package/src/MillerColumns/__tests__/MillerColumns.keyboard-selection-state.test.tsx +96 -0
- package/src/MillerColumns/__tests__/MillerColumns.preview-panel.test.tsx +29 -4
- package/src/MillerColumns/__tests__/MillerColumns.preview-visibility.test.tsx +79 -0
- package/src/MillerColumns/__tests__/MillerColumns.resize.test.tsx +189 -0
- package/src/MillerColumns/__tests__/MillerColumns.styled-parts.test.tsx +143 -0
- package/src/MillerColumns/__tests__/MillerColumns.typeahead.test.tsx +104 -0
- package/src/MillerColumns/constants.ts +2 -0
- package/src/MillerColumns/hooks/useMillerColumnsColumn.ts +5 -2
- package/src/MillerColumns/hooks/useMillerColumnsItem.ts +29 -7
- package/src/MillerColumns/hooks/useMillerColumnsResizeHandle.ts +80 -10
- package/src/MillerColumns/hooks/useMillerColumnsRoot.ts +0 -0
- package/src/MillerColumns/index.ts +4 -0
- package/src/MillerColumns/types.ts +136 -30
- package/src/MillerColumns/utils.ts +69 -3
- package/src/Modal/Modal.tsx +35 -8
- package/src/Modal/README.md +24 -15
- 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/README.md +2 -2
- 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/README.md +1 -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 +10 -2
- 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/README.md +2 -2
- package/src/RadioGroup/RadioGroup.tsx +12 -1
- 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 +87 -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 +596 -69
- 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 +7 -7
- 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/README.md +3 -3
- package/src/SkipNav/SkipNav.tsx +72 -32
- package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +6 -0
- package/src/SkipNav/types.ts +17 -9
- package/src/Slider/README.md +37 -7
- package/src/Slider/Slider.tsx +42 -4
- 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 +112 -2
- package/src/Slot/composeEventHandlers.ts +26 -2
- package/src/Slot/types.ts +16 -0
- package/src/SplitButton/README.md +225 -0
- package/src/SplitButton/SplitButton.tsx +457 -0
- package/src/SplitButton/SplitButtonContext.ts +26 -0
- package/src/SplitButton/__tests__/SplitButton.accessibility.test.tsx +55 -0
- package/src/SplitButton/__tests__/SplitButton.asChild.test.tsx +51 -0
- package/src/SplitButton/__tests__/SplitButton.basic-rendering.test.tsx +57 -0
- package/src/SplitButton/__tests__/SplitButton.data-attributes.test.tsx +28 -0
- package/src/SplitButton/__tests__/SplitButton.disabled.test.tsx +79 -0
- package/src/SplitButton/__tests__/SplitButton.error-handling.test.tsx +29 -0
- package/src/SplitButton/__tests__/SplitButton.keyboard-interaction.test.tsx +56 -0
- package/src/SplitButton/__tests__/SplitButton.state-modes.test.tsx +68 -0
- package/src/SplitButton/index.ts +2 -0
- package/src/SplitButton/types.ts +186 -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/README.md +1 -1
- package/src/Switch/Switch.tsx +17 -4
- 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/README.md +110 -8
- package/src/Table/Table.tsx +246 -16
- package/src/Table/TableExpandableContext.ts +7 -0
- package/src/Table/__tests__/Table.DetailRow.test.tsx +83 -0
- package/src/Table/__tests__/Table.ExpandTrigger.test.tsx +102 -0
- package/src/Table/__tests__/Table.Expandable.test.tsx +121 -0
- package/src/Table/types.ts +191 -17
- package/src/Tabs/README.md +4 -4
- package/src/Tabs/Tabs.tsx +13 -4
- package/src/Tabs/__tests__/Tabs.activation-mode.test.tsx +58 -0
- package/src/Tabs/__tests__/Tabs.basic-rendering.test.tsx +15 -0
- package/src/Tabs/__tests__/Tabs.change-event-callbacks.test.tsx +24 -0
- package/src/Tabs/__tests__/Tabs.disabled-tabs.test.tsx +36 -0
- package/src/Tabs/__tests__/Tabs.lazy-mount.test.tsx +17 -0
- package/src/Tabs/hooks/useTabsContent.ts +5 -0
- package/src/Tabs/hooks/useTabsRoot.ts +29 -5
- package/src/Tabs/hooks/useTabsTrigger.ts +7 -2
- 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/README.md +4 -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/README.md +7 -7
- package/src/Tooltip/Tooltip.tsx +100 -22
- package/src/Tooltip/__tests__/Tooltip.basic-rendering.test.tsx +50 -0
- package/src/Tooltip/__tests__/Tooltip.error-handling.test.tsx +37 -0
- package/src/Tooltip/__tests__/Tooltip.escape-hatches.test.tsx +73 -0
- package/src/Tooltip/__tests__/Tooltip.keyboard-interaction.test.tsx +134 -0
- package/src/Tooltip/__tests__/Tooltip.skip-delay.test.tsx +216 -0
- package/src/Tooltip/__tests__/Tooltip.timer-coordination.test.tsx +223 -0
- package/src/Tooltip/hooks/__tests__/useTooltipProvider.test.tsx +40 -0
- package/src/Tooltip/hooks/useTooltipContent.ts +3 -0
- package/src/Tooltip/hooks/useTooltipProvider.ts +46 -8
- package/src/Tooltip/hooks/useTooltipRoot.ts +30 -6
- package/src/Tooltip/types.ts +89 -8
- package/src/Tree/README.md +69 -5
- package/src/Tree/Tree.tsx +188 -34
- package/src/Tree/__tests__/Tree.styled-wrapper.test.tsx +159 -0
- package/src/Tree/hooks/useTreePath.ts +33 -1
- package/src/Tree/index.ts +6 -1
- package/src/Tree/types.ts +54 -12
- package/src/Tree/utils.ts +46 -4
- 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/hooks/index.ts +1 -0
- package/src/hooks/useTypeahead.ts +95 -0
- package/src/index.ts +10 -0
- package/src/test/intersectionObserverPolyfill.ts +7 -0
- package/src/test/resizeObserverPolyfill.ts +80 -0
- package/src/useLocalStorage/__tests__/useLocalStorage.reading.test.tsx +31 -0
- package/src/useLocalStorage/__tests__/useLocalStorage.resilience.test.tsx +97 -0
- package/src/useLocalStorage/__tests__/useLocalStorage.ssr.test.tsx +20 -0
- package/src/useLocalStorage/__tests__/useLocalStorage.writing.test.tsx +72 -0
- package/src/useLocalStorage/index.ts +1 -0
- package/src/useLocalStorage/useLocalStorage.ts +140 -0
- package/src/useMediaQuery/README.md +66 -0
- package/src/useMediaQuery/__tests__/useMediaQuery.matching.test.tsx +42 -0
- package/src/useMediaQuery/__tests__/useMediaQuery.ssr.test.tsx +19 -0
- package/src/useMediaQuery/__tests__/useMediaQuery.subscription.test.tsx +89 -0
- package/src/useMediaQuery/index.ts +1 -0
- package/src/useMediaQuery/useMediaQuery.ts +49 -0
- package/src/usePagination/README.md +170 -0
- package/src/usePagination/__tests__/usePagination.navigation.test.tsx +109 -0
- package/src/usePagination/__tests__/usePagination.page-count.test.tsx +56 -0
- package/src/usePagination/__tests__/usePagination.page-state.test.tsx +87 -0
- package/src/usePagination/__tests__/usePagination.range.test.tsx +82 -0
- package/src/usePagination/__tests__/usePagination.slice.test.tsx +59 -0
- package/src/usePagination/__tests__/usePagination.windowing.test.tsx +68 -0
- package/src/usePagination/index.ts +1 -0
- package/src/usePagination/usePagination.ts +292 -0
- package/src/utils/focusable.ts +15 -0
- package/src/utils/getKeyToActionMap.ts +1 -1
- package/src/utils/index.ts +1 -0
package/src/Tree/Tree.tsx
CHANGED
|
@@ -36,11 +36,39 @@ import type {
|
|
|
36
36
|
* `<div role="tree">` and provides every descendant with the expansion
|
|
37
37
|
* set, selection set, and roving-tabstop coordination.
|
|
38
38
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
39
|
+
* **State ownership.** Root owns two independent axes, each controllable
|
|
40
|
+
* or uncontrolled:
|
|
41
|
+
*
|
|
42
|
+
* - **Expansion** — which branches are open. Controlled via
|
|
43
|
+
* {@link TreeRootControlledExpansionProps.expandedValues | `expandedValues`}
|
|
44
|
+
* + {@link TreeRootControlledExpansionProps.onExpandedChange | `onExpandedChange`};
|
|
45
|
+
* uncontrolled via
|
|
46
|
+
* {@link TreeRootUncontrolledExpansionProps.defaultExpandedValues | `defaultExpandedValues`}.
|
|
47
|
+
* - **Selection** — which node(s) are selected, gated by
|
|
48
|
+
* {@link SelectionMode | `selectionMode`} (`"single"` default, or
|
|
49
|
+
* `"multiple"`). Each mode has its own controlled / uncontrolled
|
|
50
|
+
* `value` / `defaultValue` / `onChange` prop trio, statically
|
|
51
|
+
* discriminated so passing the wrong prop pair for the active mode is
|
|
52
|
+
* a type error.
|
|
53
|
+
*
|
|
54
|
+
* **Roving tabindex.** The whole tree exposes a **single tab stop** — at
|
|
55
|
+
* most one `treeitem` carries `tabIndex={0}` (the last-focused visible
|
|
56
|
+
* item, else the first non-disabled item in visible depth-first order);
|
|
57
|
+
* every other item is `tabIndex={-1}`. `Tab` therefore enters and leaves
|
|
58
|
+
* the tree once, and the arrow keys move focus *within* it. See
|
|
59
|
+
* {@link TreeItem} / {@link TreeBranch} for the full key table.
|
|
60
|
+
*
|
|
61
|
+
* **Context.** Root provides the `TreeContext` (expansion/selection
|
|
62
|
+
* state, the node registry, roving bookkeeping, path resolver) and seeds
|
|
63
|
+
* the `TreeLevelContext` at `{ depth: 0, parentValue: null }`, which each
|
|
64
|
+
* {@link TreeBranchContent} deepens for its children.
|
|
65
|
+
*
|
|
66
|
+
* **Styling hooks.**
|
|
67
|
+
* - `role="tree"` on the rendered container.
|
|
68
|
+
* - `aria-multiselectable="true"` only when `selectionMode="multiple"`.
|
|
69
|
+
* - `data-selection-mode="single" | "multiple"`.
|
|
70
|
+
*
|
|
71
|
+
* @extends HTMLDivElement
|
|
44
72
|
*
|
|
45
73
|
* @example Uncontrolled, single selection
|
|
46
74
|
* ```tsx
|
|
@@ -60,7 +88,7 @@ import type {
|
|
|
60
88
|
* selectionMode="multiple"
|
|
61
89
|
* onSelectedValuesChange={(values) => console.log(values)}
|
|
62
90
|
* >
|
|
63
|
-
* {
|
|
91
|
+
* {...}
|
|
64
92
|
* </Tree.Root>
|
|
65
93
|
* ```
|
|
66
94
|
*/
|
|
@@ -120,18 +148,57 @@ export function TreeRoot(props: TreeRootProps): ReactElement {
|
|
|
120
148
|
TreeRoot.displayName = "TreeRoot";
|
|
121
149
|
|
|
122
150
|
/**
|
|
123
|
-
* A leaf treeitem — a selectable, focusable node
|
|
124
|
-
* Renders a `<div role="treeitem">` by default; pass
|
|
125
|
-
*
|
|
151
|
+
* A leaf treeitem — a selectable, focusable node with no children.
|
|
152
|
+
* Renders a `<div role="treeitem">` by default; pass
|
|
153
|
+
* {@link TreeItemProps.asChild | `asChild`} to merge the tree behaviour
|
|
154
|
+
* onto a consumer element instead (e.g. an `<a>` for a navigable file).
|
|
155
|
+
*
|
|
156
|
+
* **Selection.** Clicking the item *replaces* the selection in single
|
|
157
|
+
* mode. In multiple mode, `Ctrl`/`Cmd`+click toggles it into or out of
|
|
158
|
+
* the set, and `Shift`+click selects the contiguous range (in visible
|
|
159
|
+
* depth-first order, disabled nodes excluded) between the previous
|
|
160
|
+
* selection anchor and this item.
|
|
161
|
+
*
|
|
162
|
+
* **Depth & registry.** The item reads its nesting depth and enclosing
|
|
163
|
+
* branch from `TreeLevelContext`, emits `aria-level={depth + 1}`, and
|
|
164
|
+
* registers itself (value, {@link TreeItemProps.label | `label`}, depth,
|
|
165
|
+
* parent) in the tree's node registry. The registry keeps the last-seen
|
|
166
|
+
* entry per value even after unmount, so `Tree.SelectionPath` and
|
|
167
|
+
* {@link useTreePath} can resolve ancestry through a collapsed branch.
|
|
168
|
+
*
|
|
169
|
+
* **Keyboard** (while focused; movement is over *visible* items in
|
|
170
|
+
* depth-first order, skipping collapsed subtrees and disabled nodes):
|
|
126
171
|
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
172
|
+
* | Key | Behaviour |
|
|
173
|
+
* | ----------------------- | --------------------------------------------- |
|
|
174
|
+
* | `ArrowDown` / `ArrowUp` | Move focus to the next / previous visible item |
|
|
175
|
+
* | `ArrowLeft` | Move focus to the parent branch (no-op at root)|
|
|
176
|
+
* | `Home` / `End` | Focus the first / last visible item |
|
|
177
|
+
* | `Enter` / `Space` | Select the item |
|
|
178
|
+
*
|
|
179
|
+
* A `disabled` item is removed from the roving order and ignores
|
|
180
|
+
* selection, but `Home`/`End` still land on non-disabled neighbours.
|
|
181
|
+
*
|
|
182
|
+
* **Styling hooks.**
|
|
183
|
+
* - `role="treeitem"`, `aria-level`, `aria-selected`, and `aria-disabled`
|
|
184
|
+
* (omitted when not disabled).
|
|
185
|
+
* - `data-leaf=""` (always), `data-depth="{n}"`.
|
|
186
|
+
* - `data-selected=""` when selected, `data-disabled=""` when disabled
|
|
187
|
+
* (both omitted otherwise).
|
|
188
|
+
*
|
|
189
|
+
* @extends HTMLDivElement
|
|
130
190
|
*
|
|
131
191
|
* @example
|
|
132
192
|
* ```tsx
|
|
133
193
|
* <Tree.Item value="readme">readme</Tree.Item>
|
|
134
194
|
* ```
|
|
195
|
+
*
|
|
196
|
+
* @example asChild — a navigable leaf
|
|
197
|
+
* ```tsx
|
|
198
|
+
* <Tree.Item asChild value="readme">
|
|
199
|
+
* <a href="/readme">readme</a>
|
|
200
|
+
* </Tree.Item>
|
|
201
|
+
* ```
|
|
135
202
|
*/
|
|
136
203
|
export function TreeItem({
|
|
137
204
|
value,
|
|
@@ -212,10 +279,43 @@ TreeItem.displayName = "TreeItem";
|
|
|
212
279
|
* with `partitionBranchChildren`.
|
|
213
280
|
*
|
|
214
281
|
* The branch carries `aria-expanded`, `aria-selected`, `aria-level`,
|
|
215
|
-
* and is labelled by its control row via `aria-labelledby`
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
282
|
+
* and is labelled by its control row via `aria-labelledby` (an id
|
|
283
|
+
* derived from the root id + this branch's `value`), so its accessible
|
|
284
|
+
* name is the control text alone and not polluted by descendant text.
|
|
285
|
+
* Content is unmounted while collapsed unless the inner
|
|
286
|
+
* {@link TreeBranchContent} opts in to
|
|
287
|
+
* {@link TreeBranchContentProps.forceMount | `forceMount`}.
|
|
288
|
+
*
|
|
289
|
+
* **Composition.** A `Tree.Branch` must contain exactly one
|
|
290
|
+
* {@link TreeBranchControl} and at most one {@link TreeBranchContent};
|
|
291
|
+
* both are matched however deeply they are wrapped in fragments (so
|
|
292
|
+
* `{open && <BranchContent/>}` partitions the same as a bare element).
|
|
293
|
+
* Violating that throws a descriptive error during render. The branch
|
|
294
|
+
* provides `TreeItemContext` (its value, expanded/disabled state, and
|
|
295
|
+
* control id) to those two sub-components.
|
|
296
|
+
*
|
|
297
|
+
* **Keyboard** (adds branch semantics on top of the roving keys shared
|
|
298
|
+
* with {@link TreeItem}):
|
|
299
|
+
*
|
|
300
|
+
* | Key | Behaviour |
|
|
301
|
+
* | ------------ | ------------------------------------------------------------- |
|
|
302
|
+
* | `ArrowRight` | Expand if collapsed; if already open, move focus to first child|
|
|
303
|
+
* | `ArrowLeft` | Collapse if open; otherwise move focus to the parent branch |
|
|
304
|
+
* | `Enter` / `Space` | Toggle expansion **and** select the branch in one gesture |
|
|
305
|
+
*
|
|
306
|
+
* A `disabled` branch keeps its row and current content rendered but
|
|
307
|
+
* ignores expansion, selection, and the branch arrow keys, and is
|
|
308
|
+
* skipped by roving navigation.
|
|
309
|
+
*
|
|
310
|
+
* **Styling hooks.**
|
|
311
|
+
* - `role="treeitem"`, `aria-level`, `aria-expanded`, `aria-selected`,
|
|
312
|
+
* `aria-labelledby`, and `aria-disabled` (omitted when not disabled).
|
|
313
|
+
* - `data-branch=""` (always), `data-depth="{n}"`,
|
|
314
|
+
* `data-state="open" | "closed"`.
|
|
315
|
+
* - `data-selected=""` / `data-disabled=""` when applicable (omitted
|
|
316
|
+
* otherwise).
|
|
317
|
+
*
|
|
318
|
+
* @extends HTMLDivElement
|
|
219
319
|
*
|
|
220
320
|
* @example
|
|
221
321
|
* ```tsx
|
|
@@ -307,11 +407,21 @@ export function TreeBranch({
|
|
|
307
407
|
TreeBranch.displayName = "TreeBranch";
|
|
308
408
|
|
|
309
409
|
/**
|
|
310
|
-
* The clickable row of a `Tree.Branch
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* the
|
|
410
|
+
* The clickable row of a {@link TreeBranch | `Tree.Branch`}. Renders a
|
|
411
|
+
* `<div>` by default, or any element via
|
|
412
|
+
* {@link TreeBranchControlProps.asChild | `asChild`}. It carries the
|
|
413
|
+
* `id` that the surrounding branch points `aria-labelledby` at, so its
|
|
414
|
+
* text becomes the branch's accessible name — keep the label content
|
|
415
|
+
* here rather than on the branch.
|
|
416
|
+
*
|
|
417
|
+
* Clicking the row toggles the branch's expansion **and** selects it in
|
|
418
|
+
* a single gesture (with the same `Ctrl`/`Cmd` / `Shift` modifier rules
|
|
419
|
+
* as {@link TreeItem}); the inner {@link TreeBranchIndicator} is purely
|
|
420
|
+
* decorative. A disabled branch's control ignores the click. Reads its
|
|
421
|
+
* value / disabled state / id from the branch's `TreeItemContext`, so it
|
|
422
|
+
* must be rendered inside a `Tree.Branch`.
|
|
423
|
+
*
|
|
424
|
+
* @extends HTMLDivElement
|
|
315
425
|
*
|
|
316
426
|
* @example
|
|
317
427
|
* ```tsx
|
|
@@ -356,13 +466,30 @@ export function TreeBranchControl({
|
|
|
356
466
|
TreeBranchControl.displayName = "TreeBranchControl";
|
|
357
467
|
|
|
358
468
|
/**
|
|
359
|
-
* The nested group of items inside a `Tree.Branch
|
|
360
|
-
* `<div role="group">`
|
|
469
|
+
* The nested group of items inside a {@link TreeBranch | `Tree.Branch`}.
|
|
470
|
+
* Renders a `<div role="group">` and deepens the `TreeLevelContext` for
|
|
471
|
+
* its children (`depth + 1`, `parentValue` = the enclosing branch), so
|
|
472
|
+
* they emit the correct `aria-level` and register under the right parent.
|
|
473
|
+
*
|
|
474
|
+
* By default the content is **unmounted** while the branch is collapsed —
|
|
475
|
+
* the lean-DOM strategy. Pass
|
|
476
|
+
* {@link TreeBranchContentProps.forceMount | `forceMount`} to keep it
|
|
477
|
+
* mounted while collapsed with `aria-hidden="true"` and
|
|
478
|
+
* `data-state="closed"`, so CSS can animate it in and out without the
|
|
479
|
+
* browser tearing the subtree down.
|
|
480
|
+
*
|
|
481
|
+
* **Styling hooks.**
|
|
482
|
+
* - `role="group"`, `data-depth="{n}"`, `data-state="open" | "closed"`.
|
|
483
|
+
* - `aria-hidden="true"` only while force-mounted and collapsed.
|
|
361
484
|
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
485
|
+
* @extends HTMLDivElement
|
|
486
|
+
*
|
|
487
|
+
* @example forceMount for CSS transitions
|
|
488
|
+
* ```tsx
|
|
489
|
+
* <Tree.BranchContent forceMount>
|
|
490
|
+
* <Tree.Item value="index">index.ts</Tree.Item>
|
|
491
|
+
* </Tree.BranchContent>
|
|
492
|
+
* ```
|
|
366
493
|
*/
|
|
367
494
|
export function TreeBranchContent({
|
|
368
495
|
children,
|
|
@@ -398,10 +525,15 @@ TreeBranchContent.displayName = "TreeBranchContent";
|
|
|
398
525
|
* `"closed"` so a consumer's CSS can rotate or swap the glyph. Only
|
|
399
526
|
* meaningful inside a `Tree.BranchControl`.
|
|
400
527
|
*
|
|
401
|
-
* Pass `asChild` to merge the
|
|
402
|
-
* `aria-hidden` and `data-state` — onto the
|
|
403
|
-
* This is useful when passing an icon component
|
|
404
|
-
* `data-state` directly rather than inheriting it from
|
|
528
|
+
* Pass {@link TreeBranchIndicatorProps.asChild | `asChild`} to merge the
|
|
529
|
+
* indicator's props — including `aria-hidden` and `data-state` — onto the
|
|
530
|
+
* supplied child element. This is useful when passing an icon component
|
|
531
|
+
* that must receive `data-state` directly rather than inheriting it from
|
|
532
|
+
* a wrapper. Reads its `expanded` state from the branch's
|
|
533
|
+
* `TreeItemContext`, so it must be rendered inside a
|
|
534
|
+
* {@link TreeBranchControl}.
|
|
535
|
+
*
|
|
536
|
+
* @extends HTMLSpanElement
|
|
405
537
|
*
|
|
406
538
|
* @example Default (wraps children in a `<span>`)
|
|
407
539
|
* ```tsx
|
|
@@ -456,9 +588,24 @@ TreeBranchIndicator.displayName = "TreeBranchIndicator";
|
|
|
456
588
|
*
|
|
457
589
|
* Pass `separator` to customise the divider glyph between segments.
|
|
458
590
|
*
|
|
459
|
-
* Pass
|
|
460
|
-
*
|
|
461
|
-
*
|
|
591
|
+
* Pass {@link TreeSelectionPathProps.separator | `separator`} to
|
|
592
|
+
* customise the divider glyph between segments, or a function as
|
|
593
|
+
* `children` to take full control of rendering (e.g. wire up router
|
|
594
|
+
* links). The function receives the resolved
|
|
595
|
+
* {@link TreeSelectionPathRenderProps.paths | `paths`} and replaces the
|
|
596
|
+
* default markup entirely.
|
|
597
|
+
*
|
|
598
|
+
* Backed by {@link useTreeSelectionPaths}, which reads ancestry from the
|
|
599
|
+
* persistent node registry — so trails stay resolvable even when an
|
|
600
|
+
* ancestor branch has collapsed and its descendants have unmounted.
|
|
601
|
+
*
|
|
602
|
+
* **Styling hooks.**
|
|
603
|
+
* - `data-tree-selection-path=""` on the wrapper (always).
|
|
604
|
+
* - `data-empty=""` on the wrapper when nothing is selected.
|
|
605
|
+
* - `data-tree-selection-segment=""` + `data-value` (and `data-disabled`
|
|
606
|
+
* when the node is disabled) on each rendered segment.
|
|
607
|
+
*
|
|
608
|
+
* @extends HTMLDivElement
|
|
462
609
|
*
|
|
463
610
|
* @example Default rendering
|
|
464
611
|
* ```tsx
|
|
@@ -600,6 +747,13 @@ export type TreeCompound = typeof TreeRoot & {
|
|
|
600
747
|
* </Tree.Branch>
|
|
601
748
|
* </Tree.Root>;
|
|
602
749
|
* ```
|
|
750
|
+
*
|
|
751
|
+
* @see {@link TreeRoot} for the expansion / selection state modes and the
|
|
752
|
+
* single tree-wide roving tab stop.
|
|
753
|
+
* @see {@link TreeItem} and {@link TreeBranch} for the keyboard tables and
|
|
754
|
+
* selection modifiers.
|
|
755
|
+
* @see {@link useTreePath} / {@link useTreeSelectionPaths} for reading a
|
|
756
|
+
* node's ancestry outside `Tree.SelectionPath`.
|
|
603
757
|
*/
|
|
604
758
|
const TreeCompound: TreeCompound = Object.assign(TreeRoot, {
|
|
605
759
|
Root: TreeRoot,
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import type { ComponentPropsWithRef } from "react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
import { Tree, TREE_PART, useTreeLevel } from "../index";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A styled layer wraps every part in its own component to attach classes — the
|
|
9
|
+
* shape `primitiv add tree` copies into a consumer repo. `Tree.Branch` finds its
|
|
10
|
+
* control and content by inspecting its children, so those wrappers have to be
|
|
11
|
+
* recognisable as the parts they render; without that, Tree is the one primitive
|
|
12
|
+
* in the library that cannot be wrapped at all.
|
|
13
|
+
*/
|
|
14
|
+
function StyledBranchControl(props: ComponentPropsWithRef<typeof Tree.BranchControl>) {
|
|
15
|
+
return <Tree.BranchControl {...props} className="styled-control" />;
|
|
16
|
+
}
|
|
17
|
+
StyledBranchControl[TREE_PART] = "branch-control";
|
|
18
|
+
|
|
19
|
+
function StyledBranchContent(props: ComponentPropsWithRef<typeof Tree.BranchContent>) {
|
|
20
|
+
return <Tree.BranchContent {...props} className="styled-content" />;
|
|
21
|
+
}
|
|
22
|
+
StyledBranchContent[TREE_PART] = "branch-content";
|
|
23
|
+
|
|
24
|
+
describe("Tree — styled wrappers", () => {
|
|
25
|
+
it("accepts a wrapper component in place of Tree.BranchControl", () => {
|
|
26
|
+
// Arrange / Act
|
|
27
|
+
render(
|
|
28
|
+
<Tree.Root defaultExpandedValues={["src"]}>
|
|
29
|
+
<Tree.Branch value="src">
|
|
30
|
+
<StyledBranchControl>src</StyledBranchControl>
|
|
31
|
+
<StyledBranchContent>
|
|
32
|
+
<Tree.Item value="index.ts">index.ts</Tree.Item>
|
|
33
|
+
</StyledBranchContent>
|
|
34
|
+
</Tree.Branch>
|
|
35
|
+
</Tree.Root>,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Assert
|
|
39
|
+
expect(screen.getByText("src")).toHaveClass("styled-control");
|
|
40
|
+
expect(screen.getByRole("group")).toHaveClass("styled-content");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("labels the branch by its wrapped control, so the name is just the row", () => {
|
|
44
|
+
// Arrange / Act
|
|
45
|
+
render(
|
|
46
|
+
<Tree.Root defaultExpandedValues={["src"]}>
|
|
47
|
+
<Tree.Branch value="src">
|
|
48
|
+
<StyledBranchControl>src</StyledBranchControl>
|
|
49
|
+
<StyledBranchContent>
|
|
50
|
+
<Tree.Item value="index.ts">index.ts</Tree.Item>
|
|
51
|
+
</StyledBranchContent>
|
|
52
|
+
</Tree.Branch>
|
|
53
|
+
</Tree.Root>,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Assert — the branch treeitem takes its accessible name from the control
|
|
57
|
+
// row alone, not from the whole subtree.
|
|
58
|
+
expect(screen.getByRole("treeitem", { name: "src" })).toHaveAttribute(
|
|
59
|
+
"aria-expanded",
|
|
60
|
+
"true",
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("reads forceMount from the wrapper element, not from what it renders", () => {
|
|
65
|
+
// A styled layer animates the open/close transition, which needs the group
|
|
66
|
+
// to stay mounted while closed. Tree.Branch reads `forceMount` off the
|
|
67
|
+
// element it is handed, so setting it *inside* the wrapper is invisible —
|
|
68
|
+
// the subtree unmounts and there is nothing left to animate. This is the
|
|
69
|
+
// contract the registry surface depends on.
|
|
70
|
+
|
|
71
|
+
// Arrange / Act — closed, with forceMount on the wrapper element
|
|
72
|
+
const { rerender } = render(
|
|
73
|
+
<Tree.Root>
|
|
74
|
+
<Tree.Branch value="src">
|
|
75
|
+
<StyledBranchControl>src</StyledBranchControl>
|
|
76
|
+
<StyledBranchContent forceMount>
|
|
77
|
+
<Tree.Item value="index.ts">index.ts</Tree.Item>
|
|
78
|
+
</StyledBranchContent>
|
|
79
|
+
</Tree.Branch>
|
|
80
|
+
</Tree.Root>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Assert — still in the DOM, marked closed and hidden from AT
|
|
84
|
+
const group = document.querySelector('[role="group"]');
|
|
85
|
+
expect(group).toHaveAttribute("data-state", "closed");
|
|
86
|
+
expect(group).toHaveAttribute("aria-hidden", "true");
|
|
87
|
+
|
|
88
|
+
// Act — without it, the branch drops the subtree entirely
|
|
89
|
+
rerender(
|
|
90
|
+
<Tree.Root>
|
|
91
|
+
<Tree.Branch value="src">
|
|
92
|
+
<StyledBranchControl>src</StyledBranchControl>
|
|
93
|
+
<StyledBranchContent>
|
|
94
|
+
<Tree.Item value="index.ts">index.ts</Tree.Item>
|
|
95
|
+
</StyledBranchContent>
|
|
96
|
+
</Tree.Branch>
|
|
97
|
+
</Tree.Root>,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Assert
|
|
101
|
+
expect(document.querySelector('[role="group"]')).toBeNull();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("still rejects two controls when both are wrappers", () => {
|
|
105
|
+
// Arrange / Act / Assert
|
|
106
|
+
expect(() =>
|
|
107
|
+
render(
|
|
108
|
+
<Tree.Root>
|
|
109
|
+
<Tree.Branch value="src">
|
|
110
|
+
<StyledBranchControl>one</StyledBranchControl>
|
|
111
|
+
<StyledBranchControl>two</StyledBranchControl>
|
|
112
|
+
</Tree.Branch>
|
|
113
|
+
</Tree.Root>,
|
|
114
|
+
),
|
|
115
|
+
).toThrow("A Tree.Branch may contain at most one <Tree.BranchControl>.");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("useTreeLevel", () => {
|
|
120
|
+
function Depth() {
|
|
121
|
+
const { depth } = useTreeLevel();
|
|
122
|
+
return <span data-testid={`depth-${depth}`}>{depth}</span>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
it("reports the nesting depth so a styled layer can indent without attr()", () => {
|
|
126
|
+
// A styled layer needs the depth as a NUMBER to compute a row's indent.
|
|
127
|
+
// Reading it back off `data-depth` in CSS needs `attr(... type(<integer>))`,
|
|
128
|
+
// which Firefox does not support — so the depth is exposed here instead.
|
|
129
|
+
|
|
130
|
+
// Arrange / Act
|
|
131
|
+
render(
|
|
132
|
+
<Tree.Root defaultExpandedValues={["a", "b"]}>
|
|
133
|
+
<Depth />
|
|
134
|
+
<Tree.Branch value="a">
|
|
135
|
+
<StyledBranchControl>a</StyledBranchControl>
|
|
136
|
+
<StyledBranchContent>
|
|
137
|
+
<Depth />
|
|
138
|
+
<Tree.Branch value="b">
|
|
139
|
+
<StyledBranchControl>b</StyledBranchControl>
|
|
140
|
+
<StyledBranchContent>
|
|
141
|
+
<Depth />
|
|
142
|
+
</StyledBranchContent>
|
|
143
|
+
</Tree.Branch>
|
|
144
|
+
</StyledBranchContent>
|
|
145
|
+
</Tree.Branch>
|
|
146
|
+
</Tree.Root>,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Assert — 0 at the root, one deeper per BranchContent
|
|
150
|
+
expect(screen.getByTestId("depth-0")).toBeInTheDocument();
|
|
151
|
+
expect(screen.getByTestId("depth-1")).toBeInTheDocument();
|
|
152
|
+
expect(screen.getByTestId("depth-2")).toBeInTheDocument();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("throws outside a Tree", () => {
|
|
156
|
+
// Arrange / Act / Assert
|
|
157
|
+
expect(() => render(<Depth />)).toThrow();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useTreeContext } from "../TreeContext";
|
|
1
|
+
import { useTreeContext, useTreeLevelContext } from "../TreeContext";
|
|
2
2
|
|
|
3
3
|
import type { TreePathSegment } from "../types";
|
|
4
4
|
|
|
@@ -66,3 +66,35 @@ export function useTreeSelectionPaths(): TreePathSegment[][] {
|
|
|
66
66
|
const { selectedOrder, getPath } = useTreeContext();
|
|
67
67
|
return selectedOrder.map((value) => getPath(value));
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Read the current nesting depth inside a `Tree` — `0` for a node directly
|
|
72
|
+
* under {@link TreeRoot | `Tree.Root`}, one deeper per enclosing
|
|
73
|
+
* {@link TreeBranchContent | `Tree.BranchContent`}.
|
|
74
|
+
*
|
|
75
|
+
* A styling layer needs the depth as a *number* to compute a row's indent, and
|
|
76
|
+
* so to make the row span the tree's full width (a hover or selected band that
|
|
77
|
+
* runs edge to edge, as VS Code and most file trees do). Reading it back off
|
|
78
|
+
* the `data-depth` attribute in CSS requires `attr(data-depth type(<integer>))`,
|
|
79
|
+
* which Firefox does not support — hence this hook.
|
|
80
|
+
*
|
|
81
|
+
* @throws If called outside a `Tree.Root`.
|
|
82
|
+
*
|
|
83
|
+
* @example Indenting a row so its background still spans the full width
|
|
84
|
+
* ```tsx
|
|
85
|
+
* function StyledItem(props: TreeItemProps) {
|
|
86
|
+
* const { depth } = useTreeLevel();
|
|
87
|
+
* return (
|
|
88
|
+
* <Tree.Item
|
|
89
|
+
* {...props}
|
|
90
|
+
* style={{ "--depth": depth } as CSSProperties}
|
|
91
|
+
* className="row"
|
|
92
|
+
* />
|
|
93
|
+
* );
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export function useTreeLevel(): { depth: number } {
|
|
98
|
+
const { depth } = useTreeLevelContext();
|
|
99
|
+
return { depth };
|
|
100
|
+
}
|
package/src/Tree/index.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export * from "./Tree";
|
|
2
2
|
export * from "./types";
|
|
3
|
-
export {
|
|
3
|
+
export { TREE_PART, type TreePartName } from "./utils";
|
|
4
|
+
export {
|
|
5
|
+
useTreeLevel,
|
|
6
|
+
useTreePath,
|
|
7
|
+
useTreeSelectionPaths,
|
|
8
|
+
} from "./hooks/useTreePath";
|
package/src/Tree/types.ts
CHANGED
|
@@ -7,26 +7,37 @@ export type TreeRootBaseProps = ComponentProps<"div"> & {
|
|
|
7
7
|
|
|
8
8
|
/** Uncontrolled expansion — initial expanded branches via `defaultExpandedValues`. */
|
|
9
9
|
export type TreeRootUncontrolledExpansionProps = {
|
|
10
|
-
/** Branch values expanded on first render when uncontrolled.
|
|
10
|
+
/** Branch values expanded on first render when uncontrolled. Omit for a
|
|
11
|
+
* fully-collapsed tree. */
|
|
11
12
|
defaultExpandedValues?: string[];
|
|
12
13
|
expandedValues?: never;
|
|
14
|
+
/** Called with the full set of expanded branch values whenever a branch
|
|
15
|
+
* is toggled. Optional in uncontrolled mode. */
|
|
13
16
|
onExpandedChange?: (values: string[]) => void;
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
/** Controlled expansion — the expanded set is owned by the consumer. */
|
|
17
20
|
export type TreeRootControlledExpansionProps = {
|
|
18
21
|
defaultExpandedValues?: never;
|
|
19
|
-
/** The set of expanded branch values, owned by the consumer.
|
|
22
|
+
/** The set of expanded branch values, owned by the consumer. Keep in
|
|
23
|
+
* sync via `onExpandedChange`. */
|
|
20
24
|
expandedValues: string[];
|
|
25
|
+
/** Called with the full set of expanded branch values the user requested
|
|
26
|
+
* on each toggle. Required in controlled mode. */
|
|
21
27
|
onExpandedChange: (values: string[]) => void;
|
|
22
28
|
};
|
|
23
29
|
|
|
24
30
|
/** Single-selection mode, uncontrolled — initial value via `defaultSelectedValue`. */
|
|
25
31
|
export type TreeRootSingleUncontrolledSelectionProps = {
|
|
32
|
+
/** At most one node may be selected; see {@link SelectionMode}.
|
|
33
|
+
* @default "single" */
|
|
26
34
|
selectionMode?: "single";
|
|
27
|
-
/** The value selected on first render when uncontrolled.
|
|
35
|
+
/** The value selected on first render when uncontrolled. `null` selects
|
|
36
|
+
* nothing. */
|
|
28
37
|
defaultSelectedValue?: string | null;
|
|
29
38
|
selectedValue?: never;
|
|
39
|
+
/** Called with the newly-selected value (or `null` when cleared) on each
|
|
40
|
+
* user selection. Optional in uncontrolled mode. */
|
|
30
41
|
onSelectedValueChange?: (value: string | null) => void;
|
|
31
42
|
defaultSelectedValues?: never;
|
|
32
43
|
selectedValues?: never;
|
|
@@ -35,10 +46,15 @@ export type TreeRootSingleUncontrolledSelectionProps = {
|
|
|
35
46
|
|
|
36
47
|
/** Single-selection mode, controlled — the selected value is owned by the consumer. */
|
|
37
48
|
export type TreeRootSingleControlledSelectionProps = {
|
|
49
|
+
/** At most one node may be selected; see {@link SelectionMode}.
|
|
50
|
+
* @default "single" */
|
|
38
51
|
selectionMode?: "single";
|
|
39
52
|
defaultSelectedValue?: never;
|
|
40
|
-
/** The selected value, owned by the consumer
|
|
53
|
+
/** The selected value, owned by the consumer (or `null` for no
|
|
54
|
+
* selection). Keep it in sync via `onSelectedValueChange`. */
|
|
41
55
|
selectedValue: string | null;
|
|
56
|
+
/** Called with the value the user requested to select (or `null` when
|
|
57
|
+
* cleared). Required in controlled mode. */
|
|
42
58
|
onSelectedValueChange: (value: string | null) => void;
|
|
43
59
|
defaultSelectedValues?: never;
|
|
44
60
|
selectedValues?: never;
|
|
@@ -47,10 +63,14 @@ export type TreeRootSingleControlledSelectionProps = {
|
|
|
47
63
|
|
|
48
64
|
/** Multiple-selection mode, uncontrolled — initial values via `defaultSelectedValues`. */
|
|
49
65
|
export type TreeRootMultipleUncontrolledSelectionProps = {
|
|
66
|
+
/** Many nodes may be selected via `Ctrl`/`Cmd`+click and `Shift`+click
|
|
67
|
+
* ranges; see {@link SelectionMode}. */
|
|
50
68
|
selectionMode: "multiple";
|
|
51
69
|
/** The values selected on first render when uncontrolled. */
|
|
52
70
|
defaultSelectedValues?: string[];
|
|
53
71
|
selectedValues?: never;
|
|
72
|
+
/** Called with the full selected set (in selection order) on each
|
|
73
|
+
* change. Optional in uncontrolled mode. */
|
|
54
74
|
onSelectedValuesChange?: (values: string[]) => void;
|
|
55
75
|
defaultSelectedValue?: never;
|
|
56
76
|
selectedValue?: never;
|
|
@@ -59,10 +79,15 @@ export type TreeRootMultipleUncontrolledSelectionProps = {
|
|
|
59
79
|
|
|
60
80
|
/** Multiple-selection mode, controlled — the selected values are owned by the consumer. */
|
|
61
81
|
export type TreeRootMultipleControlledSelectionProps = {
|
|
82
|
+
/** Many nodes may be selected via `Ctrl`/`Cmd`+click and `Shift`+click
|
|
83
|
+
* ranges; see {@link SelectionMode}. */
|
|
62
84
|
selectionMode: "multiple";
|
|
63
85
|
defaultSelectedValues?: never;
|
|
64
|
-
/** The selected values, owned by the consumer.
|
|
86
|
+
/** The selected values, owned by the consumer. Keep in sync via
|
|
87
|
+
* `onSelectedValuesChange`. */
|
|
65
88
|
selectedValues: string[];
|
|
89
|
+
/** Called with the full selected set (in selection order) the user
|
|
90
|
+
* requested. Required in controlled mode. */
|
|
66
91
|
onSelectedValuesChange: (values: string[]) => void;
|
|
67
92
|
defaultSelectedValue?: never;
|
|
68
93
|
selectedValue?: never;
|
|
@@ -86,7 +111,9 @@ export type TreeRootProps = TreeRootBaseProps &
|
|
|
86
111
|
|
|
87
112
|
/** Props for `Tree.Item` — a selectable, focusable leaf treeitem. */
|
|
88
113
|
export type TreeItemProps = ComponentProps<"div"> & {
|
|
89
|
-
/** Stable identifier for this item, unique within the tree.
|
|
114
|
+
/** Stable identifier for this item, unique within the tree. Links the
|
|
115
|
+
* item to selection / expansion state and to its
|
|
116
|
+
* {@link TreePathSegment} in a resolved path. */
|
|
90
117
|
value: string;
|
|
91
118
|
/**
|
|
92
119
|
* Optional display label for this item. Stored alongside the value
|
|
@@ -95,16 +122,23 @@ export type TreeItemProps = ComponentProps<"div"> & {
|
|
|
95
122
|
* Has no effect on what `Tree.Item` renders.
|
|
96
123
|
*/
|
|
97
124
|
label?: string;
|
|
98
|
-
/** Disable selection and remove the item from roving navigation
|
|
125
|
+
/** Disable selection and remove the item from roving navigation
|
|
126
|
+
* (`Home`/`End` still skip past it to a non-disabled neighbour).
|
|
127
|
+
* @default false */
|
|
99
128
|
disabled?: boolean;
|
|
100
|
-
/** Render the item as the supplied child element instead of `<div
|
|
129
|
+
/** Render the item as the supplied child element instead of `<div>`,
|
|
130
|
+
* merging the treeitem behaviour onto it via the {@link Slot} pattern.
|
|
131
|
+
* The child must accept a `ref`.
|
|
132
|
+
* @default false */
|
|
101
133
|
asChild?: boolean;
|
|
102
134
|
children: ReactNode;
|
|
103
135
|
};
|
|
104
136
|
|
|
105
137
|
/** Props for `Tree.Branch` — an expandable treeitem with nested children. */
|
|
106
138
|
export type TreeBranchProps = Omit<ComponentProps<"div">, "ref"> & {
|
|
107
|
-
/** Stable identifier for this branch, unique within the tree.
|
|
139
|
+
/** Stable identifier for this branch, unique within the tree. Also seeds
|
|
140
|
+
* the `parentValue` of its {@link TreeBranchContent}'s children and the
|
|
141
|
+
* derived `aria-labelledby` control id. */
|
|
108
142
|
value: string;
|
|
109
143
|
/**
|
|
110
144
|
* Optional display label for this branch. Stored alongside the value
|
|
@@ -116,6 +150,7 @@ export type TreeBranchProps = Omit<ComponentProps<"div">, "ref"> & {
|
|
|
116
150
|
/**
|
|
117
151
|
* Disable selection, expansion-toggling, and roving navigation for
|
|
118
152
|
* this branch. The branch and its current content remain rendered.
|
|
153
|
+
* @default false
|
|
119
154
|
*/
|
|
120
155
|
disabled?: boolean;
|
|
121
156
|
children: ReactNode;
|
|
@@ -123,7 +158,10 @@ export type TreeBranchProps = Omit<ComponentProps<"div">, "ref"> & {
|
|
|
123
158
|
|
|
124
159
|
/** Props for `Tree.BranchControl` — the branch's selectable, toggling control row. */
|
|
125
160
|
export type TreeBranchControlProps = ComponentProps<"div"> & {
|
|
126
|
-
/** Render the control as the supplied child element instead of `<div
|
|
161
|
+
/** Render the control as the supplied child element instead of `<div>`,
|
|
162
|
+
* merging its props (including the `aria-labelledby` id) onto the child
|
|
163
|
+
* via the {@link Slot} pattern. The child must accept a `ref`.
|
|
164
|
+
* @default false */
|
|
127
165
|
asChild?: boolean;
|
|
128
166
|
children: ReactNode;
|
|
129
167
|
};
|
|
@@ -134,14 +172,18 @@ export type TreeBranchContentProps = ComponentProps<"div"> & {
|
|
|
134
172
|
/**
|
|
135
173
|
* Keep the content mounted while the branch is collapsed so CSS can
|
|
136
174
|
* animate it in and out. When collapsed it is hidden from assistive
|
|
137
|
-
* technology with `aria-hidden`.
|
|
175
|
+
* technology with `aria-hidden` and carries `data-state="closed"`.
|
|
176
|
+
* @default false
|
|
138
177
|
*/
|
|
139
178
|
forceMount?: boolean;
|
|
140
179
|
};
|
|
141
180
|
|
|
142
181
|
/** Props for `Tree.BranchIndicator` — the expand/collapse affordance. */
|
|
143
182
|
export type TreeBranchIndicatorProps = ComponentProps<"span"> & {
|
|
144
|
-
/** Render as the supplied child element instead of `<span
|
|
183
|
+
/** Render as the supplied child element instead of `<span>`, merging
|
|
184
|
+
* `aria-hidden` and `data-state` onto it via the {@link Slot} pattern
|
|
185
|
+
* (e.g. an icon that rotates on `data-state="open"`).
|
|
186
|
+
* @default false */
|
|
145
187
|
asChild?: boolean;
|
|
146
188
|
};
|
|
147
189
|
|