@primitiv-ui/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -0
- package/package.json +59 -0
- package/src/AccessibleIcon/AccessibleIcon.tsx +40 -0
- package/src/AccessibleIcon/README.md +42 -0
- package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +47 -0
- package/src/AccessibleIcon/index.ts +2 -0
- package/src/AccessibleIcon/types.ts +8 -0
- package/src/Accordion/Accordion.tsx +412 -0
- package/src/Accordion/AccordionContext.ts +12 -0
- package/src/Accordion/README.md +202 -0
- package/src/Accordion/__tests__/Accordion.asChild.test.tsx +237 -0
- package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +333 -0
- package/src/Accordion/__tests__/Accordion.controlled-state.test.tsx +175 -0
- package/src/Accordion/__tests__/Accordion.data-attributes.test.tsx +272 -0
- package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +311 -0
- package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +119 -0
- package/src/Accordion/__tests__/Accordion.forceMount.test.tsx +119 -0
- package/src/Accordion/__tests__/Accordion.keyboard-interaction.test.tsx +736 -0
- package/src/Accordion/__tests__/Accordion.mouse-interaction.test.tsx +212 -0
- package/src/Accordion/__tests__/Accordion.multiple-mode.test.tsx +90 -0
- package/src/Accordion/__tests__/Accordion.reading-direction.test.tsx +139 -0
- package/src/Accordion/__tests__/Accordion.uncontrolled-state.test.tsx +154 -0
- package/src/Accordion/hooks/index.ts +6 -0
- package/src/Accordion/hooks/useAccordionContext.ts +1 -0
- package/src/Accordion/hooks/useAccordionHeaderContext.ts +10 -0
- package/src/Accordion/hooks/useAccordionItem.ts +22 -0
- package/src/Accordion/hooks/useAccordionItemContext.ts +1 -0
- package/src/Accordion/hooks/useAccordionRoot.ts +151 -0
- package/src/Accordion/hooks/useAccordionTrigger.ts +90 -0
- package/src/Accordion/index.ts +1 -0
- package/src/Accordion/types.ts +81 -0
- package/src/Alert/Alert.tsx +43 -0
- package/src/Alert/README.md +54 -0
- package/src/Alert/__tests__/Alert.test.tsx +28 -0
- package/src/Alert/index.ts +2 -0
- package/src/Alert/types.ts +5 -0
- package/src/Avatar/Avatar.tsx +149 -0
- package/src/Avatar/AvatarContext.ts +20 -0
- package/src/Avatar/README.md +116 -0
- package/src/Avatar/__tests__/Avatar.asChild.test.tsx +53 -0
- package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +14 -0
- package/src/Avatar/__tests__/Avatar.error-handling.test.tsx +30 -0
- package/src/Avatar/__tests__/Avatar.fallback.test.tsx +75 -0
- package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +81 -0
- package/src/Avatar/hooks/index.ts +2 -0
- package/src/Avatar/hooks/useAvatarContext.ts +1 -0
- package/src/Avatar/hooks/useAvatarImage.ts +40 -0
- package/src/Avatar/index.ts +3 -0
- package/src/Avatar/types.ts +44 -0
- package/src/Breadcrumb/Breadcrumb.tsx +234 -0
- package/src/Breadcrumb/README.md +111 -0
- package/src/Breadcrumb/__tests__/Breadcrumb.asChild.test.tsx +33 -0
- package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +132 -0
- package/src/Breadcrumb/index.ts +2 -0
- package/src/Breadcrumb/types.ts +22 -0
- package/src/Button/Button.tsx +95 -0
- package/src/Button/README.md +112 -0
- package/src/Button/__tests__/Button.asChild.test.tsx +91 -0
- package/src/Button/__tests__/Button.basic-rendering.test.tsx +126 -0
- package/src/Button/__tests__/Button.contract.test.tsx +72 -0
- package/src/Button/__tests__/Button.disabled.test.tsx +52 -0
- package/src/Button/__tests__/Button.icon-usage.test.tsx +57 -0
- package/src/Button/__tests__/Button.keyboard-interaction.test.tsx +70 -0
- package/src/Button/index.ts +2 -0
- package/src/Button/types.ts +8 -0
- package/src/Carousel/Carousel.tsx +708 -0
- package/src/Carousel/CarouselContext.ts +11 -0
- package/src/Carousel/README.md +848 -0
- package/src/Carousel/__tests__/Carousel.asChild.test.tsx +178 -0
- package/src/Carousel/__tests__/Carousel.auto-play.test.tsx +617 -0
- package/src/Carousel/__tests__/Carousel.basic-rendering.test.tsx +569 -0
- package/src/Carousel/__tests__/Carousel.controlled-state.test.tsx +137 -0
- package/src/Carousel/__tests__/Carousel.error-handling.test.tsx +81 -0
- package/src/Carousel/__tests__/Carousel.ids.test.tsx +111 -0
- package/src/Carousel/__tests__/Carousel.imperative-api.test.tsx +213 -0
- package/src/Carousel/__tests__/Carousel.indicators.test.tsx +560 -0
- package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +276 -0
- package/src/Carousel/__tests__/Carousel.keyboard-navigation.test.tsx +158 -0
- package/src/Carousel/__tests__/Carousel.play-pause.test.tsx +232 -0
- package/src/Carousel/__tests__/Carousel.prev-next.test.tsx +68 -0
- package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +49 -0
- package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +87 -0
- package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +179 -0
- package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +109 -0
- package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +151 -0
- package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +183 -0
- package/src/Carousel/__tests__/Carousel.touch-interaction.test.tsx +96 -0
- package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +70 -0
- package/src/Carousel/__tests__/Carousel.translations.test.tsx +157 -0
- package/src/Carousel/__tests__/Carousel.uncontrolled-state.test.tsx +146 -0
- package/src/Carousel/hooks/index.ts +4 -0
- package/src/Carousel/hooks/useCarouselContext.ts +13 -0
- package/src/Carousel/hooks/useCarouselRoot.ts +450 -0
- package/src/Carousel/hooks/useCarouselSlide.ts +45 -0
- package/src/Carousel/hooks/useCarouselViewport.ts +290 -0
- package/src/Carousel/index.ts +3 -0
- package/src/Carousel/types.ts +400 -0
- package/src/Checkbox/Checkbox.tsx +228 -0
- package/src/Checkbox/CheckboxContext.ts +12 -0
- package/src/Checkbox/README.md +156 -0
- package/src/Checkbox/__tests__/Checkbox.asChild.test.tsx +69 -0
- package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +41 -0
- package/src/Checkbox/__tests__/Checkbox.controlled-state.test.tsx +82 -0
- package/src/Checkbox/__tests__/Checkbox.disabled.test.tsx +15 -0
- package/src/Checkbox/__tests__/Checkbox.indeterminate.test.tsx +82 -0
- package/src/Checkbox/__tests__/Checkbox.indicator.test.tsx +117 -0
- package/src/Checkbox/__tests__/Checkbox.uncontrolled-state.test.tsx +89 -0
- package/src/Checkbox/hooks/index.ts +2 -0
- package/src/Checkbox/hooks/useCheckboxContext.ts +1 -0
- package/src/Checkbox/hooks/useCheckboxRoot.ts +32 -0
- package/src/Checkbox/index.ts +1 -0
- package/src/Checkbox/types.ts +33 -0
- package/src/CheckboxCard/CheckboxCard.tsx +208 -0
- package/src/CheckboxCard/CheckboxCardContext.ts +12 -0
- package/src/CheckboxCard/README.md +114 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.asChild.test.tsx +54 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +58 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.controlled-state.test.tsx +77 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.disabled.test.tsx +55 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.error-handling.test.tsx +20 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.indeterminate.test.tsx +60 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.indicator.test.tsx +136 -0
- package/src/CheckboxCard/__tests__/CheckboxCard.uncontrolled-state.test.tsx +73 -0
- package/src/CheckboxCard/hooks/index.ts +2 -0
- package/src/CheckboxCard/hooks/useCheckboxCardContext.ts +1 -0
- package/src/CheckboxCard/hooks/useCheckboxCardRoot.ts +30 -0
- package/src/CheckboxCard/index.ts +3 -0
- package/src/CheckboxCard/types.ts +33 -0
- package/src/Collapsible/Collapsible.tsx +316 -0
- package/src/Collapsible/CollapsibleContext.ts +7 -0
- package/src/Collapsible/README.md +174 -0
- package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +240 -0
- package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +118 -0
- package/src/Collapsible/__tests__/Collapsible.controlled-state.test.tsx +134 -0
- package/src/Collapsible/__tests__/Collapsible.disabled.test.tsx +132 -0
- package/src/Collapsible/__tests__/Collapsible.error-handling.test.tsx +40 -0
- package/src/Collapsible/__tests__/Collapsible.forceMount.test.tsx +111 -0
- package/src/Collapsible/__tests__/Collapsible.triggerIcon.test.tsx +93 -0
- package/src/Collapsible/__tests__/Collapsible.uncontrolled-state.test.tsx +125 -0
- package/src/Collapsible/hooks/index.ts +2 -0
- package/src/Collapsible/hooks/useCollapsibleRoot.ts +34 -0
- package/src/Collapsible/hooks/useCollapsibleTrigger.ts +49 -0
- package/src/Collapsible/index.ts +1 -0
- package/src/Collapsible/types.ts +48 -0
- package/src/ContextMenu/ContextMenu.tsx +1004 -0
- package/src/ContextMenu/ContextMenuContentContext.ts +15 -0
- package/src/ContextMenu/ContextMenuContext.ts +21 -0
- package/src/ContextMenu/ContextMenuGroupContext.ts +8 -0
- package/src/ContextMenu/ContextMenuItemIndicatorContext.ts +8 -0
- package/src/ContextMenu/ContextMenuRadioGroupContext.ts +9 -0
- package/src/ContextMenu/ContextMenuSubContext.ts +15 -0
- package/src/ContextMenu/README.md +275 -0
- package/src/ContextMenu/__tests__/ContextMenu.asChild.test.tsx +186 -0
- package/src/ContextMenu/__tests__/ContextMenu.basic-rendering.test.tsx +39 -0
- package/src/ContextMenu/__tests__/ContextMenu.checkbox-item.test.tsx +145 -0
- package/src/ContextMenu/__tests__/ContextMenu.error-handling.test.tsx +113 -0
- package/src/ContextMenu/__tests__/ContextMenu.group-label.test.tsx +48 -0
- package/src/ContextMenu/__tests__/ContextMenu.item-indicator.test.tsx +88 -0
- package/src/ContextMenu/__tests__/ContextMenu.item.test.tsx +106 -0
- package/src/ContextMenu/__tests__/ContextMenu.keyboard-interaction.test.tsx +172 -0
- package/src/ContextMenu/__tests__/ContextMenu.mouse-interaction.test.tsx +227 -0
- package/src/ContextMenu/__tests__/ContextMenu.radio-item.test.tsx +127 -0
- package/src/ContextMenu/__tests__/ContextMenu.reading-direction.test.tsx +152 -0
- package/src/ContextMenu/__tests__/ContextMenu.separator.test.tsx +47 -0
- package/src/ContextMenu/__tests__/ContextMenu.state-modes.test.tsx +119 -0
- package/src/ContextMenu/__tests__/ContextMenu.sub.test.tsx +262 -0
- package/src/ContextMenu/__tests__/ContextMenu.typeahead.test.tsx +89 -0
- package/src/ContextMenu/constants.ts +4 -0
- package/src/ContextMenu/index.ts +1 -0
- package/src/ContextMenu/types.ts +199 -0
- package/src/DirectionProvider/DirectionContext.ts +21 -0
- package/src/DirectionProvider/DirectionProvider.tsx +31 -0
- package/src/DirectionProvider/README.md +62 -0
- package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +29 -0
- package/src/DirectionProvider/index.ts +3 -0
- package/src/DirectionProvider/types.ts +10 -0
- package/src/Divider/Divider.tsx +57 -0
- package/src/Divider/README.md +57 -0
- package/src/Divider/__tests__/Divider.test.tsx +41 -0
- package/src/Divider/index.ts +1 -0
- package/src/Divider/types.ts +5 -0
- package/src/Dropdown/Dropdown.tsx +842 -0
- package/src/Dropdown/DropdownContentContext.ts +15 -0
- package/src/Dropdown/DropdownContext.ts +17 -0
- package/src/Dropdown/DropdownGroupContext.ts +8 -0
- package/src/Dropdown/DropdownItemIndicatorContext.ts +13 -0
- package/src/Dropdown/DropdownRadioGroupContext.ts +9 -0
- package/src/Dropdown/DropdownSubContext.ts +15 -0
- package/src/Dropdown/README.md +284 -0
- package/src/Dropdown/__tests__/Dropdown.asChild.test.tsx +286 -0
- package/src/Dropdown/__tests__/Dropdown.basic-rendering.test.tsx +43 -0
- package/src/Dropdown/__tests__/Dropdown.checkbox-item.test.tsx +121 -0
- package/src/Dropdown/__tests__/Dropdown.disabled.test.tsx +143 -0
- package/src/Dropdown/__tests__/Dropdown.error-handling.test.tsx +85 -0
- package/src/Dropdown/__tests__/Dropdown.group-label.test.tsx +68 -0
- package/src/Dropdown/__tests__/Dropdown.item-indicator.test.tsx +260 -0
- package/src/Dropdown/__tests__/Dropdown.item.test.tsx +72 -0
- package/src/Dropdown/__tests__/Dropdown.keyboard-edge-cases.test.tsx +77 -0
- package/src/Dropdown/__tests__/Dropdown.keyboard-interaction.test.tsx +310 -0
- package/src/Dropdown/__tests__/Dropdown.mouse-interaction.test.tsx +347 -0
- package/src/Dropdown/__tests__/Dropdown.radio-item.test.tsx +134 -0
- package/src/Dropdown/__tests__/Dropdown.reading-direction.test.tsx +153 -0
- package/src/Dropdown/__tests__/Dropdown.separator.test.tsx +46 -0
- package/src/Dropdown/__tests__/Dropdown.state-modes.test.tsx +100 -0
- package/src/Dropdown/__tests__/Dropdown.sub.test.tsx +185 -0
- package/src/Dropdown/__tests__/Dropdown.trigger.test.tsx +110 -0
- package/src/Dropdown/__tests__/Dropdown.typeahead.test.tsx +133 -0
- package/src/Dropdown/constants.ts +4 -0
- package/src/Dropdown/hooks/index.ts +9 -0
- package/src/Dropdown/hooks/useCloseSiblingSub.ts +13 -0
- package/src/Dropdown/hooks/useDropdownContent.ts +162 -0
- package/src/Dropdown/hooks/useDropdownContext.ts +1 -0
- package/src/Dropdown/hooks/useDropdownGroup.ts +18 -0
- package/src/Dropdown/hooks/useDropdownItem.ts +49 -0
- package/src/Dropdown/hooks/useDropdownLabel.ts +15 -0
- package/src/Dropdown/hooks/useDropdownRoot.ts +57 -0
- package/src/Dropdown/hooks/useDropdownSubContext.ts +1 -0
- package/src/Dropdown/hooks/useDropdownTrigger.ts +31 -0
- package/src/Dropdown/index.ts +1 -0
- package/src/Dropdown/types.ts +200 -0
- package/src/EmptyState/EmptyState.tsx +245 -0
- package/src/EmptyState/README.md +129 -0
- package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +32 -0
- package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +30 -0
- package/src/EmptyState/__tests__/EmptyState.Media.test.tsx +34 -0
- package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +28 -0
- package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +26 -0
- package/src/EmptyState/index.ts +2 -0
- package/src/EmptyState/types.ts +21 -0
- package/src/Field/Field.tsx +239 -0
- package/src/Field/FieldContext.ts +22 -0
- package/src/Field/README.md +167 -0
- package/src/Field/__tests__/Field.asChild.test.tsx +83 -0
- package/src/Field/__tests__/Field.basic-rendering.test.tsx +104 -0
- package/src/Field/__tests__/Field.state-cascade.test.tsx +75 -0
- package/src/Field/hooks/index.ts +2 -0
- package/src/Field/hooks/useFieldContext.ts +1 -0
- package/src/Field/hooks/useFieldProps.ts +57 -0
- package/src/Field/index.ts +2 -0
- package/src/Field/types.ts +33 -0
- package/src/Fieldset/Fieldset.tsx +104 -0
- package/src/Fieldset/README.md +74 -0
- package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +81 -0
- package/src/Fieldset/__tests__/Fieldset.disabled.test.tsx +41 -0
- package/src/Fieldset/index.ts +2 -0
- package/src/Fieldset/types.ts +5 -0
- package/src/Input/Input.tsx +120 -0
- package/src/Input/README.md +180 -0
- package/src/Input/__tests__/Input.asChild.test.tsx +85 -0
- package/src/Input/__tests__/Input.basic-rendering.test.tsx +118 -0
- package/src/Input/__tests__/Input.disabled.test.tsx +49 -0
- package/src/Input/__tests__/Input.field-integration.test.tsx +148 -0
- package/src/Input/index.ts +2 -0
- package/src/Input/types.ts +7 -0
- package/src/InputGroup/InputGroup.tsx +228 -0
- package/src/InputGroup/README.md +178 -0
- package/src/InputGroup/__tests__/InputGroup.asChild.test.tsx +109 -0
- package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +106 -0
- package/src/InputGroup/index.ts +2 -0
- package/src/InputGroup/types.ts +13 -0
- package/src/MillerColumns/MillerColumns.tsx +329 -0
- package/src/MillerColumns/MillerColumnsContext.ts +25 -0
- package/src/MillerColumns/README.md +278 -0
- package/src/MillerColumns/__tests__/MillerColumns.aria.test.tsx +82 -0
- package/src/MillerColumns/__tests__/MillerColumns.asChild.test.tsx +106 -0
- package/src/MillerColumns/__tests__/MillerColumns.auto-scroll.test.tsx +68 -0
- package/src/MillerColumns/__tests__/MillerColumns.basic-rendering.test.tsx +52 -0
- package/src/MillerColumns/__tests__/MillerColumns.column-projection.test.tsx +161 -0
- package/src/MillerColumns/__tests__/MillerColumns.controlled-state.test.tsx +90 -0
- package/src/MillerColumns/__tests__/MillerColumns.data-attributes.test.tsx +77 -0
- package/src/MillerColumns/__tests__/MillerColumns.disabled-items.test.tsx +65 -0
- package/src/MillerColumns/__tests__/MillerColumns.error-handling.test.tsx +57 -0
- package/src/MillerColumns/__tests__/MillerColumns.fixtures.ts +15 -0
- package/src/MillerColumns/__tests__/MillerColumns.item-indicator.test.tsx +57 -0
- package/src/MillerColumns/__tests__/MillerColumns.keyboard-interaction.test.tsx +181 -0
- package/src/MillerColumns/__tests__/MillerColumns.preview-panel.test.tsx +47 -0
- package/src/MillerColumns/__tests__/MillerColumns.resize.test.tsx +137 -0
- package/src/MillerColumns/__tests__/MillerColumns.roving-tabindex.test.tsx +91 -0
- package/src/MillerColumns/__tests__/MillerColumns.selection.test.tsx +54 -0
- package/src/MillerColumns/__tests__/MillerColumns.uncontrolled-state.test.tsx +70 -0
- package/src/MillerColumns/hooks/index.ts +7 -0
- package/src/MillerColumns/hooks/useMillerColumnsColumn.ts +23 -0
- package/src/MillerColumns/hooks/useMillerColumnsColumnContext.ts +1 -0
- package/src/MillerColumns/hooks/useMillerColumnsContext.ts +1 -0
- package/src/MillerColumns/hooks/useMillerColumnsItem.ts +157 -0
- package/src/MillerColumns/hooks/useMillerColumnsItemContext.ts +1 -0
- package/src/MillerColumns/hooks/useMillerColumnsResizeHandle.ts +76 -0
- package/src/MillerColumns/hooks/useMillerColumnsRoot.ts +0 -0
- package/src/MillerColumns/index.ts +3 -0
- package/src/MillerColumns/types.ts +93 -0
- package/src/MillerColumns/useMillerColumnsSelection.ts +31 -0
- package/src/MillerColumns/utils.ts +75 -0
- package/src/Modal/Modal.tsx +474 -0
- package/src/Modal/ModalContext.ts +13 -0
- package/src/Modal/README.md +207 -0
- package/src/Modal/__tests__/Modal.accessibility.test.tsx +167 -0
- package/src/Modal/__tests__/Modal.asChild.test.tsx +162 -0
- package/src/Modal/__tests__/Modal.click-outside.test.tsx +115 -0
- package/src/Modal/__tests__/Modal.content.test.tsx +193 -0
- package/src/Modal/__tests__/Modal.controlled-state.test.tsx +120 -0
- package/src/Modal/__tests__/Modal.error-handling.test.tsx +30 -0
- package/src/Modal/__tests__/Modal.escape-hatches.test.tsx +99 -0
- package/src/Modal/__tests__/Modal.imperative-api.test.tsx +119 -0
- package/src/Modal/__tests__/Modal.nested.test.tsx +106 -0
- package/src/Modal/__tests__/Modal.overlay.test.tsx +99 -0
- package/src/Modal/__tests__/Modal.portal.test.tsx +90 -0
- package/src/Modal/__tests__/Modal.presence.test.tsx +111 -0
- package/src/Modal/__tests__/Modal.trigger.test.tsx +70 -0
- package/src/Modal/__tests__/Modal.uncontrolled-state.test.tsx +72 -0
- package/src/Modal/__tests__/dialog-polyfill.ts +40 -0
- package/src/Modal/hooks/index.ts +4 -0
- package/src/Modal/hooks/useModalContent.ts +62 -0
- package/src/Modal/hooks/useModalContext.ts +1 -0
- package/src/Modal/hooks/useModalRoot.ts +81 -0
- package/src/Modal/hooks/useModalTrigger.ts +25 -0
- package/src/Modal/index.ts +3 -0
- package/src/Modal/types.ts +76 -0
- package/src/Portal/Portal.tsx +28 -0
- package/src/Portal/README.md +70 -0
- package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +17 -0
- package/src/Portal/index.ts +2 -0
- package/src/Portal/types.ts +6 -0
- package/src/Progress/Progress.tsx +178 -0
- package/src/Progress/ProgressContext.ts +15 -0
- package/src/Progress/README.md +112 -0
- package/src/Progress/__tests__/Progress.asChild.test.tsx +37 -0
- package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +65 -0
- package/src/Progress/__tests__/Progress.error-handling.test.tsx +40 -0
- package/src/Progress/__tests__/Progress.fixtures.ts +7 -0
- package/src/Progress/__tests__/Progress.value.test.tsx +83 -0
- package/src/Progress/hooks/index.ts +2 -0
- package/src/Progress/hooks/useProgressContext.ts +1 -0
- package/src/Progress/hooks/useProgressRoot.ts +45 -0
- package/src/Progress/index.ts +3 -0
- package/src/Progress/types.ts +43 -0
- package/src/RadioCard/README.md +133 -0
- package/src/RadioCard/RadioCard.tsx +334 -0
- package/src/RadioCard/RadioCardContext.ts +23 -0
- package/src/RadioCard/RadioCardItemContext.ts +10 -0
- package/src/RadioCard/__tests__/RadioCard.asChild.test.tsx +76 -0
- package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +87 -0
- package/src/RadioCard/__tests__/RadioCard.controlled-state.test.tsx +107 -0
- package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +61 -0
- package/src/RadioCard/__tests__/RadioCard.error-handling.test.tsx +35 -0
- package/src/RadioCard/__tests__/RadioCard.indicator.test.tsx +119 -0
- package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +158 -0
- package/src/RadioCard/__tests__/RadioCard.orientation.test.tsx +90 -0
- package/src/RadioCard/__tests__/RadioCard.reading-direction.test.tsx +65 -0
- package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +108 -0
- package/src/RadioCard/hooks/index.ts +3 -0
- package/src/RadioCard/hooks/useRadioCardContext.ts +1 -0
- package/src/RadioCard/hooks/useRadioCardItemContext.ts +1 -0
- package/src/RadioCard/hooks/useRadioCardRoot.ts +77 -0
- package/src/RadioCard/index.ts +4 -0
- package/src/RadioCard/types.ts +51 -0
- package/src/RadioGroup/README.md +185 -0
- package/src/RadioGroup/RadioGroup.tsx +353 -0
- package/src/RadioGroup/RadioGroupContext.ts +23 -0
- package/src/RadioGroup/RadioGroupItemContext.ts +10 -0
- package/src/RadioGroup/__tests__/RadioGroup.asChild.test.tsx +105 -0
- package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +72 -0
- package/src/RadioGroup/__tests__/RadioGroup.controlled-state.test.tsx +109 -0
- package/src/RadioGroup/__tests__/RadioGroup.disabled-keydown-guards.test.tsx +68 -0
- package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +79 -0
- package/src/RadioGroup/__tests__/RadioGroup.error-handling.test.tsx +33 -0
- package/src/RadioGroup/__tests__/RadioGroup.indicator.test.tsx +85 -0
- package/src/RadioGroup/__tests__/RadioGroup.keyboard-interaction.test.tsx +135 -0
- package/src/RadioGroup/__tests__/RadioGroup.orientation.test.tsx +90 -0
- package/src/RadioGroup/__tests__/RadioGroup.reading-direction.test.tsx +65 -0
- package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +45 -0
- package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +105 -0
- package/src/RadioGroup/__tests__/RadioGroup.uncontrolled-state.test.tsx +96 -0
- package/src/RadioGroup/hooks/index.ts +3 -0
- package/src/RadioGroup/hooks/useRadioGroupContext.ts +1 -0
- package/src/RadioGroup/hooks/useRadioGroupItemContext.ts +1 -0
- package/src/RadioGroup/hooks/useRadioGroupRoot.ts +87 -0
- package/src/RadioGroup/index.ts +1 -0
- package/src/RadioGroup/types.ts +51 -0
- package/src/Select/README.md +203 -0
- package/src/Select/Select.tsx +204 -0
- package/src/Select/__tests__/Select.asChild.test.tsx +36 -0
- package/src/Select/__tests__/Select.basic-rendering.test.tsx +17 -0
- package/src/Select/__tests__/Select.controlled-state.test.tsx +69 -0
- package/src/Select/__tests__/Select.data-attributes.test.tsx +29 -0
- package/src/Select/__tests__/Select.field-integration.test.tsx +150 -0
- package/src/Select/__tests__/Select.group.test.tsx +42 -0
- package/src/Select/__tests__/Select.placeholder.test.tsx +32 -0
- package/src/Select/index.ts +2 -0
- package/src/Select/types.ts +89 -0
- package/src/SkipNav/README.md +87 -0
- package/src/SkipNav/SkipNav.tsx +116 -0
- package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +23 -0
- package/src/SkipNav/__tests__/SkipNav.ids.test.tsx +19 -0
- package/src/SkipNav/index.ts +1 -0
- package/src/SkipNav/types.ts +26 -0
- package/src/Slider/README.md +215 -0
- package/src/Slider/Slider.tsx +308 -0
- package/src/Slider/SliderContext.ts +24 -0
- package/src/Slider/__tests__/Slider.asChild.test.tsx +119 -0
- package/src/Slider/__tests__/Slider.basic-rendering.test.tsx +157 -0
- package/src/Slider/__tests__/Slider.controlled-state.test.tsx +78 -0
- package/src/Slider/__tests__/Slider.disabled.test.tsx +82 -0
- package/src/Slider/__tests__/Slider.error-handling.test.tsx +45 -0
- package/src/Slider/__tests__/Slider.fixtures.ts +53 -0
- package/src/Slider/__tests__/Slider.form.test.tsx +67 -0
- package/src/Slider/__tests__/Slider.inverted.test.tsx +112 -0
- package/src/Slider/__tests__/Slider.keyboard-interaction.test.tsx +118 -0
- package/src/Slider/__tests__/Slider.multiple-thumbs.test.tsx +84 -0
- package/src/Slider/__tests__/Slider.orientation.test.tsx +101 -0
- package/src/Slider/__tests__/Slider.pointer-interaction.test.tsx +205 -0
- package/src/Slider/__tests__/Slider.reading-direction.test.tsx +99 -0
- package/src/Slider/__tests__/Slider.uncontrolled-state.test.tsx +69 -0
- package/src/Slider/__tests__/Slider.value-commit.test.tsx +103 -0
- package/src/Slider/hooks/index.ts +3 -0
- package/src/Slider/hooks/useSliderContext.ts +1 -0
- package/src/Slider/hooks/useSliderRoot.ts +197 -0
- package/src/Slider/hooks/useSliderThumb.ts +77 -0
- package/src/Slider/index.ts +3 -0
- package/src/Slider/types.ts +48 -0
- package/src/Slider/utils.ts +155 -0
- package/src/Slot/Slot.tsx +158 -0
- package/src/Slot/__tests__/Slot.test.tsx +163 -0
- package/src/Slot/__tests__/composeEventHandlers.test.ts +74 -0
- package/src/Slot/composeEventHandlers.ts +38 -0
- package/src/Slot/index.ts +3 -0
- package/src/Slot/types.ts +5 -0
- package/src/Status/README.md +50 -0
- package/src/Status/Status.tsx +44 -0
- package/src/Status/__tests__/Status.test.tsx +28 -0
- package/src/Status/index.ts +2 -0
- package/src/Status/types.ts +5 -0
- package/src/Switch/README.md +121 -0
- package/src/Switch/Switch.tsx +167 -0
- package/src/Switch/SwitchContext.ts +10 -0
- package/src/Switch/__tests__/Switch.asChild.test.tsx +56 -0
- package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +76 -0
- package/src/Switch/__tests__/Switch.contract.test.tsx +109 -0
- package/src/Switch/__tests__/Switch.controlled-state.test.tsx +79 -0
- package/src/Switch/__tests__/Switch.disabled.test.tsx +60 -0
- package/src/Switch/__tests__/Switch.error-handling.test.tsx +20 -0
- package/src/Switch/__tests__/Switch.keyboard-interaction.test.tsx +56 -0
- package/src/Switch/__tests__/Switch.thumb.test.tsx +84 -0
- package/src/Switch/__tests__/Switch.uncontrolled-state.test.tsx +83 -0
- package/src/Switch/hooks/index.ts +2 -0
- package/src/Switch/hooks/useSwitchContext.ts +1 -0
- package/src/Switch/hooks/useSwitchRoot.ts +28 -0
- package/src/Switch/index.ts +3 -0
- package/src/Switch/types.ts +37 -0
- package/src/Table/README.md +205 -0
- package/src/Table/Table.tsx +380 -0
- package/src/Table/__tests__/Table.Body.test.tsx +61 -0
- package/src/Table/__tests__/Table.Caption.test.tsx +70 -0
- package/src/Table/__tests__/Table.Cell.test.tsx +73 -0
- package/src/Table/__tests__/Table.Footer.test.tsx +61 -0
- package/src/Table/__tests__/Table.Head.test.tsx +61 -0
- package/src/Table/__tests__/Table.Header.test.tsx +73 -0
- package/src/Table/__tests__/Table.Root.test.tsx +49 -0
- package/src/Table/__tests__/Table.Row.test.tsx +67 -0
- package/src/Table/__tests__/Table.ScrollArea.test.tsx +83 -0
- package/src/Table/index.ts +1 -0
- package/src/Table/types.ts +63 -0
- package/src/Tabs/README.md +110 -0
- package/src/Tabs/Tabs.tsx +434 -0
- package/src/Tabs/TabsContext.ts +13 -0
- package/src/Tabs/__tests__/Tabs.activation-mode.test.tsx +114 -0
- package/src/Tabs/__tests__/Tabs.asChild.test.tsx +91 -0
- package/src/Tabs/__tests__/Tabs.basic-rendering.test.tsx +483 -0
- package/src/Tabs/__tests__/Tabs.change-event-callbacks.test.tsx +133 -0
- package/src/Tabs/__tests__/Tabs.controlled-state.test.tsx +152 -0
- package/src/Tabs/__tests__/Tabs.disabled-tabs.test.tsx +203 -0
- package/src/Tabs/__tests__/Tabs.error-handling.test.tsx +82 -0
- package/src/Tabs/__tests__/Tabs.fixtures.ts +171 -0
- package/src/Tabs/__tests__/Tabs.imperative-api.test.tsx +118 -0
- package/src/Tabs/__tests__/Tabs.keyboard-interaction.test.tsx +192 -0
- package/src/Tabs/__tests__/Tabs.lazy-mount.test.tsx +61 -0
- package/src/Tabs/__tests__/Tabs.mouse-interaction.test.tsx +216 -0
- package/src/Tabs/__tests__/Tabs.reading-direction.test.tsx +58 -0
- package/src/Tabs/__tests__/Tabs.uncontrolled-state.test.tsx +197 -0
- package/src/Tabs/hooks/index.ts +4 -0
- package/src/Tabs/hooks/useTabsContent.ts +27 -0
- package/src/Tabs/hooks/useTabsContext.ts +1 -0
- package/src/Tabs/hooks/useTabsRoot.ts +148 -0
- package/src/Tabs/hooks/useTabsTrigger.ts +111 -0
- package/src/Tabs/index.ts +3 -0
- package/src/Tabs/types.ts +99 -0
- package/src/Tabs/utils.ts +8 -0
- package/src/Textarea/README.md +98 -0
- package/src/Textarea/Textarea.tsx +93 -0
- package/src/Textarea/__tests__/Textarea.asChild.test.tsx +85 -0
- package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +107 -0
- package/src/Textarea/__tests__/Textarea.disabled.test.tsx +49 -0
- package/src/Textarea/__tests__/Textarea.field-integration.test.tsx +134 -0
- package/src/Textarea/index.ts +2 -0
- package/src/Textarea/types.ts +7 -0
- package/src/Toggle/README.md +97 -0
- package/src/Toggle/Toggle.tsx +81 -0
- package/src/Toggle/__tests__/Toggle.asChild.test.tsx +42 -0
- package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +28 -0
- package/src/Toggle/__tests__/Toggle.controlled-state.test.tsx +60 -0
- package/src/Toggle/__tests__/Toggle.disabled.test.tsx +34 -0
- package/src/Toggle/__tests__/Toggle.keyboard-interaction.test.tsx +42 -0
- package/src/Toggle/__tests__/Toggle.uncontrolled-state.test.tsx +40 -0
- package/src/Toggle/index.ts +2 -0
- package/src/Toggle/types.ts +23 -0
- package/src/ToggleGroup/README.md +137 -0
- package/src/ToggleGroup/ToggleGroup.tsx +298 -0
- package/src/ToggleGroup/ToggleGroupContext.ts +9 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +65 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +50 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.disabled.test.tsx +54 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +151 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.multiple-mode.test.tsx +144 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.reading-direction.test.tsx +28 -0
- package/src/ToggleGroup/__tests__/ToggleGroup.single-mode.test.tsx +139 -0
- package/src/ToggleGroup/hooks/index.ts +2 -0
- package/src/ToggleGroup/hooks/useToggleGroupContext.ts +1 -0
- package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +110 -0
- package/src/ToggleGroup/index.ts +2 -0
- package/src/ToggleGroup/types.ts +72 -0
- package/src/Tooltip/README.md +214 -0
- package/src/Tooltip/Tooltip.tsx +260 -0
- package/src/Tooltip/TooltipContext.ts +20 -0
- package/src/Tooltip/__tests__/Tooltip.asChild.test.tsx +77 -0
- package/src/Tooltip/__tests__/Tooltip.basic-rendering.test.tsx +180 -0
- package/src/Tooltip/__tests__/Tooltip.controlled-state.test.tsx +128 -0
- package/src/Tooltip/__tests__/Tooltip.escape-hatches.test.tsx +73 -0
- package/src/Tooltip/__tests__/Tooltip.focus-interaction.test.tsx +88 -0
- package/src/Tooltip/__tests__/Tooltip.hover-interaction.test.tsx +179 -0
- package/src/Tooltip/__tests__/Tooltip.keyboard-interaction.test.tsx +85 -0
- package/src/Tooltip/__tests__/Tooltip.uncontrolled-state.test.tsx +67 -0
- package/src/Tooltip/hooks/index.ts +4 -0
- package/src/Tooltip/hooks/useTooltipContent.ts +53 -0
- package/src/Tooltip/hooks/useTooltipProvider.ts +41 -0
- package/src/Tooltip/hooks/useTooltipRoot.ts +106 -0
- package/src/Tooltip/hooks/useTooltipTrigger.ts +44 -0
- package/src/Tooltip/index.ts +1 -0
- package/src/Tooltip/types.ts +64 -0
- package/src/Tree/README.md +339 -0
- package/src/Tree/Tree.tsx +571 -0
- package/src/Tree/TreeContext.ts +24 -0
- package/src/Tree/__tests__/Tree.aria.test.tsx +53 -0
- package/src/Tree/__tests__/Tree.asChild.test.tsx +134 -0
- package/src/Tree/__tests__/Tree.basic-rendering.test.tsx +111 -0
- package/src/Tree/__tests__/Tree.branch-behaviour.test.tsx +87 -0
- package/src/Tree/__tests__/Tree.controlled-expansion.test.tsx +92 -0
- package/src/Tree/__tests__/Tree.data-attributes.test.tsx +88 -0
- package/src/Tree/__tests__/Tree.disabled-items.test.tsx +196 -0
- package/src/Tree/__tests__/Tree.error-handling.test.tsx +71 -0
- package/src/Tree/__tests__/Tree.forceMount.test.tsx +72 -0
- package/src/Tree/__tests__/Tree.keyboard-interaction.test.tsx +150 -0
- package/src/Tree/__tests__/Tree.multiple-selection.test.tsx +151 -0
- package/src/Tree/__tests__/Tree.range-selection.test.tsx +200 -0
- package/src/Tree/__tests__/Tree.recursion-depth.test.tsx +73 -0
- package/src/Tree/__tests__/Tree.roving-tabindex.test.tsx +117 -0
- package/src/Tree/__tests__/Tree.selection-path.test.tsx +404 -0
- package/src/Tree/__tests__/Tree.single-selection.test.tsx +108 -0
- package/src/Tree/__tests__/Tree.uncontrolled-expansion.test.tsx +69 -0
- package/src/Tree/hooks/index.ts +3 -0
- package/src/Tree/hooks/useTreeItemKeyboard.ts +86 -0
- package/src/Tree/hooks/useTreePath.ts +68 -0
- package/src/Tree/hooks/useTreeRoot.ts +279 -0
- package/src/Tree/index.ts +3 -0
- package/src/Tree/types.ts +224 -0
- package/src/Tree/utils.ts +59 -0
- package/src/VisuallyHidden/README.md +58 -0
- package/src/VisuallyHidden/VisuallyHidden.tsx +67 -0
- package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +59 -0
- package/src/VisuallyHidden/index.ts +2 -0
- package/src/VisuallyHidden/types.ts +5 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useCollection.ts +74 -0
- package/src/hooks/useControllableState.ts +81 -0
- package/src/hooks/useRovingTabindex.ts +178 -0
- package/src/index.ts +38 -0
- package/src/test/intersectionObserverPolyfill.ts +83 -0
- package/src/test/popoverPolyfill.ts +86 -0
- package/src/test/scrollPolyfill.ts +23 -0
- package/src/types.ts +13 -0
- package/src/utils/__tests__/createStrictContext.test.tsx +69 -0
- package/src/utils/__tests__/deriveId.test.ts +28 -0
- package/src/utils/__tests__/getKeyToActionMap.test.ts +106 -0
- package/src/utils/createStrictContext.ts +49 -0
- package/src/utils/deriveId.ts +31 -0
- package/src/utils/getKeyToActionMap.ts +95 -0
- package/src/utils/index.ts +3 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { Ref } from "react";
|
|
2
|
+
|
|
3
|
+
import { Slot } from "../Slot";
|
|
4
|
+
|
|
5
|
+
import { CollapsibleContext, useCollapsibleContext } from "./CollapsibleContext";
|
|
6
|
+
import { useCollapsibleRoot, useCollapsibleTrigger } from "./hooks";
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
CollapsibleRootProps,
|
|
10
|
+
CollapsibleTriggerProps,
|
|
11
|
+
CollapsibleContentProps,
|
|
12
|
+
CollapsibleTriggerIconProps,
|
|
13
|
+
} from "./types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The root of a Collapsible widget — owns the open/closed state, provides
|
|
17
|
+
* context to descendants, and renders a plain `<div>`.
|
|
18
|
+
*
|
|
19
|
+
* Supports two state modes, statically discriminated at the type level:
|
|
20
|
+
*
|
|
21
|
+
* - **Uncontrolled** — pass `defaultOpen` (or omit it to start closed).
|
|
22
|
+
* The component owns and updates the open state internally; consumers
|
|
23
|
+
* may still observe transitions by passing `onOpenChange`.
|
|
24
|
+
* - **Controlled** — pass `open` *and* `onOpenChange` together. The parent
|
|
25
|
+
* owns the open value; the component defers every state change back
|
|
26
|
+
* through the callback. Passing `defaultOpen` alongside `open` is a type
|
|
27
|
+
* error.
|
|
28
|
+
*
|
|
29
|
+
* Unlike Accordion in this library, Collapsible fires `onOpenChange` in
|
|
30
|
+
* **both** uncontrolled and controlled modes.
|
|
31
|
+
*
|
|
32
|
+
* **Disabled.** Pass `disabled` to render `aria-disabled="true"` on the
|
|
33
|
+
* Trigger and short-circuit click and keyboard activation. The Trigger
|
|
34
|
+
* remains focusable so keyboard users can discover it. `data-disabled` is
|
|
35
|
+
* mirrored onto Root, Trigger, and Content so a single selector covers
|
|
36
|
+
* every sub-component.
|
|
37
|
+
*
|
|
38
|
+
* **Styling hooks.** `data-state="open" | "closed"` and
|
|
39
|
+
* `data-disabled="true" | "false"` are emitted on the rendered container.
|
|
40
|
+
*
|
|
41
|
+
* @example Uncontrolled
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <Collapsible.Root defaultOpen>
|
|
44
|
+
* <Collapsible.Trigger>Toggle</Collapsible.Trigger>
|
|
45
|
+
* <Collapsible.Content>Hidden content</Collapsible.Content>
|
|
46
|
+
* </Collapsible.Root>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example Controlled
|
|
50
|
+
* ```tsx
|
|
51
|
+
* const [open, setOpen] = useState(false);
|
|
52
|
+
*
|
|
53
|
+
* <Collapsible.Root open={open} onOpenChange={setOpen}>
|
|
54
|
+
* <Collapsible.Trigger>Toggle</Collapsible.Trigger>
|
|
55
|
+
* <Collapsible.Content>Hidden content</Collapsible.Content>
|
|
56
|
+
* </Collapsible.Root>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function CollapsibleRoot({
|
|
60
|
+
children,
|
|
61
|
+
defaultOpen = false,
|
|
62
|
+
open: controlledOpen,
|
|
63
|
+
onOpenChange,
|
|
64
|
+
disabled = false,
|
|
65
|
+
...rest
|
|
66
|
+
}: CollapsibleRootProps) {
|
|
67
|
+
const { contextValue } = useCollapsibleRoot(
|
|
68
|
+
controlledOpen,
|
|
69
|
+
defaultOpen,
|
|
70
|
+
onOpenChange,
|
|
71
|
+
disabled,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<CollapsibleContext.Provider value={contextValue}>
|
|
76
|
+
<div
|
|
77
|
+
data-state={contextValue.open ? "open" : "closed"}
|
|
78
|
+
data-disabled={disabled}
|
|
79
|
+
{...rest}
|
|
80
|
+
>
|
|
81
|
+
{children}
|
|
82
|
+
</div>
|
|
83
|
+
</CollapsibleContext.Provider>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
CollapsibleRoot.displayName = "CollapsibleRoot";
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The button that toggles the collapsible open or closed. Renders
|
|
91
|
+
* `<button type="button">` by default and wires up all required ARIA
|
|
92
|
+
* attributes, click handling, and keyboard activation automatically.
|
|
93
|
+
*
|
|
94
|
+
* **Disabled behaviour.** When the parent `Collapsible.Root` is `disabled`
|
|
95
|
+
* the Trigger is rendered with `aria-disabled="true"` and
|
|
96
|
+
* `data-disabled="true"` instead of the native HTML `disabled` attribute.
|
|
97
|
+
* This keeps the button focusable so keyboard users can discover it,
|
|
98
|
+
* while preventing toggle activation. Click handlers and keyboard
|
|
99
|
+
* activation are short-circuited entirely — no toggle, no consumer
|
|
100
|
+
* `onClick`.
|
|
101
|
+
*
|
|
102
|
+
* **`asChild` prop.** Pass `asChild` to render an arbitrary child element
|
|
103
|
+
* instead of the default `<button>`. All ARIA attributes, event handlers,
|
|
104
|
+
* and the internal ref are merged onto the child following the standard
|
|
105
|
+
* composition rules:
|
|
106
|
+
* - Event handlers compose — child's handler runs first, then the trigger's.
|
|
107
|
+
* - `style` is shallow-merged (child wins on collisions).
|
|
108
|
+
* - `className` strings are concatenated.
|
|
109
|
+
* - Refs from both sides are composed via `composeRefs`.
|
|
110
|
+
*
|
|
111
|
+
* When `asChild` is `true` and the parent is `disabled`, `role="button"`
|
|
112
|
+
* is automatically injected so that `aria-disabled` is semantically valid
|
|
113
|
+
* on non-button elements (e.g. `<a>`, `<div>`). Without a button role the
|
|
114
|
+
* `aria-disabled` attribute has no defined meaning in the ARIA spec.
|
|
115
|
+
*
|
|
116
|
+
* **Keyboard activation.** Native `<button>` activates on `Enter` and
|
|
117
|
+
* `Space` for free. Under `asChild`, the rendered element may not (e.g.
|
|
118
|
+
* `<div role="button">`), so the Trigger handles `Enter` and `Space`
|
|
119
|
+
* explicitly: `preventDefault` suppresses any native activation (such as
|
|
120
|
+
* an anchor following its `href`) and the toggle is dispatched directly.
|
|
121
|
+
*
|
|
122
|
+
* **Ref forwarding.** A `ref` prop (React 19 ref-as-prop style) is
|
|
123
|
+
* forwarded to the underlying DOM element — useful for imperative focus
|
|
124
|
+
* management. The generic defaults to `HTMLButtonElement`; supply a
|
|
125
|
+
* different type when using `asChild` with a non-button element.
|
|
126
|
+
*
|
|
127
|
+
* **Styling hooks.**
|
|
128
|
+
* - `data-state="open" | "closed"` on the rendered element.
|
|
129
|
+
* - `data-disabled="true" | "false"`.
|
|
130
|
+
*
|
|
131
|
+
* @example Basic
|
|
132
|
+
* ```tsx
|
|
133
|
+
* <Collapsible.Trigger>Toggle</Collapsible.Trigger>
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @example asChild — render an anchor with collapsible semantics
|
|
137
|
+
* ```tsx
|
|
138
|
+
* <Collapsible.Trigger<HTMLAnchorElement> asChild>
|
|
139
|
+
* <a href="#section">Toggle</a>
|
|
140
|
+
* </Collapsible.Trigger>
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function CollapsibleTrigger<
|
|
144
|
+
T extends HTMLElement = HTMLButtonElement,
|
|
145
|
+
>({
|
|
146
|
+
ref,
|
|
147
|
+
children,
|
|
148
|
+
onClick,
|
|
149
|
+
onKeyDown,
|
|
150
|
+
asChild = false,
|
|
151
|
+
...rest
|
|
152
|
+
}: CollapsibleTriggerProps<T>) {
|
|
153
|
+
const { triggerProps } = useCollapsibleTrigger({
|
|
154
|
+
ref: ref as Ref<HTMLButtonElement>,
|
|
155
|
+
onClick,
|
|
156
|
+
onKeyDown,
|
|
157
|
+
asChild,
|
|
158
|
+
...rest,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (asChild) {
|
|
162
|
+
return <Slot {...triggerProps}>{children}</Slot>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<button type="button" {...triggerProps}>
|
|
167
|
+
{children}
|
|
168
|
+
</button>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
CollapsibleTrigger.displayName = "CollapsibleTrigger";
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The panel that is revealed when the associated `Collapsible.Trigger` is
|
|
176
|
+
* activated. Renders a `<div>` whose visibility is controlled by the
|
|
177
|
+
* `hidden` attribute and whose `id` is automatically wired to the
|
|
178
|
+
* Trigger's `aria-controls`.
|
|
179
|
+
*
|
|
180
|
+
* **`forceMount` prop.** By default the panel is removed from the visual
|
|
181
|
+
* and accessibility trees with `hidden` when closed. Pass `forceMount` to
|
|
182
|
+
* keep the panel in the DOM at all times so consumers can drive open /
|
|
183
|
+
* close transitions with CSS — for instance with CSS Grid:
|
|
184
|
+
*
|
|
185
|
+
* ```css
|
|
186
|
+
* .panel {
|
|
187
|
+
* display: grid;
|
|
188
|
+
* grid-template-rows: 0fr;
|
|
189
|
+
* transition: grid-template-rows 250ms;
|
|
190
|
+
* }
|
|
191
|
+
* .panel[data-state="open"] {
|
|
192
|
+
* grid-template-rows: 1fr;
|
|
193
|
+
* }
|
|
194
|
+
* .panel > * {
|
|
195
|
+
* overflow: hidden;
|
|
196
|
+
* }
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* When `forceMount` is `true` and the panel is closed, `aria-hidden="true"`
|
|
200
|
+
* is set automatically so assistive technology ignores the off-screen
|
|
201
|
+
* content. It is removed when the panel opens. Consumers can override
|
|
202
|
+
* this by passing `aria-hidden` explicitly (it appears after the
|
|
203
|
+
* automatic value in the spread).
|
|
204
|
+
*
|
|
205
|
+
* **Styling hooks.**
|
|
206
|
+
* - `data-state="open" | "closed"` on the rendered element.
|
|
207
|
+
* - `data-disabled="true" | "false"`.
|
|
208
|
+
*
|
|
209
|
+
* @example Default (hidden attribute)
|
|
210
|
+
* ```tsx
|
|
211
|
+
* <Collapsible.Content>Hidden content</Collapsible.Content>
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* @example With forceMount for CSS-driven animation
|
|
215
|
+
* ```tsx
|
|
216
|
+
* <Collapsible.Content forceMount className="panel">
|
|
217
|
+
* Content that animates open and closed.
|
|
218
|
+
* </Collapsible.Content>
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
export function CollapsibleContent({
|
|
222
|
+
children,
|
|
223
|
+
forceMount = false,
|
|
224
|
+
...rest
|
|
225
|
+
}: CollapsibleContentProps) {
|
|
226
|
+
const { open, disabled, contentId } = useCollapsibleContext();
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<div
|
|
230
|
+
id={contentId}
|
|
231
|
+
hidden={forceMount ? undefined : !open}
|
|
232
|
+
aria-hidden={forceMount && !open ? true : undefined}
|
|
233
|
+
data-state={open ? "open" : "closed"}
|
|
234
|
+
data-disabled={disabled}
|
|
235
|
+
{...rest}
|
|
236
|
+
>
|
|
237
|
+
{children}
|
|
238
|
+
</div>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
CollapsibleContent.displayName = "CollapsibleContent";
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A wrapper that hides its icon child from the accessibility tree and
|
|
246
|
+
* provides a `data-state` hook for open/close animations. Accepts any
|
|
247
|
+
* renderable React content as a child — an inline `<svg>`, a component
|
|
248
|
+
* from a third-party icon library (lucide-react, react-icons, etc.), or
|
|
249
|
+
* any custom icon component.
|
|
250
|
+
*
|
|
251
|
+
* Renders a `<span>` with `aria-hidden="true"` around the child so the
|
|
252
|
+
* icon is hidden from assistive technology regardless of whether the
|
|
253
|
+
* child component forwards unknown props or emits its own ARIA surface.
|
|
254
|
+
*
|
|
255
|
+
* **Styling hooks.**
|
|
256
|
+
* - `data-state="open" | "closed"` on the rendered `<span>`.
|
|
257
|
+
* - `aria-hidden="true"` on the rendered `<span>`.
|
|
258
|
+
*
|
|
259
|
+
* @example Inline SVG
|
|
260
|
+
* ```tsx
|
|
261
|
+
* <Collapsible.Trigger>
|
|
262
|
+
* Toggle
|
|
263
|
+
* <Collapsible.TriggerIcon>
|
|
264
|
+
* <svg viewBox="0 0 24 24"><path d="M6 9l6 6 6-6" /></svg>
|
|
265
|
+
* </Collapsible.TriggerIcon>
|
|
266
|
+
* </Collapsible.Trigger>
|
|
267
|
+
* ```
|
|
268
|
+
*
|
|
269
|
+
* @example Third-party icon component
|
|
270
|
+
* ```tsx
|
|
271
|
+
* import { ChevronDown } from "lucide-react";
|
|
272
|
+
*
|
|
273
|
+
* <Collapsible.Trigger>
|
|
274
|
+
* Toggle
|
|
275
|
+
* <Collapsible.TriggerIcon>
|
|
276
|
+
* <ChevronDown />
|
|
277
|
+
* </Collapsible.TriggerIcon>
|
|
278
|
+
* </Collapsible.Trigger>
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
export function CollapsibleTriggerIcon({
|
|
282
|
+
children,
|
|
283
|
+
...rest
|
|
284
|
+
}: CollapsibleTriggerIconProps) {
|
|
285
|
+
const { open } = useCollapsibleContext();
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<span
|
|
289
|
+
aria-hidden="true"
|
|
290
|
+
data-state={open ? "open" : "closed"}
|
|
291
|
+
{...rest}
|
|
292
|
+
>
|
|
293
|
+
{children}
|
|
294
|
+
</span>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
CollapsibleTriggerIcon.displayName = "CollapsibleTriggerIcon";
|
|
299
|
+
|
|
300
|
+
type CollapsibleCompound = typeof CollapsibleRoot & {
|
|
301
|
+
Root: typeof CollapsibleRoot;
|
|
302
|
+
Trigger: typeof CollapsibleTrigger;
|
|
303
|
+
Content: typeof CollapsibleContent;
|
|
304
|
+
TriggerIcon: typeof CollapsibleTriggerIcon;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const CollapsibleCompound: CollapsibleCompound = Object.assign(CollapsibleRoot, {
|
|
308
|
+
Root: CollapsibleRoot,
|
|
309
|
+
Trigger: CollapsibleTrigger,
|
|
310
|
+
Content: CollapsibleContent,
|
|
311
|
+
TriggerIcon: CollapsibleTriggerIcon,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
CollapsibleCompound.displayName = "Collapsible";
|
|
315
|
+
|
|
316
|
+
export { CollapsibleCompound as Collapsible };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createStrictContext } from "../utils";
|
|
2
|
+
import { CollapsibleContextValue } from "./types";
|
|
3
|
+
|
|
4
|
+
export const [CollapsibleContext, useCollapsibleContext] =
|
|
5
|
+
createStrictContext<CollapsibleContextValue>(
|
|
6
|
+
"Collapsible sub-components must be rendered inside a <Collapsible.Root>.",
|
|
7
|
+
);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Collapsible
|
|
2
|
+
|
|
3
|
+
A compound component for showing and hiding a single panel of content.
|
|
4
|
+
The single-item analogue of [Accordion](../Accordion/README.md).
|
|
5
|
+
|
|
6
|
+
```tsx
|
|
7
|
+
import { Collapsible } from "@primitiv-ui/react";
|
|
8
|
+
|
|
9
|
+
<Collapsible.Root defaultOpen>
|
|
10
|
+
<Collapsible.Trigger>Toggle</Collapsible.Trigger>
|
|
11
|
+
<Collapsible.Content>
|
|
12
|
+
The content revealed by the trigger.
|
|
13
|
+
</Collapsible.Content>
|
|
14
|
+
</Collapsible.Root>;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Sub-components
|
|
18
|
+
|
|
19
|
+
| Export | Role | Notes |
|
|
20
|
+
| ------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
21
|
+
| `Collapsible.Root` | State owner | Uncontrolled (`defaultOpen`) or controlled (`open` + `onOpenChange`); `disabled` short-circuits Trigger activation |
|
|
22
|
+
| `Collapsible.Trigger` | Toggle button | Supports `asChild`, ref forwarding, explicit Enter/Space handling |
|
|
23
|
+
| `Collapsible.Content` | Hidden panel | Hidden via the `hidden` attribute; supports `forceMount` for CSS animation |
|
|
24
|
+
| `Collapsible.TriggerIcon` | Icon wrapper | Injects `aria-hidden` and `data-state` onto a decorative icon |
|
|
25
|
+
|
|
26
|
+
## Keyboard interaction
|
|
27
|
+
|
|
28
|
+
| Key | Behaviour |
|
|
29
|
+
| ----------------- | ---------------------- |
|
|
30
|
+
| `Enter` / `Space` | Toggle the collapsible |
|
|
31
|
+
|
|
32
|
+
`<button>` activates on `Enter` and `Space` natively. The Trigger also
|
|
33
|
+
handles both keys explicitly so that `asChild` rendering on non-button
|
|
34
|
+
elements (anchors, divs, custom components) still toggles correctly.
|
|
35
|
+
`preventDefault` suppresses native activation (e.g. an anchor following
|
|
36
|
+
its `href`) so the toggle never double-fires.
|
|
37
|
+
|
|
38
|
+
## State modes
|
|
39
|
+
|
|
40
|
+
- **Uncontrolled** — pass `defaultOpen` (or omit to start closed).
|
|
41
|
+
`onOpenChange` is optional and fires on every toggle.
|
|
42
|
+
- **Controlled** — pass `open` and `onOpenChange` together. The parent
|
|
43
|
+
owns the value; the component defers every state change back through
|
|
44
|
+
the callback.
|
|
45
|
+
|
|
46
|
+
The two shapes are discriminated at the type level: passing `defaultOpen`
|
|
47
|
+
alongside `open` is a type error.
|
|
48
|
+
|
|
49
|
+
Unlike `Accordion`, `Collapsible` fires `onOpenChange` in **both** modes.
|
|
50
|
+
|
|
51
|
+
## Disabled
|
|
52
|
+
|
|
53
|
+
Pass `disabled` on `Collapsible.Root` to render `aria-disabled="true"` on
|
|
54
|
+
the Trigger (without the native `disabled` attribute, so it remains
|
|
55
|
+
focusable for keyboard discovery) and short-circuit click and keyboard
|
|
56
|
+
activation:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<Collapsible.Root disabled>
|
|
60
|
+
<Collapsible.Trigger>Toggle</Collapsible.Trigger>
|
|
61
|
+
<Collapsible.Content>Currently unavailable.</Collapsible.Content>
|
|
62
|
+
</Collapsible.Root>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`data-disabled` is mirrored onto Root, Trigger, and Content so a single
|
|
66
|
+
`[data-disabled="true"]` selector covers every sub-component.
|
|
67
|
+
|
|
68
|
+
## `forceMount` for CSS animations
|
|
69
|
+
|
|
70
|
+
By default the content panel is removed from accessibility and visual
|
|
71
|
+
rendering via the `hidden` attribute. Pass `forceMount` to keep it in the
|
|
72
|
+
DOM and drive open/close visually with CSS. The recommended pattern uses
|
|
73
|
+
CSS Grid so consumers don't have to measure content height in JavaScript:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<Collapsible.Content forceMount className="panel">
|
|
77
|
+
Content that animates open and closed.
|
|
78
|
+
</Collapsible.Content>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```css
|
|
82
|
+
.panel {
|
|
83
|
+
display: grid;
|
|
84
|
+
grid-template-rows: 0fr;
|
|
85
|
+
transition: grid-template-rows 250ms ease;
|
|
86
|
+
}
|
|
87
|
+
.panel[data-state="open"] {
|
|
88
|
+
grid-template-rows: 1fr;
|
|
89
|
+
}
|
|
90
|
+
.panel > * {
|
|
91
|
+
overflow: hidden;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
When `forceMount` is active and the panel is closed, `aria-hidden="true"`
|
|
96
|
+
is applied automatically so assistive technology skips the off-screen
|
|
97
|
+
content. It is removed when the panel opens. Consumers can override this
|
|
98
|
+
by passing `aria-hidden` explicitly.
|
|
99
|
+
|
|
100
|
+
## `asChild` composition
|
|
101
|
+
|
|
102
|
+
`Collapsible.Trigger` accepts an `asChild` prop to render any child
|
|
103
|
+
element with full collapsible semantics. All ARIA attributes, event
|
|
104
|
+
handlers, and the internal ref are merged onto the child (child handler
|
|
105
|
+
runs first, then the trigger's):
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<Collapsible.Trigger<HTMLAnchorElement> asChild>
|
|
109
|
+
<a href="#section">Toggle</a>
|
|
110
|
+
</Collapsible.Trigger>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`Enter` and `Space` are handled in `onKeyDown` so non-button elements
|
|
114
|
+
(e.g. `<a>`, `<div>`) toggle correctly without relying on native click
|
|
115
|
+
behaviour. `preventDefault` suppresses native activation so an anchor
|
|
116
|
+
doesn't follow its `href` while toggling.
|
|
117
|
+
|
|
118
|
+
When `asChild` is combined with `disabled`, `role="button"` is injected
|
|
119
|
+
automatically so that `aria-disabled` is semantically valid on non-button
|
|
120
|
+
elements:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<Collapsible.Root disabled>
|
|
124
|
+
<Collapsible.Trigger asChild>
|
|
125
|
+
<a href="#section">Toggle</a>
|
|
126
|
+
{/* rendered with role="button" aria-disabled="true" */}
|
|
127
|
+
</Collapsible.Trigger>
|
|
128
|
+
<Collapsible.Content>…</Collapsible.Content>
|
|
129
|
+
</Collapsible.Root>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Trigger icon
|
|
133
|
+
|
|
134
|
+
Wrap a decorative icon in `Collapsible.TriggerIcon` to hide it from
|
|
135
|
+
assistive technology and expose a `data-state` hook for rotation
|
|
136
|
+
animations. The child can be any renderable React content — an inline
|
|
137
|
+
`<svg>`, a component from a third-party icon library (lucide-react,
|
|
138
|
+
react-icons, etc.), or a custom icon component. `aria-hidden` and
|
|
139
|
+
`data-state` are placed on a wrapping `<span>`, so they work regardless
|
|
140
|
+
of whether the icon component forwards unknown props.
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { ChevronDown } from "lucide-react";
|
|
144
|
+
|
|
145
|
+
<Collapsible.Trigger>
|
|
146
|
+
Toggle
|
|
147
|
+
<Collapsible.TriggerIcon>
|
|
148
|
+
<ChevronDown />
|
|
149
|
+
</Collapsible.TriggerIcon>
|
|
150
|
+
</Collapsible.Trigger>;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Styling hooks
|
|
154
|
+
|
|
155
|
+
```css
|
|
156
|
+
/* Trigger open/closed — rotate a chevron icon */
|
|
157
|
+
[data-state="open"] .chevron {
|
|
158
|
+
transform: rotate(180deg);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Content panel — animate via CSS Grid (see forceMount above) */
|
|
162
|
+
.panel[data-state="closed"] {
|
|
163
|
+
grid-template-rows: 0fr;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Disabled trigger */
|
|
167
|
+
[aria-disabled="true"] {
|
|
168
|
+
opacity: 0.5;
|
|
169
|
+
cursor: not-allowed;
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`data-state` (`"open"` | `"closed"`) and `data-disabled` (`"true"` |
|
|
174
|
+
`"false"`) are available on every rendered sub-component.
|