@primitiv-ui/react 0.1.27 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/README.md +4 -0
  2. package/package.json +6 -1
  3. package/src/AccessibleIcon/AccessibleIcon.tsx +32 -9
  4. package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +5 -0
  5. package/src/AccessibleIcon/types.ts +15 -2
  6. package/src/Accordion/Accordion.tsx +25 -4
  7. package/src/Accordion/__tests__/Accordion.asChild.test.tsx +46 -0
  8. package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +15 -0
  9. package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +42 -0
  10. package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +33 -0
  11. package/src/Accordion/hooks/useAccordionRoot.ts +44 -7
  12. package/src/Accordion/hooks/useAccordionTrigger.ts +9 -1
  13. package/src/Accordion/types.ts +72 -21
  14. package/src/Alert/Alert.tsx +36 -7
  15. package/src/Alert/__tests__/Alert.test.tsx +5 -0
  16. package/src/Alert/types.ts +18 -2
  17. package/src/Avatar/Avatar.tsx +99 -15
  18. package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +10 -0
  19. package/src/Avatar/__tests__/Avatar.fallback.test.tsx +30 -0
  20. package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +35 -0
  21. package/src/Avatar/hooks/useAvatarImage.ts +16 -0
  22. package/src/Avatar/types.ts +15 -3
  23. package/src/Breadcrumb/Breadcrumb.tsx +136 -18
  24. package/src/Breadcrumb/README.md +47 -0
  25. package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +11 -0
  26. package/src/Breadcrumb/__tests__/Breadcrumb.ellipsis.test.tsx +110 -0
  27. package/src/Breadcrumb/types.ts +73 -7
  28. package/src/Button/Button.tsx +2 -0
  29. package/src/Button/__tests__/Button.basic-rendering.test.tsx +6 -0
  30. package/src/Button/types.ts +21 -0
  31. package/src/Carousel/Carousel.tsx +263 -15
  32. package/src/Carousel/README.md +749 -72
  33. package/src/Carousel/__tests__/Carousel.autoplay-status.test.tsx +239 -0
  34. package/src/Carousel/__tests__/Carousel.drag-status.test.tsx +292 -0
  35. package/src/Carousel/__tests__/Carousel.dynamic-slides.test.tsx +38 -0
  36. package/src/Carousel/__tests__/Carousel.indicators.test.tsx +101 -0
  37. package/src/Carousel/__tests__/Carousel.infinite.test.tsx +1453 -0
  38. package/src/Carousel/__tests__/Carousel.instant-scroll.test.tsx +124 -0
  39. package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +88 -0
  40. package/src/Carousel/__tests__/Carousel.loop.test.tsx +163 -0
  41. package/src/Carousel/__tests__/Carousel.loopEngine.test.ts +77 -0
  42. package/src/Carousel/__tests__/Carousel.mouse-drag.test.tsx +504 -0
  43. package/src/Carousel/__tests__/Carousel.multi-slide-bounds.test.tsx +91 -0
  44. package/src/Carousel/__tests__/Carousel.orientation.test.tsx +207 -0
  45. package/src/Carousel/__tests__/Carousel.overscroll.test.tsx +532 -0
  46. package/src/Carousel/__tests__/Carousel.page-snap-points.test.tsx +50 -0
  47. package/src/Carousel/__tests__/Carousel.progress-text.test.tsx +76 -0
  48. package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +3 -6
  49. package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +2 -5
  50. package/src/Carousel/__tests__/Carousel.scroll-margin.test.tsx +167 -0
  51. package/src/Carousel/__tests__/Carousel.scroll-progress.test.tsx +518 -0
  52. package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +32 -11
  53. package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +237 -24
  54. package/src/Carousel/__tests__/Carousel.scroll-to-index.test.tsx +115 -0
  55. package/src/Carousel/__tests__/Carousel.slide-snap.test.tsx +172 -0
  56. package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +179 -1
  57. package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +91 -7
  58. package/src/Carousel/__tests__/Carousel.snap-type.test.tsx +35 -0
  59. package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +30 -5
  60. package/src/Carousel/__tests__/Carousel.translations.test.tsx +23 -0
  61. package/src/Carousel/__tests__/Carousel.wheel.test.tsx +160 -0
  62. package/src/Carousel/hooks/index.ts +1 -0
  63. package/src/Carousel/hooks/useCarouselLoop.ts +869 -0
  64. package/src/Carousel/hooks/useCarouselRoot.ts +338 -42
  65. package/src/Carousel/hooks/useCarouselSlide.ts +44 -9
  66. package/src/Carousel/hooks/useCarouselViewport.ts +649 -46
  67. package/src/Carousel/loopEngine.ts +54 -0
  68. package/src/Carousel/types.ts +457 -35
  69. package/src/Checkbox/Checkbox.tsx +9 -1
  70. package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +14 -0
  71. package/src/Checkbox/hooks/useCheckboxInput.ts +4 -0
  72. package/src/Checkbox/types.ts +19 -0
  73. package/src/CheckboxCard/CheckboxCard.tsx +13 -4
  74. package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +7 -0
  75. package/src/CheckboxCard/types.ts +65 -17
  76. package/src/Collapsible/Collapsible.tsx +49 -3
  77. package/src/Collapsible/README.md +40 -0
  78. package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +89 -0
  79. package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +27 -0
  80. package/src/Collapsible/__tests__/Collapsible.collapsedHeight.test.tsx +140 -0
  81. package/src/Collapsible/hooks/useCollapsibleTrigger.ts +3 -1
  82. package/src/Collapsible/types.ts +89 -22
  83. package/src/ContextMenu/ContextMenu.tsx +270 -4
  84. package/src/ContextMenu/types.ts +148 -6
  85. package/src/DirectionProvider/DirectionProvider.tsx +41 -9
  86. package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +5 -0
  87. package/src/DirectionProvider/types.ts +28 -3
  88. package/src/Divider/Divider.tsx +24 -10
  89. package/src/Divider/__tests__/Divider.test.tsx +5 -0
  90. package/src/Divider/types.ts +10 -3
  91. package/src/Drawer/Drawer.tsx +315 -0
  92. package/src/Drawer/README.md +124 -0
  93. package/src/Drawer/__tests__/Drawer.behavior.test.tsx +100 -0
  94. package/src/Drawer/__tests__/Drawer.focus-trap.test.tsx +55 -0
  95. package/src/Drawer/__tests__/Drawer.rendering.test.tsx +111 -0
  96. package/src/Drawer/__tests__/Drawer.side.test.tsx +69 -0
  97. package/src/Drawer/__tests__/dialog-polyfill.ts +39 -0
  98. package/src/Drawer/index.ts +2 -0
  99. package/src/Drawer/types.ts +66 -0
  100. package/src/Dropdown/Dropdown.tsx +117 -0
  101. package/src/Dropdown/types.ts +241 -11
  102. package/src/EmptyState/EmptyState.tsx +74 -4
  103. package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +7 -2
  104. package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +3 -1
  105. package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +10 -0
  106. package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +3 -1
  107. package/src/EmptyState/types.ts +49 -6
  108. package/src/Field/Field.tsx +23 -0
  109. package/src/Field/__tests__/Field.basic-rendering.test.tsx +11 -0
  110. package/src/Field/__tests__/Field.error-handling.test.tsx +23 -0
  111. package/src/Field/__tests__/Field.state-cascade.test.tsx +21 -0
  112. package/src/Field/hooks/__tests__/useFieldProps.test.tsx +104 -9
  113. package/src/Field/types.ts +60 -12
  114. package/src/Fieldset/Fieldset.tsx +54 -18
  115. package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +9 -0
  116. package/src/Fieldset/types.ts +20 -2
  117. package/src/Input/Input.tsx +14 -9
  118. package/src/Input/__tests__/Input.basic-rendering.test.tsx +5 -0
  119. package/src/Input/types.ts +19 -2
  120. package/src/InputGroup/InputGroup.tsx +11 -3
  121. package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +14 -0
  122. package/src/InputGroup/types.ts +26 -5
  123. package/src/MillerColumns/MillerColumns.tsx +86 -19
  124. package/src/MillerColumns/types.ts +83 -27
  125. package/src/Modal/Modal.tsx +31 -4
  126. package/src/Modal/README.md +19 -10
  127. package/src/Modal/__tests__/Modal.focus-trap.test.tsx +162 -0
  128. package/src/Modal/hooks/useModalContent.ts +32 -0
  129. package/src/Modal/types.ts +110 -29
  130. package/src/NavigationMenu/NavigationMenu.tsx +890 -0
  131. package/src/NavigationMenu/NavigationMenuContext.ts +67 -0
  132. package/src/NavigationMenu/README.md +355 -0
  133. package/src/NavigationMenu/__tests__/NavigationMenu.anchor-positioning.test.tsx +132 -0
  134. package/src/NavigationMenu/__tests__/NavigationMenu.asChild.test.tsx +73 -0
  135. package/src/NavigationMenu/__tests__/NavigationMenu.basic-rendering.test.tsx +92 -0
  136. package/src/NavigationMenu/__tests__/NavigationMenu.contract.test.tsx +45 -0
  137. package/src/NavigationMenu/__tests__/NavigationMenu.controlled-state.test.tsx +134 -0
  138. package/src/NavigationMenu/__tests__/NavigationMenu.error-handling.test.tsx +62 -0
  139. package/src/NavigationMenu/__tests__/NavigationMenu.fixtures.tsx +79 -0
  140. package/src/NavigationMenu/__tests__/NavigationMenu.forceMount.test.tsx +52 -0
  141. package/src/NavigationMenu/__tests__/NavigationMenu.hover-interaction.test.tsx +254 -0
  142. package/src/NavigationMenu/__tests__/NavigationMenu.indicator.test.tsx +252 -0
  143. package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-interaction.test.tsx +294 -0
  144. package/src/NavigationMenu/__tests__/NavigationMenu.keyboard-navigation.test.tsx +126 -0
  145. package/src/NavigationMenu/__tests__/NavigationMenu.link.test.tsx +139 -0
  146. package/src/NavigationMenu/__tests__/NavigationMenu.motion.test.tsx +568 -0
  147. package/src/NavigationMenu/__tests__/NavigationMenu.mouse-interaction.test.tsx +76 -0
  148. package/src/NavigationMenu/__tests__/NavigationMenu.uncontrolled-state.test.tsx +30 -0
  149. package/src/NavigationMenu/__tests__/NavigationMenu.viewport.test.tsx +113 -0
  150. package/src/NavigationMenu/constants.ts +5 -0
  151. package/src/NavigationMenu/hooks/index.ts +7 -0
  152. package/src/NavigationMenu/hooks/useNavigationMenuEntry.ts +33 -0
  153. package/src/NavigationMenu/hooks/useNavigationMenuIndicator.ts +70 -0
  154. package/src/NavigationMenu/hooks/useNavigationMenuLink.ts +72 -0
  155. package/src/NavigationMenu/hooks/useNavigationMenuRoot.ts +301 -0
  156. package/src/NavigationMenu/hooks/useNavigationMenuTopLevelEntry.ts +65 -0
  157. package/src/NavigationMenu/hooks/useNavigationMenuTrigger.ts +158 -0
  158. package/src/NavigationMenu/hooks/useNavigationMenuViewport.ts +101 -0
  159. package/src/NavigationMenu/index.ts +3 -0
  160. package/src/NavigationMenu/types.ts +259 -0
  161. package/src/NavigationMenu/utils.ts +89 -0
  162. package/src/Popover/Popover.tsx +427 -0
  163. package/src/Popover/PopoverContext.ts +13 -0
  164. package/src/Popover/README.md +106 -0
  165. package/src/Popover/__tests__/Popover.anchor.test.tsx +38 -0
  166. package/src/Popover/__tests__/Popover.asChild.test.tsx +160 -0
  167. package/src/Popover/__tests__/Popover.basic-rendering.test.tsx +106 -0
  168. package/src/Popover/__tests__/Popover.click-outside.test.tsx +149 -0
  169. package/src/Popover/__tests__/Popover.close.test.tsx +70 -0
  170. package/src/Popover/__tests__/Popover.content.test.tsx +58 -0
  171. package/src/Popover/__tests__/Popover.controlled-state.test.tsx +45 -0
  172. package/src/Popover/__tests__/Popover.error-handling.test.tsx +51 -0
  173. package/src/Popover/__tests__/Popover.focus.test.tsx +43 -0
  174. package/src/Popover/__tests__/Popover.keyboard-interaction.test.tsx +71 -0
  175. package/src/Popover/__tests__/Popover.mouse-interaction.test.tsx +69 -0
  176. package/src/Popover/__tests__/Popover.uncontrolled-state.test.tsx +44 -0
  177. package/src/Popover/hooks/index.ts +3 -0
  178. package/src/Popover/hooks/usePopoverContent.ts +98 -0
  179. package/src/Popover/hooks/usePopoverRoot.ts +84 -0
  180. package/src/Popover/hooks/usePopoverTrigger.ts +33 -0
  181. package/src/Popover/index.ts +2 -0
  182. package/src/Popover/types.ts +197 -0
  183. package/src/Portal/Portal.tsx +50 -10
  184. package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +5 -0
  185. package/src/Portal/types.ts +5 -0
  186. package/src/Progress/Progress.tsx +33 -1
  187. package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +10 -0
  188. package/src/Progress/__tests__/Progress.value.test.tsx +21 -0
  189. package/src/Progress/types.ts +20 -4
  190. package/src/Radio/Radio.tsx +9 -1
  191. package/src/Radio/__tests__/Radio.basic-rendering.test.tsx +14 -0
  192. package/src/Radio/hooks/useRadioRoot.ts +3 -0
  193. package/src/Radio/types.ts +20 -0
  194. package/src/RadioCard/RadioCard.tsx +11 -0
  195. package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +8 -0
  196. package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +27 -0
  197. package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +53 -0
  198. package/src/RadioCard/__tests__/RadioCard.ref-forwarding.test.tsx +54 -0
  199. package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +21 -0
  200. package/src/RadioCard/hooks/useRadioCardRoot.ts +11 -0
  201. package/src/RadioCard/types.ts +95 -22
  202. package/src/RadioGroup/RadioGroup.tsx +11 -0
  203. package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +8 -0
  204. package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +27 -0
  205. package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +29 -0
  206. package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +31 -0
  207. package/src/RadioGroup/hooks/useRadioGroupRoot.ts +11 -0
  208. package/src/RadioGroup/types.ts +73 -9
  209. package/src/SegmentedControl/README.md +154 -0
  210. package/src/SegmentedControl/SegmentedControl.tsx +309 -0
  211. package/src/SegmentedControl/SegmentedControlContext.ts +28 -0
  212. package/src/SegmentedControl/__tests__/SegmentedControl.asChild.test.tsx +50 -0
  213. package/src/SegmentedControl/__tests__/SegmentedControl.basic-rendering.test.tsx +100 -0
  214. package/src/SegmentedControl/__tests__/SegmentedControl.controlled-state.test.tsx +107 -0
  215. package/src/SegmentedControl/__tests__/SegmentedControl.disabled-keydown-guards.test.tsx +62 -0
  216. package/src/SegmentedControl/__tests__/SegmentedControl.disabled.test.tsx +151 -0
  217. package/src/SegmentedControl/__tests__/SegmentedControl.error-handling.test.tsx +17 -0
  218. package/src/SegmentedControl/__tests__/SegmentedControl.keyboard-interaction.test.tsx +117 -0
  219. package/src/SegmentedControl/__tests__/SegmentedControl.orientation.test.tsx +54 -0
  220. package/src/SegmentedControl/__tests__/SegmentedControl.reading-direction.test.tsx +56 -0
  221. package/src/SegmentedControl/__tests__/SegmentedControl.ref-forwarding.test.tsx +45 -0
  222. package/src/SegmentedControl/__tests__/SegmentedControl.roving-tabindex.test.tsx +104 -0
  223. package/src/SegmentedControl/__tests__/SegmentedControl.uncontrolled-state.test.tsx +94 -0
  224. package/src/SegmentedControl/hooks/index.ts +2 -0
  225. package/src/SegmentedControl/hooks/useSegmentedControlContext.ts +1 -0
  226. package/src/SegmentedControl/hooks/useSegmentedControlRoot.ts +86 -0
  227. package/src/SegmentedControl/index.ts +2 -0
  228. package/src/SegmentedControl/types.ts +138 -0
  229. package/src/Select/README.md +122 -157
  230. package/src/Select/Select.tsx +595 -68
  231. package/src/Select/SelectContext.ts +38 -0
  232. package/src/Select/SelectItemIndicatorContext.ts +29 -0
  233. package/src/Select/__tests__/Select.asChild.test.tsx +2 -2
  234. package/src/Select/__tests__/Select.basic-rendering.test.tsx +22 -3
  235. package/src/Select/__tests__/Select.controlled-state.test.tsx +6 -6
  236. package/src/Select/__tests__/Select.data-attributes.test.tsx +4 -4
  237. package/src/Select/__tests__/Select.field-integration.test.tsx +11 -11
  238. package/src/Select/__tests__/Select.form.test.tsx +38 -0
  239. package/src/Select/__tests__/Select.group.test.tsx +6 -6
  240. package/src/Select/__tests__/Select.keyboard-edge.test.tsx +95 -0
  241. package/src/Select/__tests__/Select.keyboard.test.tsx +124 -0
  242. package/src/Select/__tests__/Select.mutation-hardening.test.tsx +317 -0
  243. package/src/Select/__tests__/Select.native-item.test.tsx +30 -0
  244. package/src/Select/__tests__/Select.open-close.test.tsx +57 -0
  245. package/src/Select/__tests__/Select.placeholder.test.tsx +4 -4
  246. package/src/Select/__tests__/Select.rich-asChild.test.tsx +60 -0
  247. package/src/Select/__tests__/Select.rich-mode.test.tsx +16 -0
  248. package/src/Select/__tests__/Select.selection.test.tsx +104 -0
  249. package/src/Select/__tests__/Select.separator.test.tsx +71 -0
  250. package/src/Select/__tests__/Select.strict-context.test.tsx +36 -0
  251. package/src/Select/__tests__/Select.typeahead.test.tsx +156 -0
  252. package/src/Select/__tests__/Select.value-mirror.test.tsx +240 -0
  253. package/src/Select/constants.ts +8 -0
  254. package/src/Select/hooks/index.ts +2 -0
  255. package/src/Select/hooks/useSelectContent.ts +149 -0
  256. package/src/Select/hooks/useSelectRoot.ts +149 -0
  257. package/src/Select/types.ts +199 -31
  258. package/src/SkipNav/SkipNav.tsx +69 -29
  259. package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +6 -0
  260. package/src/SkipNav/types.ts +17 -9
  261. package/src/Slider/Slider.tsx +33 -1
  262. package/src/Slider/SliderContext.ts +28 -1
  263. package/src/Slider/types.ts +157 -9
  264. package/src/Slot/Slot.tsx +94 -19
  265. package/src/Slot/__tests__/Slot.test.tsx +110 -0
  266. package/src/Slot/composeEventHandlers.ts +26 -2
  267. package/src/Slot/types.ts +16 -0
  268. package/src/Status/Status.tsx +36 -15
  269. package/src/Status/__tests__/Status.test.tsx +5 -0
  270. package/src/Status/types.ts +12 -1
  271. package/src/Switch/Switch.tsx +15 -2
  272. package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +7 -0
  273. package/src/Switch/hooks/useSwitchRoot.ts +3 -0
  274. package/src/Switch/types.ts +49 -22
  275. package/src/Table/Table.tsx +44 -10
  276. package/src/Table/types.ts +66 -17
  277. package/src/Tabs/Tabs.tsx +8 -0
  278. package/src/Tabs/types.ts +29 -4
  279. package/src/Textarea/Textarea.tsx +9 -1
  280. package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +5 -0
  281. package/src/Textarea/types.ts +20 -3
  282. package/src/Toggle/Toggle.tsx +51 -13
  283. package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +5 -0
  284. package/src/Toggle/types.ts +54 -4
  285. package/src/ToggleGroup/ToggleGroup.tsx +14 -1
  286. package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +20 -0
  287. package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +9 -0
  288. package/src/ToggleGroup/__tests__/ToggleGroup.dynamic-items.test.tsx +70 -0
  289. package/src/ToggleGroup/__tests__/ToggleGroup.error-handling.test.tsx +23 -0
  290. package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +23 -1
  291. package/src/ToggleGroup/__tests__/ToggleGroup.ref-forwarding.test.tsx +58 -0
  292. package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +21 -1
  293. package/src/ToggleGroup/types.ts +55 -2
  294. package/src/Tooltip/Tooltip.tsx +98 -20
  295. package/src/Tooltip/hooks/__tests__/useTooltipProvider.test.tsx +40 -0
  296. package/src/Tooltip/hooks/useTooltipProvider.ts +13 -1
  297. package/src/Tooltip/types.ts +89 -8
  298. package/src/Tree/Tree.tsx +187 -33
  299. package/src/Tree/types.ts +54 -12
  300. package/src/VisuallyHidden/VisuallyHidden.tsx +33 -8
  301. package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +15 -1
  302. package/src/VisuallyHidden/types.ts +11 -1
  303. package/src/index.ts +4 -0
  304. package/src/test/intersectionObserverPolyfill.ts +7 -0
  305. package/src/test/resizeObserverPolyfill.ts +80 -0
  306. package/src/utils/focusable.ts +15 -0
  307. package/src/utils/index.ts +1 -0
@@ -8,6 +8,8 @@ import { Direction } from "../DirectionProvider/index.ts";
8
8
  * plus the reading-direction control.
9
9
  */
10
10
  export type ContextMenuRootBaseProps = {
11
+ /** The {@link ContextMenu.Trigger} and {@link ContextMenu.Content} pair
12
+ * (Root renders no wrapping element of its own). */
11
13
  children?: ReactNode;
12
14
  /**
13
15
  * Reading direction for the menu. Affects which arrow key opens / closes
@@ -24,8 +26,14 @@ export type ContextMenuRootBaseProps = {
24
26
  * optional and `open` is forbidden.
25
27
  */
26
28
  export type ContextMenuRootUncontrolledProps = ContextMenuRootBaseProps & {
29
+ /** Whether the menu is open on first render.
30
+ * @default false */
27
31
  defaultOpen?: boolean;
32
+ /** Forbidden in uncontrolled mode — use `defaultOpen` instead. */
28
33
  open?: never;
34
+ /** Observes open-state transitions. Fires only on user-driven changes
35
+ * (right-click, Escape, selection, outside click), never on external
36
+ * flips. Optional in uncontrolled mode. */
29
37
  onOpenChange?: (open: boolean) => void;
30
38
  };
31
39
 
@@ -35,8 +43,13 @@ export type ContextMenuRootUncontrolledProps = ContextMenuRootBaseProps & {
35
43
  * forbidden.
36
44
  */
37
45
  export type ContextMenuRootControlledProps = ContextMenuRootBaseProps & {
46
+ /** Forbidden in controlled mode — use `open` instead. */
38
47
  defaultOpen?: never;
48
+ /** Whether the menu is open. Must be kept in sync by the parent via
49
+ * `onOpenChange`. */
39
50
  open: boolean;
51
+ /** Called with the requested open state on each user-driven transition.
52
+ * Required in controlled mode. */
40
53
  onOpenChange: (open: boolean) => void;
41
54
  };
42
55
 
@@ -56,9 +69,18 @@ export type ContextMenuRootProps =
56
69
  * onto a custom element and `disabled` to suppress the gesture.
57
70
  */
58
71
  export type ContextMenuTriggerProps = ComponentProps<"span"> & {
72
+ /** The content the right-click gesture is bound to. */
59
73
  children?: ReactNode;
74
+ /** Forwarded to the underlying `HTMLSpanElement` (or the `asChild` host). */
60
75
  ref?: Ref<HTMLSpanElement>;
76
+ /** Render the child element instead of the default `<span>`, merging the
77
+ * trigger's `contextmenu` handler and data-attributes onto it via the
78
+ * {@link Slot} pattern. The child must accept a `ref`.
79
+ * @default false */
61
80
  asChild?: boolean;
81
+ /** When `true`, the trigger ignores the `contextmenu` gesture entirely,
82
+ * letting the native browser menu through. Exposed as `data-disabled=""`.
83
+ * @default false */
62
84
  disabled?: boolean;
63
85
  };
64
86
 
@@ -69,10 +91,17 @@ export type ContextMenuTriggerProps = ComponentProps<"span"> & {
69
91
  */
70
92
  export type ContextMenuContentProps = Omit<
71
93
  ComponentProps<"menu">,
72
- "role" | "popover" | "id"
94
+ "role" | "popover" | "id" | "ref"
73
95
  > & {
96
+ /** The menu items — {@link ContextMenuItem}, {@link ContextMenuGroup},
97
+ * {@link ContextMenuSub}, and friends. */
74
98
  children?: ReactNode;
99
+ /** Forwarded to the underlying `HTMLMenuElement` (or the `asChild` host). */
75
100
  ref?: Ref<HTMLMenuElement>;
101
+ /** Render the child element instead of the default `<menu>`, merging the
102
+ * managed `role` / `popover` / positioning props and the keyboard handler
103
+ * onto it via the {@link Slot} pattern.
104
+ * @default false */
76
105
  asChild?: boolean;
77
106
  };
78
107
 
@@ -85,9 +114,19 @@ export type ContextMenuItemProps = Omit<
85
114
  ComponentProps<"li">,
86
115
  "role" | "tabIndex" | "onSelect"
87
116
  > & {
117
+ /** The item's visible content. */
88
118
  children?: ReactNode;
119
+ /** Forwarded to the underlying `HTMLLIElement` (or the `asChild` host). */
89
120
  ref?: Ref<HTMLLIElement>;
121
+ /** Render the child element instead of the default `<li role="menuitem">`,
122
+ * merging the item's props onto it via the {@link Slot} pattern (e.g. to
123
+ * render a menu item as an `<a>`).
124
+ * @default false */
90
125
  asChild?: boolean;
126
+ /** Removes the item from activation and marks it `aria-disabled`; it
127
+ * still renders for assistive technology but no-ops on click / Enter, and
128
+ * arrow navigation and typeahead skip it.
129
+ * @default false */
91
130
  disabled?: boolean;
92
131
  /**
93
132
  * Fires when the item is activated (click, Enter, or Space). Called
@@ -103,8 +142,14 @@ export type ContextMenuItemProps = Omit<
103
142
  * `asChild` merges the props onto a custom element.
104
143
  */
105
144
  export type ContextMenuSeparatorProps = Omit<ComponentProps<"li">, "role"> & {
145
+ /** Usually empty — a separator renders no content by default. */
106
146
  children?: ReactNode;
147
+ /** Forwarded to the underlying `HTMLLIElement` (or the `asChild` host). */
107
148
  ref?: Ref<HTMLLIElement>;
149
+ /** Render the child element instead of the default
150
+ * `<li role="separator">`, merging the managed `role` onto it via the
151
+ * {@link Slot} pattern.
152
+ * @default false */
108
153
  asChild?: boolean;
109
154
  };
110
155
 
@@ -114,8 +159,14 @@ export type ContextMenuSeparatorProps = Omit<ComponentProps<"li">, "role"> & {
114
159
  * `role` is omitted; `asChild` merges the props onto a custom element.
115
160
  */
116
161
  export type ContextMenuGroupProps = Omit<ComponentProps<"li">, "role"> & {
162
+ /** The grouped items, optionally led by a {@link ContextMenu.Label}. */
117
163
  children?: ReactNode;
164
+ /** Forwarded to the underlying `HTMLLIElement` (or the `asChild` host). */
118
165
  ref?: Ref<HTMLLIElement>;
166
+ /** Render the child element instead of the default `<li role="group">`
167
+ * (which otherwise wraps an inner `<ul role="none">`), merging the group's
168
+ * props onto it via the {@link Slot} pattern.
169
+ * @default false */
119
170
  asChild?: boolean;
120
171
  };
121
172
 
@@ -125,8 +176,13 @@ export type ContextMenuGroupProps = Omit<ComponentProps<"li">, "role"> & {
125
176
  * onto a custom element.
126
177
  */
127
178
  export type ContextMenuLabelProps = ComponentProps<"li"> & {
179
+ /** The label text. */
128
180
  children?: ReactNode;
181
+ /** Forwarded to the underlying `HTMLLIElement` (or the `asChild` host). */
129
182
  ref?: Ref<HTMLLIElement>;
183
+ /** Render the child element instead of the default `<li>`, merging the
184
+ * (auto-wired) `id` onto it via the {@link Slot} pattern.
185
+ * @default false */
130
186
  asChild?: boolean;
131
187
  };
132
188
 
@@ -140,9 +196,19 @@ export type ContextMenuCheckboxItemBaseProps = Omit<
140
196
  ComponentProps<"li">,
141
197
  "role" | "tabIndex" | "aria-checked" | "defaultChecked" | "onSelect"
142
198
  > & {
199
+ /** The item's visible content, typically including a
200
+ * {@link ContextMenuItemIndicator}. */
143
201
  children?: ReactNode;
202
+ /** Forwarded to the underlying `HTMLLIElement` (or the `asChild` host). */
144
203
  ref?: Ref<HTMLLIElement>;
204
+ /** Render the child element instead of the default
205
+ * `<li role="menuitemcheckbox">`, merging the item's props onto it via
206
+ * the {@link Slot} pattern.
207
+ * @default false */
145
208
  asChild?: boolean;
209
+ /** Removes the item from activation and marks it `aria-disabled`; it
210
+ * still renders for assistive technology but no-ops on click / Enter.
211
+ * @default false */
146
212
  disabled?: boolean;
147
213
  /**
148
214
  * Fires when activation completes and the auto-close fires. Call
@@ -159,8 +225,14 @@ export type ContextMenuCheckboxItemBaseProps = Omit<
159
225
  */
160
226
  export type ContextMenuCheckboxItemUncontrolledProps =
161
227
  ContextMenuCheckboxItemBaseProps & {
228
+ /** Checked state on first render. Accepts the tri-state
229
+ * {@link CheckedState} (`true` / `false` / `"indeterminate"`).
230
+ * @default false */
162
231
  defaultChecked?: CheckedState;
232
+ /** Forbidden in uncontrolled mode — use `defaultChecked` instead. */
163
233
  checked?: never;
234
+ /** Called with the resolved boolean checked state on each toggle.
235
+ * Optional in uncontrolled mode. */
164
236
  onCheckedChange?: (checked: boolean) => void;
165
237
  };
166
238
 
@@ -171,8 +243,14 @@ export type ContextMenuCheckboxItemUncontrolledProps =
171
243
  */
172
244
  export type ContextMenuCheckboxItemControlledProps =
173
245
  ContextMenuCheckboxItemBaseProps & {
246
+ /** Forbidden in controlled mode — use `checked` instead. */
174
247
  defaultChecked?: never;
248
+ /** The current checked state. Accepts the tri-state {@link CheckedState}
249
+ * (`true` / `false` / `"indeterminate"`, the last rendered as
250
+ * `aria-checked="mixed"`). Must be kept in sync via `onCheckedChange`. */
175
251
  checked: CheckedState;
252
+ /** Called with the resolved boolean checked state on each toggle.
253
+ * Required in controlled mode. */
176
254
  onCheckedChange: (checked: boolean) => void;
177
255
  };
178
256
 
@@ -193,12 +271,19 @@ export type ContextMenuCheckboxItemProps =
193
271
  * onto a custom element.
194
272
  */
195
273
  export type ContextMenuItemIndicatorProps = ComponentProps<"span"> & {
274
+ /** The mark to show — usually a checkmark or radio-dot icon. */
196
275
  children?: ReactNode;
276
+ /** Forwarded to the underlying `HTMLSpanElement` (or the `asChild` host). */
197
277
  ref?: Ref<HTMLSpanElement>;
278
+ /** Render the child element instead of the default `<span>`, merging the
279
+ * `data-state` attribute onto it via the {@link Slot} pattern (e.g. to
280
+ * compose onto an SVG icon).
281
+ * @default false */
198
282
  asChild?: boolean;
199
283
  /**
200
284
  * Render the indicator even when its parent item is unchecked. The
201
285
  * `data-state` attribute still reflects the live state.
286
+ * @default false
202
287
  */
203
288
  forceMount?: boolean;
204
289
  };
@@ -206,14 +291,22 @@ export type ContextMenuItemIndicatorProps = ComponentProps<"span"> & {
206
291
  /**
207
292
  * Shared base for both {@link ContextMenuRadioGroupProps} variants — the
208
293
  * `<li>` host props plus `children`, `ref`, and `asChild`. The managed
209
- * `role` host attribute is omitted.
294
+ * `role` host attribute is omitted, along with the native `<li>` `value`
295
+ * and `defaultValue` attributes: the variants below re-declare both as the
296
+ * radio-group's selection state (`string`), narrowing the native
297
+ * `string | number | readonly string[]`. They must be `Omit`-ted here or
298
+ * the docs-data extractor silently drops the narrowed declaration (the
299
+ * §1.16 narrow-without-omit rule).
210
300
  */
211
301
  export type ContextMenuRadioGroupBaseProps = Omit<
212
302
  ComponentProps<"li">,
213
- "role"
303
+ "role" | "value" | "defaultValue"
214
304
  > & {
215
305
  children?: ReactNode;
216
306
  ref?: Ref<HTMLLIElement>;
307
+ /** Render the child element instead of the default `<li role="group">`,
308
+ * merging the group's props onto it via the {@link Slot} pattern.
309
+ * @default false */
217
310
  asChild?: boolean;
218
311
  };
219
312
 
@@ -225,8 +318,12 @@ export type ContextMenuRadioGroupBaseProps = Omit<
225
318
  */
226
319
  export type ContextMenuRadioGroupUncontrolledProps =
227
320
  ContextMenuRadioGroupBaseProps & {
321
+ /** Value of the {@link ContextMenuRadioItem} selected on first render. */
228
322
  defaultValue?: string;
323
+ /** Forbidden in uncontrolled mode — use `defaultValue` instead. */
229
324
  value?: never;
325
+ /** Called with the newly-selected item's `value` on each change.
326
+ * Optional in uncontrolled mode. */
230
327
  onValueChange?: (value: string) => void;
231
328
  };
232
329
 
@@ -237,8 +334,13 @@ export type ContextMenuRadioGroupUncontrolledProps =
237
334
  */
238
335
  export type ContextMenuRadioGroupControlledProps =
239
336
  ContextMenuRadioGroupBaseProps & {
337
+ /** Forbidden in controlled mode — use `value` instead. */
240
338
  defaultValue?: never;
339
+ /** The `value` of the currently-selected {@link ContextMenuRadioItem}.
340
+ * Must be kept in sync by the parent via `onValueChange`. */
241
341
  value: string;
342
+ /** Called with the newly-selected item's `value` on each change.
343
+ * Required in controlled mode. */
242
344
  onValueChange: (value: string) => void;
243
345
  };
244
346
 
@@ -256,17 +358,32 @@ export type ContextMenuRadioGroupProps =
256
358
  * Props for {@link ContextMenu.RadioItem} — a mutually-exclusive choice
257
359
  * within a {@link ContextMenu.RadioGroup}, identified by its required
258
360
  * `value`. The managed `role`, `tabIndex`, `aria-checked`, and `onSelect`
259
- * host attributes are omitted.
361
+ * host attributes are omitted, as is the native `<li>` `value` attribute:
362
+ * the required `value: string` below narrows it (from the native
363
+ * `string | number | readonly string[]`), so it must be `Omit`-ted or the
364
+ * docs-data extractor silently drops it (the §1.16 narrow-without-omit
365
+ * rule).
260
366
  */
261
367
  export type ContextMenuRadioItemProps = Omit<
262
368
  ComponentProps<"li">,
263
- "role" | "tabIndex" | "aria-checked" | "onSelect"
369
+ "role" | "tabIndex" | "aria-checked" | "onSelect" | "value"
264
370
  > & {
265
371
  children?: ReactNode;
266
372
  ref?: Ref<HTMLLIElement>;
373
+ /** Render the child element instead of the default `<li role="menuitemradio">`,
374
+ * merging the item's props onto it via the {@link Slot} pattern.
375
+ * @default false */
267
376
  asChild?: boolean;
377
+ /** Removes the item from activation and marks it `aria-disabled`; it
378
+ * still renders for assistive technology but no-ops on click / Enter.
379
+ * @default false */
268
380
  disabled?: boolean;
381
+ /** Identifies this choice within the enclosing
382
+ * {@link ContextMenuRadioGroup}. When it matches the group's active
383
+ * value, the item is checked. */
269
384
  value: string;
385
+ /** Fires when the item is activated (click, Enter, or Space). Call
386
+ * `event.preventDefault()` to skip the auto-close after selection. */
270
387
  onSelect?: (event: Event) => void;
271
388
  };
272
389
 
@@ -275,6 +392,8 @@ export type ContextMenuRadioItemProps = Omit<
275
392
  * `children` of the submenu.
276
393
  */
277
394
  export type ContextMenuSubBaseProps = {
395
+ /** The {@link ContextMenu.SubTrigger} and {@link ContextMenu.SubContent}
396
+ * pair (Sub renders no wrapping element of its own). */
278
397
  children?: ReactNode;
279
398
  };
280
399
 
@@ -284,8 +403,12 @@ export type ContextMenuSubBaseProps = {
284
403
  * is optional and the controlled `open` prop is forbidden.
285
404
  */
286
405
  export type ContextMenuSubUncontrolledProps = ContextMenuSubBaseProps & {
406
+ /** Whether the submenu is open on first render.
407
+ * @default false */
287
408
  defaultOpen?: boolean;
409
+ /** Forbidden in uncontrolled mode — use `defaultOpen` instead. */
288
410
  open?: never;
411
+ /** Observes submenu open-state transitions. Optional in uncontrolled mode. */
289
412
  onOpenChange?: (open: boolean) => void;
290
413
  };
291
414
 
@@ -295,8 +418,12 @@ export type ContextMenuSubUncontrolledProps = ContextMenuSubBaseProps & {
295
418
  * `defaultOpen` is forbidden.
296
419
  */
297
420
  export type ContextMenuSubControlledProps = ContextMenuSubBaseProps & {
421
+ /** Forbidden in controlled mode — use `open` instead. */
298
422
  defaultOpen?: never;
423
+ /** Whether the submenu is open. Must be kept in sync via `onOpenChange`. */
299
424
  open: boolean;
425
+ /** Called with the requested open state on each transition.
426
+ * Required in controlled mode. */
300
427
  onOpenChange: (open: boolean) => void;
301
428
  };
302
429
 
@@ -320,9 +447,18 @@ export type ContextMenuSubTriggerProps = Omit<
320
447
  ComponentProps<"li">,
321
448
  "role" | "tabIndex" | "aria-haspopup" | "aria-expanded" | "aria-controls"
322
449
  > & {
450
+ /** The trigger's visible content (typically the submenu's name). */
323
451
  children?: ReactNode;
452
+ /** Forwarded to the underlying `HTMLLIElement` (or the `asChild` host). */
324
453
  ref?: Ref<HTMLLIElement>;
454
+ /** Render the child element instead of the default
455
+ * `<li role="menuitem">`, merging the submenu-wiring ARIA and handlers
456
+ * onto it via the {@link Slot} pattern.
457
+ * @default false */
325
458
  asChild?: boolean;
459
+ /** When `true`, the trigger refuses to open the submenu on both click
460
+ * and the inline-forward arrow key, and is marked `aria-disabled`.
461
+ * @default false */
326
462
  disabled?: boolean;
327
463
  };
328
464
 
@@ -334,9 +470,15 @@ export type ContextMenuSubTriggerProps = Omit<
334
470
  */
335
471
  export type ContextMenuSubContentProps = Omit<
336
472
  ComponentProps<"menu">,
337
- "role" | "popover" | "id"
473
+ "role" | "popover" | "id" | "ref"
338
474
  > & {
475
+ /** The submenu's items. */
339
476
  children?: ReactNode;
477
+ /** Forwarded to the underlying `HTMLMenuElement` (or the `asChild` host). */
340
478
  ref?: Ref<HTMLMenuElement>;
479
+ /** Render the child element instead of the default `<menu>`, merging the
480
+ * managed `role` / `popover` / `id` and the keyboard handler onto it via
481
+ * the {@link Slot} pattern.
482
+ * @default false */
341
483
  asChild?: boolean;
342
484
  };
@@ -5,20 +5,52 @@ import { DirectionProviderProps } from "./types";
5
5
 
6
6
  /**
7
7
  * Broadcasts a reading direction to every descendant so direction-aware
8
- * components can inherit it instead of each being passed an explicit
9
- * `dir` prop.
8
+ * components can inherit it through context instead of each being passed
9
+ * an explicit `dir` prop.
10
10
  *
11
- * Renders **no DOM** — only a context provider. Direction is never
12
- * inferred from the DOM; the consumer still owns setting `dir` on their
13
- * `<html>` (or another element) for CSS. A descendant component's own
14
- * `dir` prop always wins over the inherited value; components fall back
15
- * to {@link useDirection} only when their `dir` prop is omitted.
11
+ * Renders **no DOM host element** — only a React context provider.
12
+ * Direction is never inferred from the DOM; the consumer still owns
13
+ * setting `dir` on their `<html>` (or another ancestor element) for
14
+ * CSS purposes. This component only propagates the {@link Direction}
15
+ * value to `@primitiv-ui/react` components.
16
+ *
17
+ * **Override precedence.** A descendant component's own `dir` prop
18
+ * always wins over the inherited value; components fall back to
19
+ * {@link useDirection} only when their `dir` prop is omitted:
20
+ *
21
+ * ```tsx
22
+ * <DirectionProvider dir="rtl">
23
+ * {/* inherits rtl *\/}
24
+ * <Tabs.Root defaultValue="a">…</Tabs.Root>
25
+ * {/* explicit ltr overrides the provider *\/}
26
+ * <Tabs.Root dir="ltr" defaultValue="a">…</Tabs.Root>
27
+ * </DirectionProvider>
28
+ * ```
29
+ *
30
+ * **Nesting.** Providers nest — an inner `DirectionProvider` overrides an
31
+ * outer one for its subtree.
32
+ *
33
+ * **`useDirection` hook.** Direction-aware sub-components read the
34
+ * inherited value via {@link useDirection}, which falls back to `"ltr"`
35
+ * when no provider is present.
16
36
  *
17
37
  * @example Declare direction once near the app root
18
38
  * ```tsx
39
+ * import { DirectionProvider } from "@primitiv-ui/react";
40
+ *
41
+ * <DirectionProvider dir="rtl">
42
+ * <Tabs.Root defaultValue="overview">…</Tabs.Root>
43
+ * <Slider.Root defaultValue={[50]} aria-label="Volume" />
44
+ * </DirectionProvider>
45
+ * ```
46
+ *
47
+ * @example Nested providers — inner overrides outer for its subtree
48
+ * ```tsx
19
49
  * <DirectionProvider dir="rtl">
20
- * <Tabs.Root>…</Tabs.Root>
21
- * <Slider.Root>…</Slider.Root>
50
+ * <Tabs.Root defaultValue="a">…</Tabs.Root>
51
+ * <DirectionProvider dir="ltr">
52
+ * <Tabs.Root defaultValue="b">…</Tabs.Root>
53
+ * </DirectionProvider>
22
54
  * </DirectionProvider>
23
55
  * ```
24
56
  */
@@ -26,4 +26,9 @@ describe("DirectionProvider", () => {
26
26
  // Assert
27
27
  expect(screen.getByTestId("probe")).toHaveTextContent("rtl");
28
28
  });
29
+
30
+ it('sets displayName to "DirectionProvider" for React DevTools and stack traces', () => {
31
+ // Assert — an empty displayName would render it as an anonymous component.
32
+ expect(DirectionProvider.displayName).toBe("DirectionProvider");
33
+ });
29
34
  });
@@ -1,11 +1,36 @@
1
1
  import { ReactNode } from "react";
2
2
 
3
- /** A reading direction — left-to-right or right-to-left. */
3
+ /**
4
+ * A reading direction value — left-to-right or right-to-left.
5
+ *
6
+ * Used as the {@link DirectionProviderProps.dir | `dir`} prop on
7
+ * {@link DirectionProvider} and as the `dir` prop accepted by every
8
+ * direction-aware component (e.g. `Tabs.Root`, `Slider.Root`).
9
+ * {@link useDirection} returns this type.
10
+ */
4
11
  export type Direction = "ltr" | "rtl";
5
12
 
6
- /** Props for {@link DirectionProvider} — the `dir` broadcast to descendants. */
13
+ /**
14
+ * Props for {@link DirectionProvider} — the reading direction broadcast to
15
+ * all descendant components.
16
+ *
17
+ * `DirectionProvider` accepts no native HTML element props: it renders no
18
+ * DOM host element and is a pure React context provider.
19
+ */
7
20
  export type DirectionProviderProps = {
8
- /** The reading direction broadcast to every descendant. */
21
+ /**
22
+ * The reading direction to broadcast to every descendant.
23
+ * See {@link Direction}.
24
+ *
25
+ * Direction-aware components (e.g. `Tabs.Root`, `Slider.Root`) read
26
+ * this value via {@link useDirection} when their own `dir` prop is
27
+ * omitted. An explicit `dir` prop on a descendant always takes
28
+ * precedence over the inherited value.
29
+ */
9
30
  dir: Direction;
31
+ /**
32
+ * The subtree whose direction-aware components will inherit `dir`.
33
+ * Any React content is accepted; there is no DOM wrapper.
34
+ */
10
35
  children?: ReactNode;
11
36
  };
@@ -3,29 +3,38 @@ import type { ReactElement } from "react";
3
3
  import { DividerProps } from "./types";
4
4
 
5
5
  /**
6
- * A visual and semantic separator between content sections.
6
+ * A headless `<span role="separator">` that marks a visual and semantic
7
+ * boundary between content sections.
7
8
  *
8
- * Renders a `<span role="separator">` with the correct `aria-orientation`
9
- * attribute. Screen readers announce it as a separator element, which is
10
- * appropriate when the divider conveys structural meaning (e.g. separating
11
- * navigation groups or distinct content sections).
9
+ * Renders a native `<span>` with `role="separator"` and the matching
10
+ * `aria-orientation` attribute. Screen readers announce it as a separator,
11
+ * which is appropriate when the divider conveys genuine structural meaning
12
+ * (e.g. separating navigation groups or distinct content areas).
12
13
  *
13
14
  * **Decorative use.** When the divider is purely visual and carries no
14
15
  * semantic meaning, pass `aria-hidden="true"` to remove it from the
15
- * accessibility tree:
16
+ * accessibility tree entirely:
16
17
  *
17
18
  * ```tsx
18
19
  * <Divider aria-hidden="true" />
19
20
  * ```
20
21
  *
21
- * **Styling hooks.** No styles ship with the component. Use the
22
- * `aria-orientation` attribute selector to apply orientation-specific styles:
22
+ * **Styling hooks.** No styles ship with the component. Because Divider
23
+ * emits no `data-*` attributes, target the rendered element via the
24
+ * `aria-orientation` attribute selector or a `className`:
23
25
  *
24
26
  * ```css
25
- * [role="separator"][aria-orientation="horizontal"] { height: 1px; width: 100%; }
26
- * [role="separator"][aria-orientation="vertical"] { width: 1px; height: 100%; }
27
+ * [role="separator"][aria-orientation="horizontal"] { height: 1px; width: 100%; background: currentColor; }
28
+ * [role="separator"][aria-orientation="vertical"] { width: 1px; height: 100%; background: currentColor; }
27
29
  * ```
28
30
  *
31
+ * ```tsx
32
+ * <Divider className="my-divider" />
33
+ * <Divider orientation="vertical" className="my-divider--vertical" />
34
+ * ```
35
+ *
36
+ * @extends HTMLSpanElement
37
+ *
29
38
  * @example Horizontal (default)
30
39
  * ```tsx
31
40
  * <Divider />
@@ -40,6 +49,11 @@ import { DividerProps } from "./types";
40
49
  * ```tsx
41
50
  * <Divider aria-hidden="true" />
42
51
  * ```
52
+ *
53
+ * @example With a className
54
+ * ```tsx
55
+ * <Divider className="my-section-rule" />
56
+ * ```
43
57
  */
44
58
  export function Divider({
45
59
  orientation = "horizontal",
@@ -38,4 +38,9 @@ describe("Divider component", () => {
38
38
  const divider = screen.getByRole("separator");
39
39
  expect(divider).toHaveAttribute("class", "");
40
40
  });
41
+
42
+ it('sets displayName to "Divider" for React DevTools and stack traces', () => {
43
+ // Assert — an empty displayName would render it as an anonymous component.
44
+ expect(Divider.displayName).toBe("Divider");
45
+ });
41
46
  });
@@ -1,10 +1,17 @@
1
1
  import { ComponentProps } from "react";
2
2
 
3
3
  /**
4
- * Props for the {@link Divider} component. Extends the native `<span>` props
5
- * with an optional `orientation`.
4
+ * Props for {@link Divider} — all native `<span>` attributes plus an
5
+ * `orientation` that controls both the visual axis and the `aria-orientation`
6
+ * value announced to assistive technology.
6
7
  */
7
8
  export type DividerProps = ComponentProps<"span"> & {
8
- /** Axis the separator runs along. Defaults to `"horizontal"`. */
9
+ /**
10
+ * Axis the separator runs along. Sets `aria-orientation` on the rendered
11
+ * `<span role="separator">` so screen readers announce the correct
12
+ * orientation. Use `"horizontal"` for row-dividing rules and
13
+ * `"vertical"` for column-dividing rules.
14
+ * @default "horizontal"
15
+ */
9
16
  orientation?: "horizontal" | "vertical";
10
17
  };