@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
|
@@ -46,6 +46,8 @@ import { ToggleGroupItemProps, ToggleGroupRootProps } from "./types";
|
|
|
46
46
|
* element in place of the native `<div>`, with the group's `role`,
|
|
47
47
|
* `data-orientation`, and ref merged in.
|
|
48
48
|
*
|
|
49
|
+
* @extends HTMLDivElement
|
|
50
|
+
*
|
|
49
51
|
* @example Single mode — text alignment
|
|
50
52
|
* ```tsx
|
|
51
53
|
* <ToggleGroup.Root type="single" defaultValue="left" aria-label="Alignment">
|
|
@@ -140,6 +142,7 @@ export function ToggleGroupRoot({
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
/** @internal */
|
|
145
|
+
// Stryker disable next-line StringLiteral: overwritten by the compound alias — an equivalent mutant.
|
|
143
146
|
ToggleGroupRoot.displayName = "ToggleGroupRoot";
|
|
144
147
|
|
|
145
148
|
/**
|
|
@@ -165,9 +168,19 @@ ToggleGroupRoot.displayName = "ToggleGroupRoot";
|
|
|
165
168
|
*
|
|
166
169
|
* **`asChild` prop.** Pass `asChild` to render any consumer element in
|
|
167
170
|
* place of the native `<button>`, with the Item's `aria-pressed`,
|
|
168
|
-
* `data-state`, `tabIndex`, and event handlers merged onto it.
|
|
171
|
+
* `data-state`, `tabIndex`, and event handlers merged onto it. The child
|
|
172
|
+
* must accept a `ref`. Useful for a link or a styled control that needs
|
|
173
|
+
* toggle-button semantics.
|
|
169
174
|
*
|
|
170
175
|
* @throws if rendered outside a `ToggleGroup.Root`.
|
|
176
|
+
*
|
|
177
|
+
* @extends HTMLButtonElement
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```tsx
|
|
181
|
+
* <ToggleGroup.Item value="bold">Bold</ToggleGroup.Item>
|
|
182
|
+
* <ToggleGroup.Item value="strike" disabled>Strikethrough</ToggleGroup.Item>
|
|
183
|
+
* ```
|
|
171
184
|
*/
|
|
172
185
|
export function ToggleGroupItem({
|
|
173
186
|
value,
|
|
@@ -62,4 +62,24 @@ describe("ToggleGroup asChild composition", () => {
|
|
|
62
62
|
expect(consumerClick).toHaveBeenCalledOnce();
|
|
63
63
|
expect(item).toHaveAttribute("aria-pressed", "true");
|
|
64
64
|
});
|
|
65
|
+
|
|
66
|
+
it("Item asChild activates on Space through the keyboard handler", async () => {
|
|
67
|
+
// A non-button element fires no native click on Space, so toggling can
|
|
68
|
+
// only come from the roving handler's activate mapping — the guarantee a
|
|
69
|
+
// native <button> would otherwise mask.
|
|
70
|
+
const user = userEvent.setup();
|
|
71
|
+
render(
|
|
72
|
+
<ToggleGroup.Root type="single" aria-label="Alignment">
|
|
73
|
+
<ToggleGroup.Item value="left" asChild>
|
|
74
|
+
<div>Left</div>
|
|
75
|
+
</ToggleGroup.Item>
|
|
76
|
+
</ToggleGroup.Root>,
|
|
77
|
+
);
|
|
78
|
+
const item = screen
|
|
79
|
+
.getByLabelText("Alignment")
|
|
80
|
+
.querySelector<HTMLElement>("[data-state]")!;
|
|
81
|
+
item.focus();
|
|
82
|
+
await user.keyboard(" ");
|
|
83
|
+
expect(item).toHaveAttribute("aria-pressed", "true");
|
|
84
|
+
});
|
|
65
85
|
});
|
|
@@ -47,4 +47,13 @@ describe("ToggleGroup basic rendering", () => {
|
|
|
47
47
|
"horizontal",
|
|
48
48
|
);
|
|
49
49
|
});
|
|
50
|
+
|
|
51
|
+
it("sets a displayName on the compound and Item", () => {
|
|
52
|
+
// Assert — empty displayNames would render each as anonymous in DevTools.
|
|
53
|
+
// `ToggleGroup`, `ToggleGroup.Root`, and the underlying function are one
|
|
54
|
+
// object (Object.assign compound), so the compound's "ToggleGroup" alias is
|
|
55
|
+
// the observable Root displayName; only Item is a distinct object.
|
|
56
|
+
expect(ToggleGroup.displayName).toBe("ToggleGroup");
|
|
57
|
+
expect(ToggleGroup.Item.displayName).toBe("ToggleGroupItem");
|
|
58
|
+
});
|
|
50
59
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
|
|
4
|
+
import { ToggleGroup } from "../ToggleGroup";
|
|
5
|
+
|
|
6
|
+
function ItemsFixture({ items }: { items: string[] }) {
|
|
7
|
+
return (
|
|
8
|
+
<ToggleGroup.Root type="single" aria-label="Alignment">
|
|
9
|
+
{items.map((v) => (
|
|
10
|
+
<ToggleGroup.Item key={v} value={v}>
|
|
11
|
+
{v}
|
|
12
|
+
</ToggleGroup.Item>
|
|
13
|
+
))}
|
|
14
|
+
</ToggleGroup.Root>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function DisabledFixture({ disabledItem }: { disabledItem?: string }) {
|
|
19
|
+
return (
|
|
20
|
+
<ToggleGroup.Root type="single" aria-label="Alignment">
|
|
21
|
+
<ToggleGroup.Item value="left" disabled={disabledItem === "left"}>
|
|
22
|
+
Left
|
|
23
|
+
</ToggleGroup.Item>
|
|
24
|
+
<ToggleGroup.Item value="center" disabled={disabledItem === "center"}>
|
|
25
|
+
Center
|
|
26
|
+
</ToggleGroup.Item>
|
|
27
|
+
<ToggleGroup.Item value="right" disabled={disabledItem === "right"}>
|
|
28
|
+
Right
|
|
29
|
+
</ToggleGroup.Item>
|
|
30
|
+
</ToggleGroup.Root>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("ToggleGroup dynamic items — registration lifecycle", () => {
|
|
35
|
+
it("unregisters an unmounted item so the tab stop moves to the survivor", () => {
|
|
36
|
+
// Arrange — "a" is the first enabled item, so it holds the tab stop.
|
|
37
|
+
const { rerender } = render(<ItemsFixture items={["a", "b"]} />);
|
|
38
|
+
expect(screen.getByRole("button", { name: "a" })).toHaveAttribute(
|
|
39
|
+
"tabindex",
|
|
40
|
+
"0",
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Act — remove "a"; its effect cleanup must unregister it.
|
|
44
|
+
rerender(<ItemsFixture items={["b"]} />);
|
|
45
|
+
|
|
46
|
+
// Assert — with "a" gone from the registry, "b" is now the tab stop. A
|
|
47
|
+
// no-op cleanup would leave "a" registered ahead of "b", stranding the
|
|
48
|
+
// tab stop on the detached element and leaving "b" at tabindex -1.
|
|
49
|
+
expect(screen.getByRole("button", { name: "b" })).toHaveAttribute(
|
|
50
|
+
"tabindex",
|
|
51
|
+
"0",
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("re-registers an item's disabled flag when it changes after mount", async () => {
|
|
56
|
+
// Arrange — all three enabled; focus can travel Left → Center → Right.
|
|
57
|
+
const user = userEvent.setup();
|
|
58
|
+
const { rerender } = render(<DisabledFixture />);
|
|
59
|
+
|
|
60
|
+
// Act — disable Center after mount, then navigate rightward from Left.
|
|
61
|
+
rerender(<DisabledFixture disabledItem="center" />);
|
|
62
|
+
screen.getByRole("button", { name: "Left" }).focus();
|
|
63
|
+
await user.keyboard("{ArrowRight}");
|
|
64
|
+
|
|
65
|
+
// Assert — the registrar effect re-ran, so Center is skipped and focus
|
|
66
|
+
// lands on Right. A stale registration would keep Center navigable and
|
|
67
|
+
// strand focus (a disabled button cannot receive it).
|
|
68
|
+
expect(screen.getByRole("button", { name: "Right" })).toHaveFocus();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { ToggleGroup } from "../ToggleGroup";
|
|
4
|
+
import { ToggleGroupContext } from "../ToggleGroupContext";
|
|
5
|
+
|
|
6
|
+
describe("ToggleGroup error handling", () => {
|
|
7
|
+
it("throws a helpful error when ToggleGroup.Item is rendered outside Root", () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
const error = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
10
|
+
|
|
11
|
+
// Assert
|
|
12
|
+
expect(() =>
|
|
13
|
+
render(<ToggleGroup.Item value="left">Left</ToggleGroup.Item>),
|
|
14
|
+
).toThrow(/ToggleGroup\.Item must be rendered inside a ToggleGroup\.Root/);
|
|
15
|
+
|
|
16
|
+
error.mockRestore();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("names the context for React DevTools", () => {
|
|
20
|
+
// Assert — an empty displayName would render the provider as anonymous.
|
|
21
|
+
expect(ToggleGroupContext.displayName).toBe("ToggleGroupContext");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
1
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
2
|
import userEvent from "@testing-library/user-event";
|
|
3
3
|
|
|
4
4
|
import { ToggleGroup } from "../ToggleGroup";
|
|
@@ -45,6 +45,28 @@ describe("ToggleGroup keyboard — roving tabindex", () => {
|
|
|
45
45
|
"-1",
|
|
46
46
|
);
|
|
47
47
|
});
|
|
48
|
+
|
|
49
|
+
it("moves the tab stop to the focused item (onFocus updates the home base)", () => {
|
|
50
|
+
// Arrange — nothing selected, so the first item is the initial tab stop.
|
|
51
|
+
render(<SingleFixture />);
|
|
52
|
+
expect(screen.getByRole("button", { name: "Left" })).toHaveAttribute(
|
|
53
|
+
"tabindex",
|
|
54
|
+
"0",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Act — focusing another item should hand it the tab stop.
|
|
58
|
+
fireEvent.focus(screen.getByRole("button", { name: "Right" }));
|
|
59
|
+
|
|
60
|
+
// Assert — Right is now the sole tab stop; Left drops out of the sequence.
|
|
61
|
+
expect(screen.getByRole("button", { name: "Right" })).toHaveAttribute(
|
|
62
|
+
"tabindex",
|
|
63
|
+
"0",
|
|
64
|
+
);
|
|
65
|
+
expect(screen.getByRole("button", { name: "Left" })).toHaveAttribute(
|
|
66
|
+
"tabindex",
|
|
67
|
+
"-1",
|
|
68
|
+
);
|
|
69
|
+
});
|
|
48
70
|
});
|
|
49
71
|
|
|
50
72
|
describe("ToggleGroup keyboard — arrow key navigation (no auto-select)", () => {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { ToggleGroup } from "../ToggleGroup";
|
|
4
|
+
|
|
5
|
+
describe("ToggleGroup.Item ref forwarding", () => {
|
|
6
|
+
it("forwards the underlying button element to a consumer function ref", () => {
|
|
7
|
+
// Arrange
|
|
8
|
+
const received: (HTMLButtonElement | null)[] = [];
|
|
9
|
+
const functionRef = (node: HTMLButtonElement | null) => {
|
|
10
|
+
received.push(node);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Act
|
|
14
|
+
render(
|
|
15
|
+
<ToggleGroup.Root type="single" aria-label="Alignment">
|
|
16
|
+
<ToggleGroup.Item value="left" ref={functionRef}>
|
|
17
|
+
Left
|
|
18
|
+
</ToggleGroup.Item>
|
|
19
|
+
</ToggleGroup.Root>,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Assert
|
|
23
|
+
expect(received).toContain(screen.getByRole("button", { name: "Left" }));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("re-attaches to a replacement ref when the ref prop changes", () => {
|
|
27
|
+
// Arrange — the item's ref memo must depend on `ref`, or a changed ref
|
|
28
|
+
// callback is never invoked with the element.
|
|
29
|
+
const firstReceived: (HTMLButtonElement | null)[] = [];
|
|
30
|
+
const secondReceived: (HTMLButtonElement | null)[] = [];
|
|
31
|
+
const firstRef = (node: HTMLButtonElement | null) => {
|
|
32
|
+
firstReceived.push(node);
|
|
33
|
+
};
|
|
34
|
+
const secondRef = (node: HTMLButtonElement | null) => {
|
|
35
|
+
secondReceived.push(node);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Act — mount with the first ref, then re-render with a different one.
|
|
39
|
+
const { rerender } = render(
|
|
40
|
+
<ToggleGroup.Root type="single" aria-label="Alignment">
|
|
41
|
+
<ToggleGroup.Item value="left" ref={firstRef}>
|
|
42
|
+
Left
|
|
43
|
+
</ToggleGroup.Item>
|
|
44
|
+
</ToggleGroup.Root>,
|
|
45
|
+
);
|
|
46
|
+
rerender(
|
|
47
|
+
<ToggleGroup.Root type="single" aria-label="Alignment">
|
|
48
|
+
<ToggleGroup.Item value="left" ref={secondRef}>
|
|
49
|
+
Left
|
|
50
|
+
</ToggleGroup.Item>
|
|
51
|
+
</ToggleGroup.Root>,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Assert — the replacement ref received the button.
|
|
55
|
+
const button = screen.getByRole("button", { name: "Left" });
|
|
56
|
+
expect(secondReceived).toContain(button);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -42,7 +42,16 @@ export function useToggleGroupRoot({
|
|
|
42
42
|
(itemValue: string) => {
|
|
43
43
|
const isSelected = value.includes(itemValue);
|
|
44
44
|
if (type === "single") {
|
|
45
|
-
|
|
45
|
+
// Deselecting clears the selection to the empty array. Stryker's
|
|
46
|
+
// ArrayDeclaration mutant fills this with a phantom value, but since
|
|
47
|
+
// that value can never equal a registered item's `value` — and single
|
|
48
|
+
// mode always overwrites the whole array on the next selection — no
|
|
49
|
+
// item ever reads it as pressed. The mutation is observationally
|
|
50
|
+
// identical, so it is isolated here to keep the disable off the tested
|
|
51
|
+
// `[itemValue]` literal below.
|
|
52
|
+
// Stryker disable next-line ArrayDeclaration
|
|
53
|
+
const cleared: string[] = [];
|
|
54
|
+
const next = isSelected ? cleared : [itemValue];
|
|
46
55
|
setValue(next);
|
|
47
56
|
(onValueChange as ((v: string | undefined) => void) | undefined)?.(
|
|
48
57
|
isSelected ? undefined : itemValue,
|
|
@@ -68,6 +77,9 @@ export function useToggleGroupRoot({
|
|
|
68
77
|
(itemValue: string, element: HTMLButtonElement | null, disabled = false) => {
|
|
69
78
|
registerBase(itemValue, element ? { element, disabled } : null);
|
|
70
79
|
},
|
|
80
|
+
// `registerBase` is a stable useCollection callback (its own deps never
|
|
81
|
+
// change), so emptying this array yields the identical memoised function.
|
|
82
|
+
// Stryker disable next-line ArrayDeclaration: equivalent — stable dependency.
|
|
71
83
|
[registerBase],
|
|
72
84
|
);
|
|
73
85
|
|
|
@@ -83,8 +95,16 @@ export function useToggleGroupRoot({
|
|
|
83
95
|
|
|
84
96
|
const focusItem = useCallback(
|
|
85
97
|
(itemValue: string) => {
|
|
98
|
+
// `itemValue` is always sourced from `navigable` (the enabled subset of
|
|
99
|
+
// `itemValues`), and useCollection's register() writes `itemsRef` and the
|
|
100
|
+
// `itemValues` state together — so a value that reaches here is guaranteed
|
|
101
|
+
// to have a live entry in the map.
|
|
102
|
+
// Stryker disable next-line OptionalChaining: unreachable given that invariant.
|
|
86
103
|
itemsRef.current.get(itemValue)?.element.focus();
|
|
87
104
|
},
|
|
105
|
+
// `itemsRef` is a stable RefObject, so emptying this array yields the
|
|
106
|
+
// identical memoised function.
|
|
107
|
+
// Stryker disable next-line ArrayDeclaration: equivalent — stable dependency.
|
|
88
108
|
[itemsRef],
|
|
89
109
|
);
|
|
90
110
|
|
package/src/ToggleGroup/types.ts
CHANGED
|
@@ -7,12 +7,27 @@ import { HTMLAttributes, ButtonHTMLAttributes, ReactNode, Ref } from "react";
|
|
|
7
7
|
*/
|
|
8
8
|
export type ToggleGroupRootBaseProps = Omit<
|
|
9
9
|
HTMLAttributes<HTMLDivElement>,
|
|
10
|
-
"dir"
|
|
10
|
+
"dir" | "defaultValue"
|
|
11
11
|
> & {
|
|
12
|
+
/** Layout axis for keyboard navigation. `"horizontal"` binds
|
|
13
|
+
* ArrowLeft/ArrowRight; `"vertical"` binds ArrowUp/ArrowDown. Surfaces as
|
|
14
|
+
* `data-orientation` on the root for styling; it does not itself apply any
|
|
15
|
+
* flex/grid layout.
|
|
16
|
+
* @default "horizontal" */
|
|
12
17
|
orientation?: "horizontal" | "vertical";
|
|
18
|
+
/** Reading direction. In `"rtl"` the horizontal arrow keys are mirrored so
|
|
19
|
+
* focus follows the visual order. Inherited from the nearest
|
|
20
|
+
* {@link DirectionProvider} when omitted, falling back to `"ltr"`. */
|
|
13
21
|
dir?: "ltr" | "rtl";
|
|
22
|
+
/** Render a single consumer-supplied element in place of the native
|
|
23
|
+
* `<div>`, with the group's `role="group"`, `data-orientation`, and `ref`
|
|
24
|
+
* merged onto it via the {@link Slot} pattern.
|
|
25
|
+
* @default false */
|
|
14
26
|
asChild?: boolean;
|
|
27
|
+
/** The {@link ToggleGroupItemProps | `ToggleGroup.Item`} elements that make
|
|
28
|
+
* up the segmented control. */
|
|
15
29
|
children?: ReactNode;
|
|
30
|
+
/** Forwarded to the underlying `HTMLDivElement`. */
|
|
16
31
|
ref?: Ref<HTMLDivElement>;
|
|
17
32
|
};
|
|
18
33
|
|
|
@@ -21,9 +36,15 @@ export type ToggleGroupRootBaseProps = Omit<
|
|
|
21
36
|
* initial value supplied via `defaultValue`.
|
|
22
37
|
*/
|
|
23
38
|
export type SingleUncontrolledProps = {
|
|
39
|
+
/** Selects single-selection semantics: at most one item pressed at a time,
|
|
40
|
+
* and pressing the active item again clears the selection. */
|
|
24
41
|
type: "single";
|
|
42
|
+
/** Value of the item pressed on first render. Omit to start with nothing
|
|
43
|
+
* pressed. */
|
|
25
44
|
defaultValue?: string;
|
|
45
|
+
/** Forbidden in uncontrolled mode — use `defaultValue`. */
|
|
26
46
|
value?: never;
|
|
47
|
+
/** Forbidden in uncontrolled mode. */
|
|
27
48
|
onValueChange?: never;
|
|
28
49
|
};
|
|
29
50
|
|
|
@@ -32,9 +53,16 @@ export type SingleUncontrolledProps = {
|
|
|
32
53
|
* caller via `value` / `onValueChange`.
|
|
33
54
|
*/
|
|
34
55
|
export type SingleControlledProps = {
|
|
56
|
+
/** Selects single-selection semantics: at most one item pressed at a time,
|
|
57
|
+
* and pressing the active item again clears the selection. */
|
|
35
58
|
type: "single";
|
|
59
|
+
/** The currently pressed item's value, or `undefined` when none is pressed.
|
|
60
|
+
* Must be kept in sync by the caller via `onValueChange`. */
|
|
36
61
|
value: string | undefined;
|
|
62
|
+
/** Called with the requested next value — the pressed item's value, or
|
|
63
|
+
* `undefined` when the active item is pressed again to deselect it. */
|
|
37
64
|
onValueChange: (value: string | undefined) => void;
|
|
65
|
+
/** Forbidden in controlled mode — use `value`. */
|
|
38
66
|
defaultValue?: never;
|
|
39
67
|
};
|
|
40
68
|
|
|
@@ -43,9 +71,15 @@ export type SingleControlledProps = {
|
|
|
43
71
|
* the initial set supplied via `defaultValue`.
|
|
44
72
|
*/
|
|
45
73
|
export type MultipleUncontrolledProps = {
|
|
74
|
+
/** Selects multiple-selection semantics: any number of items can be pressed
|
|
75
|
+
* simultaneously and each toggles independently. */
|
|
46
76
|
type: "multiple";
|
|
77
|
+
/** Values of the items pressed on first render. Omit to start with nothing
|
|
78
|
+
* pressed. */
|
|
47
79
|
defaultValue?: string[];
|
|
80
|
+
/** Forbidden in uncontrolled mode — use `defaultValue`. */
|
|
48
81
|
value?: never;
|
|
82
|
+
/** Forbidden in uncontrolled mode. */
|
|
49
83
|
onValueChange?: never;
|
|
50
84
|
};
|
|
51
85
|
|
|
@@ -54,9 +88,16 @@ export type MultipleUncontrolledProps = {
|
|
|
54
88
|
* by the caller via `value` / `onValueChange`.
|
|
55
89
|
*/
|
|
56
90
|
export type MultipleControlledProps = {
|
|
91
|
+
/** Selects multiple-selection semantics: any number of items can be pressed
|
|
92
|
+
* simultaneously and each toggles independently. */
|
|
57
93
|
type: "multiple";
|
|
94
|
+
/** The full set of currently pressed item values. Must be kept in sync by
|
|
95
|
+
* the caller via `onValueChange`. */
|
|
58
96
|
value: string[];
|
|
97
|
+
/** Called with the complete next array of pressed values whenever the user
|
|
98
|
+
* toggles any item. */
|
|
59
99
|
onValueChange: (value: string[]) => void;
|
|
100
|
+
/** Forbidden in controlled mode — use `value`. */
|
|
60
101
|
defaultValue?: never;
|
|
61
102
|
};
|
|
62
103
|
|
|
@@ -81,11 +122,23 @@ export type ToggleGroupRootProps = ToggleGroupRootBaseProps &
|
|
|
81
122
|
*/
|
|
82
123
|
export type ToggleGroupItemProps = Omit<
|
|
83
124
|
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
84
|
-
"type"
|
|
125
|
+
"type" | "value"
|
|
85
126
|
> & {
|
|
127
|
+
/** Identifies this item within the group. It is this string — not the
|
|
128
|
+
* visible label — that is compared against the group's pressed
|
|
129
|
+
* `value` / `defaultValue`; use `children` for the label. */
|
|
86
130
|
value: string;
|
|
131
|
+
/** Forwards the native `disabled` attribute and removes the item from the
|
|
132
|
+
* roving tab order so arrow-key navigation skips it. Also sets
|
|
133
|
+
* `data-disabled=""` for CSS targeting.
|
|
134
|
+
* @default false */
|
|
87
135
|
disabled?: boolean;
|
|
136
|
+
/** Render a single consumer-supplied element in place of the native
|
|
137
|
+
* `<button>`, with the item's `aria-pressed`, `data-state`, `tabIndex`, and
|
|
138
|
+
* event handlers merged onto it via the {@link Slot} pattern.
|
|
139
|
+
* @default false */
|
|
88
140
|
asChild?: boolean;
|
|
141
|
+
/** Forwarded to the underlying `HTMLButtonElement`. */
|
|
89
142
|
ref?: Ref<HTMLButtonElement>;
|
|
90
143
|
};
|
|
91
144
|
|
package/src/Tooltip/README.md
CHANGED
|
@@ -60,12 +60,12 @@ The two shapes are statically discriminated at the type level.
|
|
|
60
60
|
|
|
61
61
|
```tsx
|
|
62
62
|
// Uncontrolled
|
|
63
|
-
<Tooltip.Root defaultOpen
|
|
63
|
+
<Tooltip.Root defaultOpen>...</Tooltip.Root>
|
|
64
64
|
|
|
65
65
|
// Controlled
|
|
66
66
|
const [open, setOpen] = useState(false);
|
|
67
67
|
<Tooltip.Root open={open} onOpenChange={setOpen}>
|
|
68
|
-
|
|
68
|
+
...
|
|
69
69
|
</Tooltip.Root>
|
|
70
70
|
```
|
|
71
71
|
|
|
@@ -82,7 +82,7 @@ before the normal delay resumes.
|
|
|
82
82
|
|
|
83
83
|
```tsx
|
|
84
84
|
<Tooltip.Provider delayDuration={400} skipDelayDuration={150}>
|
|
85
|
-
|
|
85
|
+
...
|
|
86
86
|
</Tooltip.Provider>
|
|
87
87
|
```
|
|
88
88
|
|
|
@@ -95,7 +95,7 @@ when the cursor leaves the trigger:
|
|
|
95
95
|
|
|
96
96
|
```tsx
|
|
97
97
|
<Tooltip.Root disableHoverableContent>
|
|
98
|
-
|
|
98
|
+
...
|
|
99
99
|
</Tooltip.Root>
|
|
100
100
|
```
|
|
101
101
|
|
|
@@ -114,7 +114,7 @@ tooltip open:
|
|
|
114
114
|
event.preventDefault(); // keep open when clicking outside
|
|
115
115
|
}}
|
|
116
116
|
>
|
|
117
|
-
|
|
117
|
+
...
|
|
118
118
|
</Tooltip.Content>
|
|
119
119
|
```
|
|
120
120
|
|
|
@@ -178,7 +178,7 @@ subtree stays in the DOM while closed so a CSS animation can play on
|
|
|
178
178
|
|
|
179
179
|
```tsx
|
|
180
180
|
<Tooltip.Portal forceMount>
|
|
181
|
-
<Tooltip.Content forceMount
|
|
181
|
+
<Tooltip.Content forceMount>...</Tooltip.Content>
|
|
182
182
|
</Tooltip.Portal>
|
|
183
183
|
```
|
|
184
184
|
|
|
@@ -199,7 +199,7 @@ child element; the child's own handlers run first.
|
|
|
199
199
|
</Tooltip.Trigger>
|
|
200
200
|
|
|
201
201
|
<Tooltip.Arrow asChild>
|
|
202
|
-
<svg viewBox="0 0 10 5"
|
|
202
|
+
<svg viewBox="0 0 10 5">...</svg>
|
|
203
203
|
</Tooltip.Arrow>
|
|
204
204
|
```
|
|
205
205
|
|
package/src/Tooltip/Tooltip.tsx
CHANGED
|
@@ -24,11 +24,27 @@ import type {
|
|
|
24
24
|
} from "./types";
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Wraps one or more `Tooltip.Root` instances and provides
|
|
28
|
-
* delay configuration
|
|
29
|
-
* without a Provider throws a context
|
|
27
|
+
* Wraps one or more {@link TooltipRoot | `Tooltip.Root`} instances and provides
|
|
28
|
+
* the shared delay configuration and group-wide open coordination. Required
|
|
29
|
+
* ancestor — rendering `Tooltip.Root` without a Provider throws a context
|
|
30
|
+
* error. Renders no DOM of its own (a `TooltipProviderContext` provider only).
|
|
30
31
|
*
|
|
31
|
-
*
|
|
32
|
+
* **Delay coordination.** Two timings govern how a group of tooltips feels:
|
|
33
|
+
*
|
|
34
|
+
* - `delayDuration` — how long a pointer must rest on a trigger before its
|
|
35
|
+
* tooltip opens (hover only; focus always opens immediately).
|
|
36
|
+
* - `skipDelayDuration` — the grace window after a tooltip closes during which
|
|
37
|
+
* moving to *another* trigger in the same Provider opens that tooltip
|
|
38
|
+
* instantly, with no `delayDuration` wait. Rest longer than this window and
|
|
39
|
+
* the next hover pays the full `delayDuration` again. This is what makes a
|
|
40
|
+
* toolbar of icon buttons feel responsive once the first tip has shown.
|
|
41
|
+
*
|
|
42
|
+
* Place a single Provider high in the tree (typically at the app root) so every
|
|
43
|
+
* tooltip shares one skip window. A per-instance
|
|
44
|
+
* {@link TooltipRootProps.delayDuration | `delayDuration`} on `Tooltip.Root`
|
|
45
|
+
* overrides the Provider's value for that one tooltip.
|
|
46
|
+
*
|
|
47
|
+
* @example App-wide provider
|
|
32
48
|
* ```tsx
|
|
33
49
|
* <Tooltip.Provider delayDuration={400}>
|
|
34
50
|
* <App />
|
|
@@ -55,13 +71,27 @@ export function TooltipProviderComponent({
|
|
|
55
71
|
TooltipProviderComponent.displayName = "TooltipProvider";
|
|
56
72
|
|
|
57
73
|
/**
|
|
58
|
-
* The state boundary for a single tooltip. Owns the open/closed
|
|
59
|
-
*
|
|
74
|
+
* The state boundary for a single tooltip. Owns the open/closed state, runs the
|
|
75
|
+
* open-delay and grace-period timers, and wires everything down to the
|
|
76
|
+
* sub-components via `TooltipContext`. Renders no DOM of its own. Must be a
|
|
77
|
+
* descendant of {@link TooltipProviderComponent | `Tooltip.Provider`}.
|
|
60
78
|
*
|
|
61
|
-
*
|
|
79
|
+
* **Controlled vs uncontrolled**, statically discriminated at the type level so
|
|
80
|
+
* TypeScript accepts only one shape:
|
|
62
81
|
*
|
|
63
|
-
* - **Uncontrolled** — pass `defaultOpen`
|
|
64
|
-
* -
|
|
82
|
+
* - **Uncontrolled** — pass {@link UncontrolledTooltipRootProps.defaultOpen | `defaultOpen`}
|
|
83
|
+
* (or omit it for closed-on-mount). The component owns the flag; observe
|
|
84
|
+
* transitions with `onOpenChange`.
|
|
85
|
+
* - **Controlled** — pass {@link ControlledTooltipRootProps.open | `open`}
|
|
86
|
+
* (with an optional `onOpenChange`). The parent owns the flag; the delay and
|
|
87
|
+
* grace timers still run and drive `onOpenChange`, but the tooltip only moves
|
|
88
|
+
* when the parent updates `open`.
|
|
89
|
+
*
|
|
90
|
+
* **Timing overrides.** {@link TooltipRootProps.delayDuration | `delayDuration`}
|
|
91
|
+
* overrides the Provider's open delay for this tooltip only.
|
|
92
|
+
* {@link TooltipRootProps.disableHoverableContent | `disableHoverableContent`}
|
|
93
|
+
* removes the grace period, so the tooltip closes the instant the pointer
|
|
94
|
+
* leaves the trigger (the user cannot move into the content).
|
|
65
95
|
*
|
|
66
96
|
* @example
|
|
67
97
|
* ```tsx
|
|
@@ -90,17 +120,28 @@ export function TooltipRoot({
|
|
|
90
120
|
return <TooltipProvider value={contextValue}>{children}</TooltipProvider>;
|
|
91
121
|
}
|
|
92
122
|
|
|
93
|
-
|
|
123
|
+
// Stryker disable next-line StringLiteral: overwritten by the compound alias — an equivalent mutant.
|
|
94
124
|
TooltipRoot.displayName = "TooltipRoot";
|
|
95
125
|
|
|
96
126
|
/**
|
|
97
|
-
* The element that
|
|
98
|
-
*
|
|
99
|
-
* element.
|
|
127
|
+
* The element that opens the tooltip. Renders a `<button type="button">` by
|
|
128
|
+
* default; pass `asChild` to project the trigger behaviour onto a custom
|
|
129
|
+
* element (a link, an icon button, etc.).
|
|
130
|
+
*
|
|
131
|
+
* **Open / close behaviour.** Pointer-enter opens after the effective
|
|
132
|
+
* `delayDuration` (immediately if the group's skip window is active);
|
|
133
|
+
* pointer-leave closes after a short grace period so the pointer can travel
|
|
134
|
+
* into hoverable content (immediately when `disableHoverableContent` is set on
|
|
135
|
+
* Root). Focus opens immediately and blur closes immediately, and `Escape`
|
|
136
|
+
* while focused closes immediately — matching the WAI-ARIA tooltip pattern.
|
|
137
|
+
* Consumer `onPointerEnter` / `onPointerLeave` / `onFocus` / `onBlur` /
|
|
138
|
+
* `onKeyDown` all compose (they run first).
|
|
139
|
+
*
|
|
140
|
+
* **ARIA wiring.**
|
|
141
|
+
* - `aria-describedby` points at {@link TooltipContent | `Tooltip.Content`}'s id.
|
|
142
|
+
* - `data-state="open" | "closed"` mirrors the tooltip state for CSS.
|
|
100
143
|
*
|
|
101
|
-
*
|
|
102
|
-
* - `aria-describedby` points at `Tooltip.Content`'s id.
|
|
103
|
-
* - `data-state="open" | "closed"` mirrors the tooltip state.
|
|
144
|
+
* @extends HTMLButtonElement
|
|
104
145
|
*
|
|
105
146
|
* @example
|
|
106
147
|
* ```tsx
|
|
@@ -148,12 +189,13 @@ TooltipTrigger.displayName = "TooltipTrigger";
|
|
|
148
189
|
*
|
|
149
190
|
* By default the portal only renders while the tooltip is open. Pass
|
|
150
191
|
* `forceMount` to keep the subtree mounted when closed — useful for
|
|
151
|
-
* CSS exit animations driven by `data-state="closed"`.
|
|
192
|
+
* CSS exit animations driven by `data-state="closed"`. Renders no wrapper
|
|
193
|
+
* element of its own.
|
|
152
194
|
*
|
|
153
195
|
* @example
|
|
154
196
|
* ```tsx
|
|
155
197
|
* <Tooltip.Portal>
|
|
156
|
-
* <Tooltip.Content
|
|
198
|
+
* <Tooltip.Content>...</Tooltip.Content>
|
|
157
199
|
* </Tooltip.Portal>
|
|
158
200
|
* ```
|
|
159
201
|
*/
|
|
@@ -192,6 +234,8 @@ TooltipPortal.displayName = "TooltipPortal";
|
|
|
192
234
|
* `anchor-name` to the trigger and `position-anchor` / `position-area`
|
|
193
235
|
* to the content in your own stylesheet.
|
|
194
236
|
*
|
|
237
|
+
* @extends HTMLDivElement
|
|
238
|
+
*
|
|
195
239
|
* @example
|
|
196
240
|
* ```tsx
|
|
197
241
|
* <Tooltip.Content className="tooltip__content">
|
|
@@ -233,6 +277,8 @@ TooltipContent.displayName = "TooltipContent";
|
|
|
233
277
|
*
|
|
234
278
|
* All positioning and styling is the consumer's responsibility via CSS.
|
|
235
279
|
*
|
|
280
|
+
* @extends HTMLSpanElement
|
|
281
|
+
*
|
|
236
282
|
* @example
|
|
237
283
|
* ```tsx
|
|
238
284
|
* <Tooltip.Content>
|
|
@@ -270,11 +316,43 @@ export type TooltipCompound = typeof TooltipRoot & {
|
|
|
270
316
|
* Headless, accessible **Tooltip** — a compound component implementing the
|
|
271
317
|
* [WAI-ARIA tooltip pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/):
|
|
272
318
|
* a deferred, hoverable label anchored to a trigger, with a shared
|
|
273
|
-
* {@link Tooltip.Provider} governing open / skip
|
|
274
|
-
*
|
|
319
|
+
* {@link TooltipProviderComponent | `Tooltip.Provider`} governing open / skip
|
|
320
|
+
* delays across a group. Opens on hover (after a delay) and on focus
|
|
321
|
+
* (immediately); closes on blur, on `Escape`, and after a short grace period on
|
|
322
|
+
* pointer-leave so the pointer can travel into hoverable content. Zero styles
|
|
323
|
+
* ship; visual placement is your CSS concern (CSS anchor positioning).
|
|
324
|
+
*
|
|
325
|
+
* `Tooltip` is both callable (an alias of {@link TooltipRoot | `Tooltip.Root`})
|
|
326
|
+
* and carries its sub-components as static properties. A `Tooltip.Provider`
|
|
327
|
+
* ancestor is required — `Tooltip.Root` throws without one.
|
|
328
|
+
*
|
|
329
|
+
* - {@link TooltipProviderComponent | `Tooltip.Provider`} — shared delay config + group open coordination.
|
|
330
|
+
* - {@link TooltipRoot | `Tooltip.Root`} — per-tooltip state owner and timing.
|
|
331
|
+
* - {@link TooltipTrigger | `Tooltip.Trigger`} — `<button>` that opens on hover/focus.
|
|
332
|
+
* - {@link TooltipPortal | `Tooltip.Portal`} — optional portal to `document.body`.
|
|
333
|
+
* - {@link TooltipContent | `Tooltip.Content`} — the `role="tooltip"` panel.
|
|
334
|
+
* - {@link TooltipArrow | `Tooltip.Arrow`} — optional visual pointer.
|
|
335
|
+
*
|
|
336
|
+
* @example Minimal usage
|
|
337
|
+
* ```tsx
|
|
338
|
+
* import { Tooltip } from "@primitiv-ui/react";
|
|
339
|
+
*
|
|
340
|
+
* <Tooltip.Provider>
|
|
341
|
+
* <Tooltip.Root>
|
|
342
|
+
* <Tooltip.Trigger>Save</Tooltip.Trigger>
|
|
343
|
+
* <Tooltip.Portal>
|
|
344
|
+
* <Tooltip.Content>
|
|
345
|
+
* Save your changes
|
|
346
|
+
* <Tooltip.Arrow />
|
|
347
|
+
* </Tooltip.Content>
|
|
348
|
+
* </Tooltip.Portal>
|
|
349
|
+
* </Tooltip.Root>
|
|
350
|
+
* </Tooltip.Provider>;
|
|
351
|
+
* ```
|
|
275
352
|
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
353
|
+
* @see {@link TooltipProviderComponent} for delay / skip-delay coordination.
|
|
354
|
+
* @see {@link TooltipRoot} for the controlled/uncontrolled state modes and timing overrides.
|
|
355
|
+
* @see {@link TooltipContent} for the grace period and the Escape / outside-pointer escape hatches.
|
|
278
356
|
*/
|
|
279
357
|
const TooltipCompound: TooltipCompound = Object.assign(TooltipRoot, {
|
|
280
358
|
Provider: TooltipProviderComponent,
|