@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,329 @@
|
|
|
1
|
+
import { Ref } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
|
|
4
|
+
import { Slot } from "../Slot";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MillerColumnsContext,
|
|
8
|
+
MillerColumnsColumnContext,
|
|
9
|
+
MillerColumnsItemContext,
|
|
10
|
+
} from "./MillerColumnsContext";
|
|
11
|
+
import {
|
|
12
|
+
useMillerColumnsColumn,
|
|
13
|
+
useMillerColumnsContext,
|
|
14
|
+
useMillerColumnsItem,
|
|
15
|
+
useMillerColumnsItemContext,
|
|
16
|
+
useMillerColumnsResizeHandle,
|
|
17
|
+
useMillerColumnsRoot,
|
|
18
|
+
} from "./hooks";
|
|
19
|
+
|
|
20
|
+
import { partitionItemChildren } from "./utils";
|
|
21
|
+
|
|
22
|
+
import type {
|
|
23
|
+
MillerColumnsRootProps,
|
|
24
|
+
MillerColumnsColumnProps,
|
|
25
|
+
MillerColumnsItemProps,
|
|
26
|
+
MillerColumnsItemIndicatorProps,
|
|
27
|
+
MillerColumnsResizeHandleProps,
|
|
28
|
+
MillerColumnsPreviewPanelProps,
|
|
29
|
+
} from "./types";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The root of a Miller Columns widget — a horizontal strip of vertical
|
|
33
|
+
* lists where selecting a node reveals its children in the next column.
|
|
34
|
+
*
|
|
35
|
+
* Renders the strip container (`role="tree"`) into which every
|
|
36
|
+
* `MillerColumns.Column` projects itself via a portal. Authoring the tree
|
|
37
|
+
* is recursive: an `Item` declares its child column as a *nested*
|
|
38
|
+
* `<MillerColumns.Column>`, and the strip flattens those nested columns
|
|
39
|
+
* into a single left-to-right row.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <MillerColumns.Root>
|
|
44
|
+
* <MillerColumns.Column>
|
|
45
|
+
* <MillerColumns.Item value="docs">Docs</MillerColumns.Item>
|
|
46
|
+
* </MillerColumns.Column>
|
|
47
|
+
* </MillerColumns.Root>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function MillerColumnsRoot({
|
|
51
|
+
children,
|
|
52
|
+
defaultValue,
|
|
53
|
+
value,
|
|
54
|
+
onValueChange,
|
|
55
|
+
...rest
|
|
56
|
+
}: MillerColumnsRootProps) {
|
|
57
|
+
const { contextValue, columnCount, registerSlotRef, stripRef } =
|
|
58
|
+
useMillerColumnsRoot(value, defaultValue, onValueChange);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<MillerColumnsContext.Provider value={contextValue}>
|
|
62
|
+
<div
|
|
63
|
+
ref={stripRef}
|
|
64
|
+
role="tree"
|
|
65
|
+
data-miller-columns-strip=""
|
|
66
|
+
data-orientation="horizontal"
|
|
67
|
+
{...rest}
|
|
68
|
+
>
|
|
69
|
+
{Array.from({ length: columnCount }, (_, depth) => (
|
|
70
|
+
<div
|
|
71
|
+
key={depth}
|
|
72
|
+
data-miller-columns-slot=""
|
|
73
|
+
style={{ display: "contents" }}
|
|
74
|
+
ref={registerSlotRef(depth)}
|
|
75
|
+
/>
|
|
76
|
+
))}
|
|
77
|
+
{children}
|
|
78
|
+
</div>
|
|
79
|
+
</MillerColumnsContext.Provider>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
MillerColumnsRoot.displayName = "MillerColumnsRoot";
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A single vertical list of items within the strip. Renders a
|
|
87
|
+
* `<div role="group">` that is portal-projected into the `Root` strip, so
|
|
88
|
+
* a `Column` nested inside an `Item` still appears side-by-side with its
|
|
89
|
+
* ancestors rather than in document flow.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```tsx
|
|
93
|
+
* <MillerColumns.Column>
|
|
94
|
+
* <MillerColumns.Item value="a">A</MillerColumns.Item>
|
|
95
|
+
* </MillerColumns.Column>
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export function MillerColumnsColumn({
|
|
99
|
+
children,
|
|
100
|
+
style,
|
|
101
|
+
...rest
|
|
102
|
+
}: MillerColumnsColumnProps) {
|
|
103
|
+
const { slot, depth, width, columnContextValue } = useMillerColumnsColumn();
|
|
104
|
+
|
|
105
|
+
if (!slot) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return createPortal(
|
|
110
|
+
<MillerColumnsColumnContext.Provider value={columnContextValue}>
|
|
111
|
+
<div
|
|
112
|
+
role="group"
|
|
113
|
+
data-miller-columns-column=""
|
|
114
|
+
data-depth={depth}
|
|
115
|
+
style={{ ...style, ...(width !== undefined ? { width } : {}) }}
|
|
116
|
+
{...rest}
|
|
117
|
+
>
|
|
118
|
+
{children}
|
|
119
|
+
</div>
|
|
120
|
+
</MillerColumnsColumnContext.Provider>,
|
|
121
|
+
slot,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
MillerColumnsColumn.displayName = "MillerColumnsColumn";
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A single selectable node within a `Column`. Renders a
|
|
129
|
+
* `<div role="treeitem">` for its cell content.
|
|
130
|
+
*
|
|
131
|
+
* The {@link MillerColumnsItemProps.value | `value`} prop is the stable
|
|
132
|
+
* identifier used to match this item against the active selection path.
|
|
133
|
+
*
|
|
134
|
+
* An `Item` becomes a branch by nesting a `<MillerColumns.Column>` among
|
|
135
|
+
* its children. That child column is mounted (and projected into the
|
|
136
|
+
* strip) only while the item is selected; an item with no nested column
|
|
137
|
+
* is a leaf.
|
|
138
|
+
*
|
|
139
|
+
* **`asChild` prop.** Pass `asChild` to render the cell as a
|
|
140
|
+
* consumer-supplied element (e.g. an `<a>`) instead of the default
|
|
141
|
+
* `<div>`. All treeitem ARIA attributes, event handlers, and the internal
|
|
142
|
+
* ref are merged onto that child. A nested `<MillerColumns.Column>` is
|
|
143
|
+
* still declared as a sibling of the cell element.
|
|
144
|
+
*
|
|
145
|
+
* **Ref forwarding.** A `ref` prop (React 19 ref-as-prop style) is
|
|
146
|
+
* forwarded to the rendered element and composed with the library's
|
|
147
|
+
* internal ref.
|
|
148
|
+
*
|
|
149
|
+
* @example Leaf
|
|
150
|
+
* ```tsx
|
|
151
|
+
* <MillerColumns.Item value="guides">Guides</MillerColumns.Item>
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @example Branch
|
|
155
|
+
* ```tsx
|
|
156
|
+
* <MillerColumns.Item value="docs">
|
|
157
|
+
* Docs
|
|
158
|
+
* <MillerColumns.Column>
|
|
159
|
+
* <MillerColumns.Item value="guides">Guides</MillerColumns.Item>
|
|
160
|
+
* </MillerColumns.Column>
|
|
161
|
+
* </MillerColumns.Item>
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function MillerColumnsItem<T extends HTMLElement = HTMLDivElement>({
|
|
165
|
+
children,
|
|
166
|
+
ref,
|
|
167
|
+
asChild = false,
|
|
168
|
+
...props
|
|
169
|
+
}: MillerColumnsItemProps<T>) {
|
|
170
|
+
const { cell, column } = partitionItemChildren(children);
|
|
171
|
+
const { itemProps, selected, itemContextValue } = useMillerColumnsItem(
|
|
172
|
+
{ ref: ref as Ref<HTMLDivElement>, ...props },
|
|
173
|
+
column !== null,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<MillerColumnsItemContext.Provider value={itemContextValue}>
|
|
178
|
+
{asChild ? (
|
|
179
|
+
<Slot {...itemProps}>{cell[0]}</Slot>
|
|
180
|
+
) : (
|
|
181
|
+
<div {...itemProps}>{cell}</div>
|
|
182
|
+
)}
|
|
183
|
+
{selected ? column : null}
|
|
184
|
+
</MillerColumnsItemContext.Provider>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
MillerColumnsItem.displayName = "MillerColumnsItem";
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* An optional, decorative affordance for a branch item — typically a
|
|
192
|
+
* chevron or arrow signalling "this item reveals a child column".
|
|
193
|
+
*
|
|
194
|
+
* Renders a `<span aria-hidden="true">` (so the glyph is ignored by
|
|
195
|
+
* assistive technology) **only for branch items**; for a leaf item it
|
|
196
|
+
* renders nothing. Place it among an `Item`'s cell content.
|
|
197
|
+
*
|
|
198
|
+
* **Styling hooks.**
|
|
199
|
+
* - `data-state="selected" | "unselected"` — mirrors the parent item.
|
|
200
|
+
* - `data-has-children` — always present (the indicator only renders
|
|
201
|
+
* for branch items).
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```tsx
|
|
205
|
+
* <MillerColumns.Item value="docs">
|
|
206
|
+
* Docs
|
|
207
|
+
* <MillerColumns.ItemIndicator>▸</MillerColumns.ItemIndicator>
|
|
208
|
+
* <MillerColumns.Column>…</MillerColumns.Column>
|
|
209
|
+
* </MillerColumns.Item>
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export function MillerColumnsItemIndicator({
|
|
213
|
+
children,
|
|
214
|
+
...rest
|
|
215
|
+
}: MillerColumnsItemIndicatorProps) {
|
|
216
|
+
const { selected, hasChildren } = useMillerColumnsItemContext();
|
|
217
|
+
|
|
218
|
+
if (!hasChildren) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<span
|
|
224
|
+
aria-hidden="true"
|
|
225
|
+
data-state={selected ? "selected" : "unselected"}
|
|
226
|
+
data-has-children=""
|
|
227
|
+
{...rest}
|
|
228
|
+
>
|
|
229
|
+
{children}
|
|
230
|
+
</span>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
MillerColumnsItemIndicator.displayName = "MillerColumnsItemIndicator";
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* A drag-to-resize affordance for the column it is rendered in. Renders a
|
|
238
|
+
* `<div role="separator" aria-orientation="vertical">` that, while
|
|
239
|
+
* pointer-dragged, drives that column's width as state on the `Root`.
|
|
240
|
+
*
|
|
241
|
+
* Must be rendered among a `MillerColumns.Column`'s children — it reads
|
|
242
|
+
* the column's depth from context. Position it with CSS (typically
|
|
243
|
+
* absolutely, against the column's trailing edge).
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```tsx
|
|
247
|
+
* <MillerColumns.Column>
|
|
248
|
+
* <MillerColumns.ResizeHandle />
|
|
249
|
+
* <MillerColumns.Item value="a">A</MillerColumns.Item>
|
|
250
|
+
* </MillerColumns.Column>
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
export function MillerColumnsResizeHandle(
|
|
254
|
+
props: MillerColumnsResizeHandleProps,
|
|
255
|
+
) {
|
|
256
|
+
const { handleProps } = useMillerColumnsResizeHandle(props);
|
|
257
|
+
|
|
258
|
+
return <div {...handleProps} />;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
MillerColumnsResizeHandle.displayName = "MillerColumnsResizeHandle";
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* A trailing panel for previewing the current selection — the
|
|
265
|
+
* macOS-Finder-style preview pane.
|
|
266
|
+
*
|
|
267
|
+
* Renders a plain `<div data-miller-columns-preview>` as the last child
|
|
268
|
+
* of the strip, sitting to the right of the columns. The component is
|
|
269
|
+
* deliberately content-agnostic: it does not know how to preview an
|
|
270
|
+
* item, so the consumer supplies whatever the panel should show.
|
|
271
|
+
*
|
|
272
|
+
* Pair it with {@link useMillerColumnsSelection} to render content for
|
|
273
|
+
* the current selection. Author it as the **last child** of `Root`, a
|
|
274
|
+
* sibling of the root `Column`.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```tsx
|
|
278
|
+
* function FilePreview() {
|
|
279
|
+
* const { selectedValue } = useMillerColumnsSelection();
|
|
280
|
+
* return selectedValue ? <Preview id={selectedValue} /> : null;
|
|
281
|
+
* }
|
|
282
|
+
*
|
|
283
|
+
* <MillerColumns.Root>
|
|
284
|
+
* <MillerColumns.Column>{items}</MillerColumns.Column>
|
|
285
|
+
* <MillerColumns.PreviewPanel>
|
|
286
|
+
* <FilePreview />
|
|
287
|
+
* </MillerColumns.PreviewPanel>
|
|
288
|
+
* </MillerColumns.Root>;
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
export function MillerColumnsPreviewPanel({
|
|
292
|
+
children,
|
|
293
|
+
...rest
|
|
294
|
+
}: MillerColumnsPreviewPanelProps) {
|
|
295
|
+
useMillerColumnsContext();
|
|
296
|
+
|
|
297
|
+
return (
|
|
298
|
+
<div data-miller-columns-preview="" {...rest}>
|
|
299
|
+
{children}
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
MillerColumnsPreviewPanel.displayName = "MillerColumnsPreviewPanel";
|
|
305
|
+
|
|
306
|
+
type MillerColumnsCompound = typeof MillerColumnsRoot & {
|
|
307
|
+
Root: typeof MillerColumnsRoot;
|
|
308
|
+
Column: typeof MillerColumnsColumn;
|
|
309
|
+
Item: typeof MillerColumnsItem;
|
|
310
|
+
ItemIndicator: typeof MillerColumnsItemIndicator;
|
|
311
|
+
ResizeHandle: typeof MillerColumnsResizeHandle;
|
|
312
|
+
PreviewPanel: typeof MillerColumnsPreviewPanel;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const MillerColumnsCompound: MillerColumnsCompound = Object.assign(
|
|
316
|
+
MillerColumnsRoot,
|
|
317
|
+
{
|
|
318
|
+
Root: MillerColumnsRoot,
|
|
319
|
+
Column: MillerColumnsColumn,
|
|
320
|
+
Item: MillerColumnsItem,
|
|
321
|
+
ItemIndicator: MillerColumnsItemIndicator,
|
|
322
|
+
ResizeHandle: MillerColumnsResizeHandle,
|
|
323
|
+
PreviewPanel: MillerColumnsPreviewPanel,
|
|
324
|
+
},
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
MillerColumnsCompound.displayName = "MillerColumns";
|
|
328
|
+
|
|
329
|
+
export { MillerColumnsCompound as MillerColumns };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createStrictContext } from "../utils";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
MillerColumnsContextValue,
|
|
5
|
+
MillerColumnsColumnContextValue,
|
|
6
|
+
MillerColumnsItemContextValue,
|
|
7
|
+
} from "./types";
|
|
8
|
+
|
|
9
|
+
export const [MillerColumnsContext, useMillerColumnsContext] =
|
|
10
|
+
createStrictContext<MillerColumnsContextValue>(
|
|
11
|
+
"MillerColumns sub-components must be rendered inside <MillerColumns.Root>.",
|
|
12
|
+
"MillerColumnsContext",
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export const [MillerColumnsColumnContext, useMillerColumnsColumnContext] =
|
|
16
|
+
createStrictContext<MillerColumnsColumnContextValue>(
|
|
17
|
+
"MillerColumns.Item must be rendered inside <MillerColumns.Column>.",
|
|
18
|
+
"MillerColumnsColumnContext",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const [MillerColumnsItemContext, useMillerColumnsItemContext] =
|
|
22
|
+
createStrictContext<MillerColumnsItemContextValue>(
|
|
23
|
+
"MillerColumns.ItemIndicator must be rendered inside <MillerColumns.Item>.",
|
|
24
|
+
"MillerColumnsItemContext",
|
|
25
|
+
);
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# MillerColumns
|
|
2
|
+
|
|
3
|
+
A compound component for the **Miller columns** pattern (also called
|
|
4
|
+
cascading lists or the macOS Finder "column view"): a horizontal strip
|
|
5
|
+
of vertical lists where selecting a node reveals its children in the
|
|
6
|
+
next column to the right.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { MillerColumns } from "@primitiv-ui/react";
|
|
10
|
+
|
|
11
|
+
function Node({ node }) {
|
|
12
|
+
return (
|
|
13
|
+
<MillerColumns.Item value={node.id}>
|
|
14
|
+
{node.label}
|
|
15
|
+
{node.children?.length ? (
|
|
16
|
+
<>
|
|
17
|
+
<MillerColumns.ItemIndicator>▸</MillerColumns.ItemIndicator>
|
|
18
|
+
<MillerColumns.Column>
|
|
19
|
+
{node.children.map((child) => (
|
|
20
|
+
<Node key={child.id} node={child} />
|
|
21
|
+
))}
|
|
22
|
+
</MillerColumns.Column>
|
|
23
|
+
</>
|
|
24
|
+
) : null}
|
|
25
|
+
</MillerColumns.Item>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
<MillerColumns.Root defaultValue={["docs", "guides"]}>
|
|
30
|
+
<MillerColumns.Column>
|
|
31
|
+
{tree.map((node) => (
|
|
32
|
+
<Node key={node.id} node={node} />
|
|
33
|
+
))}
|
|
34
|
+
</MillerColumns.Column>
|
|
35
|
+
</MillerColumns.Root>;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Authoring model
|
|
39
|
+
|
|
40
|
+
The tree is authored by **recursive composition** — there is no `data`
|
|
41
|
+
prop. An `Item` becomes a *branch* by nesting a
|
|
42
|
+
`<MillerColumns.Column>` among its children; that nested column lists
|
|
43
|
+
the item's children, each of which is another `Item`. An `Item` with no
|
|
44
|
+
nested column is a *leaf*.
|
|
45
|
+
|
|
46
|
+
Although child columns are authored nested, every `Column` is
|
|
47
|
+
**portal-projected** into the `Root` strip, so the active columns sit
|
|
48
|
+
side-by-side in a single left-to-right row regardless of how deeply
|
|
49
|
+
they were declared.
|
|
50
|
+
|
|
51
|
+
A branch's nested column is only **mounted while that branch is
|
|
52
|
+
selected**, so a consumer's recursive `Node` component naturally stops
|
|
53
|
+
recursing at inactive branches — only the columns along the active path
|
|
54
|
+
are ever rendered.
|
|
55
|
+
|
|
56
|
+
## Sub-components
|
|
57
|
+
|
|
58
|
+
| Export | Role | Notes |
|
|
59
|
+
| ----------------------------- | ------------- | ---------------------------------------------------------------------------------------------- |
|
|
60
|
+
| `MillerColumns.Root` | State owner | Uncontrolled (`defaultValue`) or controlled (`value` + `onValueChange`); renders the strip |
|
|
61
|
+
| `MillerColumns.Column` | List | A vertical list of items, projected into the strip as `role="group"` |
|
|
62
|
+
| `MillerColumns.Item` | Tree node | A `role="treeitem"`; branch when it nests a `Column`. Supports `disabled`, `asChild`, `ref` |
|
|
63
|
+
| `MillerColumns.ItemIndicator` | Icon wrapper | Decorative `aria-hidden` icon, rendered only for branch items |
|
|
64
|
+
| `MillerColumns.ResizeHandle` | Resize grip | A `role="separator"` drag handle that sets its column's width |
|
|
65
|
+
| `MillerColumns.PreviewPanel` | Preview pane | A content-agnostic trailing panel; pair with `useMillerColumnsSelection` |
|
|
66
|
+
|
|
67
|
+
## Selection model
|
|
68
|
+
|
|
69
|
+
The selection is a single **active path** — an array of item ids from
|
|
70
|
+
the root column down to the deepest selected item. Selecting an item at
|
|
71
|
+
depth _d_ truncates the path to _d_ and appends the new id, so every
|
|
72
|
+
column deeper than _d_ closes.
|
|
73
|
+
|
|
74
|
+
- **Uncontrolled** — pass `defaultValue` (or omit it to start with
|
|
75
|
+
nothing selected).
|
|
76
|
+
- **Controlled** — pass `value` and `onValueChange` together. The
|
|
77
|
+
parent owns the path; the component defers every change back through
|
|
78
|
+
the callback.
|
|
79
|
+
|
|
80
|
+
The two shapes are discriminated at the type level: passing
|
|
81
|
+
`defaultValue` alongside `value` is a type error.
|
|
82
|
+
|
|
83
|
+
There is no multi-select — only one path is active at a time.
|
|
84
|
+
|
|
85
|
+
## Keyboard interaction
|
|
86
|
+
|
|
87
|
+
| Key | Behaviour |
|
|
88
|
+
| ------------------- | ---------------------------------------------------------------------- |
|
|
89
|
+
| `ArrowUp` / `ArrowDown` | Move focus within the focused column (wraps, skips disabled items) |
|
|
90
|
+
| `Home` / `End` | Focus the first / last item of the focused column |
|
|
91
|
+
| `Enter` / `Space` | Select the focused item |
|
|
92
|
+
| `ArrowRight` | Branch: select it and move focus to its child column's first item; leaf: no-op |
|
|
93
|
+
| `ArrowLeft` | Move focus to the selected item of the parent column |
|
|
94
|
+
| `Tab` | Move into / out of the whole tree (single tabstop) |
|
|
95
|
+
|
|
96
|
+
The strip is a single roving-tabindex widget: exactly one item is
|
|
97
|
+
tabbable at a time. The tabstop follows the last-focused item and
|
|
98
|
+
defaults to the deepest selected item, falling back to the first item
|
|
99
|
+
of the root column.
|
|
100
|
+
|
|
101
|
+
## Auto-scroll
|
|
102
|
+
|
|
103
|
+
When selecting a branch reveals a new column, the strip scrolls to its
|
|
104
|
+
trailing edge so the newly opened column is brought into view. This
|
|
105
|
+
fires only when a column is *added* — neither the initial render nor
|
|
106
|
+
closing a column scrolls the strip. Scrolling only has a visible
|
|
107
|
+
effect when the strip is itself an overflow container; give
|
|
108
|
+
`[data-miller-columns-strip]` an `overflow-x` for it to take hold.
|
|
109
|
+
|
|
110
|
+
## ARIA
|
|
111
|
+
|
|
112
|
+
- The strip is `role="tree"`.
|
|
113
|
+
- Each `Column` is `role="group"`.
|
|
114
|
+
- Each `Item` is `role="treeitem"` with `aria-level` (1-based column
|
|
115
|
+
depth), `aria-selected`, and — on branch items — `aria-expanded`.
|
|
116
|
+
|
|
117
|
+
## Disabled items
|
|
118
|
+
|
|
119
|
+
Pass `disabled` on an `Item` to render `aria-disabled="true"` and
|
|
120
|
+
`data-disabled`, ignore clicks and activation keys, and skip the item
|
|
121
|
+
during arrow-key navigation. Disabled items remain in the DOM and
|
|
122
|
+
focusable for discovery.
|
|
123
|
+
|
|
124
|
+
## Resizable columns
|
|
125
|
+
|
|
126
|
+
Drop a `MillerColumns.ResizeHandle` among a `Column`'s children to make
|
|
127
|
+
that column drag-resizable. The handle renders a
|
|
128
|
+
`<div role="separator" aria-orientation="vertical">`; while it is
|
|
129
|
+
pointer-dragged it drives that column's width as state on the `Root`,
|
|
130
|
+
applied as the column's inline `width`.
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
<MillerColumns.Column className="column">
|
|
134
|
+
<MillerColumns.ResizeHandle className="resize-handle" />
|
|
135
|
+
{items}
|
|
136
|
+
</MillerColumns.Column>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The handle ships with no styles or position — give it a width and pin
|
|
140
|
+
it to the column's trailing edge yourself:
|
|
141
|
+
|
|
142
|
+
```css
|
|
143
|
+
.column {
|
|
144
|
+
position: relative;
|
|
145
|
+
}
|
|
146
|
+
.resize-handle {
|
|
147
|
+
position: absolute;
|
|
148
|
+
inset-block: 0;
|
|
149
|
+
inset-inline-end: 0;
|
|
150
|
+
width: 6px;
|
|
151
|
+
cursor: col-resize;
|
|
152
|
+
}
|
|
153
|
+
.resize-handle[data-dragging] {
|
|
154
|
+
/* feedback while a drag is in progress */
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The first drag measures the column's current rendered width and
|
|
159
|
+
resizes from there; later drags resume from the last resized width. A
|
|
160
|
+
width is clamped so it can never be dragged below zero — use CSS
|
|
161
|
+
`min-width` for any larger floor. Resize is pointer-only; the handle
|
|
162
|
+
is not keyboard-operable.
|
|
163
|
+
|
|
164
|
+
## Preview panel
|
|
165
|
+
|
|
166
|
+
`MillerColumns.PreviewPanel` is a trailing pane — the macOS-Finder
|
|
167
|
+
"preview" column — rendered as the last child of the strip, to the
|
|
168
|
+
right of the columns. It is deliberately **content-agnostic**: the
|
|
169
|
+
component cannot know how to preview an item, so the consumer decides
|
|
170
|
+
what the panel shows. Author it as the last child of `Root`, a sibling
|
|
171
|
+
of the root `Column`:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
<MillerColumns.Root>
|
|
175
|
+
<MillerColumns.Column>{items}</MillerColumns.Column>
|
|
176
|
+
<MillerColumns.PreviewPanel>
|
|
177
|
+
<FilePreview />
|
|
178
|
+
</MillerColumns.PreviewPanel>
|
|
179
|
+
</MillerColumns.Root>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
To render content for whatever is selected, read the selection with
|
|
183
|
+
the `useMillerColumnsSelection` hook from any component inside `Root`:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { useMillerColumnsSelection } from "@primitiv-ui/react";
|
|
187
|
+
|
|
188
|
+
function FilePreview() {
|
|
189
|
+
const { path, selectedValue } = useMillerColumnsSelection();
|
|
190
|
+
if (!selectedValue) {
|
|
191
|
+
return <p>Nothing selected</p>;
|
|
192
|
+
}
|
|
193
|
+
return <Preview id={selectedValue} />;
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`useMillerColumnsSelection` returns the active `path` and the deepest
|
|
198
|
+
`selectedValue` (`undefined` when nothing is selected). It works for
|
|
199
|
+
both controlled and uncontrolled roots, and throws if called outside
|
|
200
|
+
`MillerColumns.Root`.
|
|
201
|
+
|
|
202
|
+
The panel ships with no ARIA role. The strip is a `role="tree"`, whose
|
|
203
|
+
conforming children are `treeitem`s and `group`s — so give the panel
|
|
204
|
+
content its own labelled landmark (`role`, `aria-label`, …) through
|
|
205
|
+
props if the preview warrants being announced.
|
|
206
|
+
|
|
207
|
+
## `asChild` composition
|
|
208
|
+
|
|
209
|
+
`MillerColumns.Item` accepts `asChild` to render the cell as a
|
|
210
|
+
consumer-supplied element instead of the default `<div>`. All treeitem
|
|
211
|
+
ARIA attributes, event handlers, and the internal ref are merged onto
|
|
212
|
+
the child (child handler runs first, then the component's). A nested
|
|
213
|
+
`<MillerColumns.Column>` is still declared as a sibling of the cell
|
|
214
|
+
element:
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<MillerColumns.Item<HTMLAnchorElement> asChild value="docs">
|
|
218
|
+
<a href="#docs">Docs</a>
|
|
219
|
+
<MillerColumns.Column>…</MillerColumns.Column>
|
|
220
|
+
</MillerColumns.Item>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
A `ref` prop (React 19 ref-as-prop style) is forwarded to the rendered
|
|
224
|
+
element and composed with the library's internal ref.
|
|
225
|
+
|
|
226
|
+
## Styling hooks
|
|
227
|
+
|
|
228
|
+
Zero styles ship with the component. Style it through `data-*` hooks;
|
|
229
|
+
typically the strip is `display: flex` and each column scrolls
|
|
230
|
+
vertically.
|
|
231
|
+
|
|
232
|
+
Inside the strip, each column is wrapped in a transparent
|
|
233
|
+
(`display: contents`) slot element that fixes left-to-right order — so
|
|
234
|
+
target columns with `[data-miller-columns-column]`, not a direct-child
|
|
235
|
+
selector like `[data-miller-columns-strip] > *`.
|
|
236
|
+
|
|
237
|
+
```css
|
|
238
|
+
[data-miller-columns-strip] {
|
|
239
|
+
display: flex;
|
|
240
|
+
}
|
|
241
|
+
[data-miller-columns-column] {
|
|
242
|
+
overflow-y: auto;
|
|
243
|
+
}
|
|
244
|
+
[data-miller-columns-column][data-depth="0"] {
|
|
245
|
+
/* the root column */
|
|
246
|
+
}
|
|
247
|
+
[role="treeitem"][data-state="selected"] {
|
|
248
|
+
/* the selected item in each column */
|
|
249
|
+
}
|
|
250
|
+
[role="treeitem"][data-has-children] {
|
|
251
|
+
/* branch items */
|
|
252
|
+
}
|
|
253
|
+
[role="treeitem"][data-disabled] {
|
|
254
|
+
opacity: 0.5;
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
| Element | Attributes |
|
|
259
|
+
| --------------- | --------------------------------------------------------------------------- |
|
|
260
|
+
| Strip (`Root`) | `data-miller-columns-strip`, `data-orientation="horizontal"` |
|
|
261
|
+
| `Column` | `data-miller-columns-column`, `data-depth` |
|
|
262
|
+
| `Item` | `data-state="selected" \| "unselected"`, `data-depth`, `data-has-children`, `data-disabled` |
|
|
263
|
+
| `ItemIndicator` | `data-state`, `data-has-children` |
|
|
264
|
+
| `ResizeHandle` | `data-miller-columns-resize-handle`, `data-dragging` (present mid-drag) |
|
|
265
|
+
| `PreviewPanel` | `data-miller-columns-preview` |
|
|
266
|
+
|
|
267
|
+
## Deferred / follow-up work
|
|
268
|
+
|
|
269
|
+
The following were intentionally left out of the first version and are
|
|
270
|
+
good candidates for later, independent cycles:
|
|
271
|
+
|
|
272
|
+
1. **Context-menu image preview.** Once a Context Menu component
|
|
273
|
+
exists, a leaf image `Item` could open a context menu on
|
|
274
|
+
right-click whose first entry ("Preview", with an eye icon) opens
|
|
275
|
+
the `Modal` to show the image larger.
|
|
276
|
+
2. **Keyboard-operable resize.** `MillerColumns.ResizeHandle` is
|
|
277
|
+
pointer-only; an arrow-key resize on the focused handle would round
|
|
278
|
+
out the WAI-ARIA window-splitter pattern.
|