@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.
Files changed (585) hide show
  1. package/README.md +79 -0
  2. package/package.json +59 -0
  3. package/src/AccessibleIcon/AccessibleIcon.tsx +40 -0
  4. package/src/AccessibleIcon/README.md +42 -0
  5. package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +47 -0
  6. package/src/AccessibleIcon/index.ts +2 -0
  7. package/src/AccessibleIcon/types.ts +8 -0
  8. package/src/Accordion/Accordion.tsx +412 -0
  9. package/src/Accordion/AccordionContext.ts +12 -0
  10. package/src/Accordion/README.md +202 -0
  11. package/src/Accordion/__tests__/Accordion.asChild.test.tsx +237 -0
  12. package/src/Accordion/__tests__/Accordion.basic-rendering.test.tsx +333 -0
  13. package/src/Accordion/__tests__/Accordion.controlled-state.test.tsx +175 -0
  14. package/src/Accordion/__tests__/Accordion.data-attributes.test.tsx +272 -0
  15. package/src/Accordion/__tests__/Accordion.disabled-items.test.tsx +311 -0
  16. package/src/Accordion/__tests__/Accordion.error-handling.test.tsx +119 -0
  17. package/src/Accordion/__tests__/Accordion.forceMount.test.tsx +119 -0
  18. package/src/Accordion/__tests__/Accordion.keyboard-interaction.test.tsx +736 -0
  19. package/src/Accordion/__tests__/Accordion.mouse-interaction.test.tsx +212 -0
  20. package/src/Accordion/__tests__/Accordion.multiple-mode.test.tsx +90 -0
  21. package/src/Accordion/__tests__/Accordion.reading-direction.test.tsx +139 -0
  22. package/src/Accordion/__tests__/Accordion.uncontrolled-state.test.tsx +154 -0
  23. package/src/Accordion/hooks/index.ts +6 -0
  24. package/src/Accordion/hooks/useAccordionContext.ts +1 -0
  25. package/src/Accordion/hooks/useAccordionHeaderContext.ts +10 -0
  26. package/src/Accordion/hooks/useAccordionItem.ts +22 -0
  27. package/src/Accordion/hooks/useAccordionItemContext.ts +1 -0
  28. package/src/Accordion/hooks/useAccordionRoot.ts +151 -0
  29. package/src/Accordion/hooks/useAccordionTrigger.ts +90 -0
  30. package/src/Accordion/index.ts +1 -0
  31. package/src/Accordion/types.ts +81 -0
  32. package/src/Alert/Alert.tsx +43 -0
  33. package/src/Alert/README.md +54 -0
  34. package/src/Alert/__tests__/Alert.test.tsx +28 -0
  35. package/src/Alert/index.ts +2 -0
  36. package/src/Alert/types.ts +5 -0
  37. package/src/Avatar/Avatar.tsx +149 -0
  38. package/src/Avatar/AvatarContext.ts +20 -0
  39. package/src/Avatar/README.md +116 -0
  40. package/src/Avatar/__tests__/Avatar.asChild.test.tsx +53 -0
  41. package/src/Avatar/__tests__/Avatar.basic-rendering.test.tsx +14 -0
  42. package/src/Avatar/__tests__/Avatar.error-handling.test.tsx +30 -0
  43. package/src/Avatar/__tests__/Avatar.fallback.test.tsx +75 -0
  44. package/src/Avatar/__tests__/Avatar.image-loading.test.tsx +81 -0
  45. package/src/Avatar/hooks/index.ts +2 -0
  46. package/src/Avatar/hooks/useAvatarContext.ts +1 -0
  47. package/src/Avatar/hooks/useAvatarImage.ts +40 -0
  48. package/src/Avatar/index.ts +3 -0
  49. package/src/Avatar/types.ts +44 -0
  50. package/src/Breadcrumb/Breadcrumb.tsx +234 -0
  51. package/src/Breadcrumb/README.md +111 -0
  52. package/src/Breadcrumb/__tests__/Breadcrumb.asChild.test.tsx +33 -0
  53. package/src/Breadcrumb/__tests__/Breadcrumb.basic-rendering.test.tsx +132 -0
  54. package/src/Breadcrumb/index.ts +2 -0
  55. package/src/Breadcrumb/types.ts +22 -0
  56. package/src/Button/Button.tsx +95 -0
  57. package/src/Button/README.md +112 -0
  58. package/src/Button/__tests__/Button.asChild.test.tsx +91 -0
  59. package/src/Button/__tests__/Button.basic-rendering.test.tsx +126 -0
  60. package/src/Button/__tests__/Button.contract.test.tsx +72 -0
  61. package/src/Button/__tests__/Button.disabled.test.tsx +52 -0
  62. package/src/Button/__tests__/Button.icon-usage.test.tsx +57 -0
  63. package/src/Button/__tests__/Button.keyboard-interaction.test.tsx +70 -0
  64. package/src/Button/index.ts +2 -0
  65. package/src/Button/types.ts +8 -0
  66. package/src/Carousel/Carousel.tsx +708 -0
  67. package/src/Carousel/CarouselContext.ts +11 -0
  68. package/src/Carousel/README.md +848 -0
  69. package/src/Carousel/__tests__/Carousel.asChild.test.tsx +178 -0
  70. package/src/Carousel/__tests__/Carousel.auto-play.test.tsx +617 -0
  71. package/src/Carousel/__tests__/Carousel.basic-rendering.test.tsx +569 -0
  72. package/src/Carousel/__tests__/Carousel.controlled-state.test.tsx +137 -0
  73. package/src/Carousel/__tests__/Carousel.error-handling.test.tsx +81 -0
  74. package/src/Carousel/__tests__/Carousel.ids.test.tsx +111 -0
  75. package/src/Carousel/__tests__/Carousel.imperative-api.test.tsx +213 -0
  76. package/src/Carousel/__tests__/Carousel.indicators.test.tsx +560 -0
  77. package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +276 -0
  78. package/src/Carousel/__tests__/Carousel.keyboard-navigation.test.tsx +158 -0
  79. package/src/Carousel/__tests__/Carousel.play-pause.test.tsx +232 -0
  80. package/src/Carousel/__tests__/Carousel.prev-next.test.tsx +68 -0
  81. package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +49 -0
  82. package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +87 -0
  83. package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +179 -0
  84. package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +109 -0
  85. package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +151 -0
  86. package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +183 -0
  87. package/src/Carousel/__tests__/Carousel.touch-interaction.test.tsx +96 -0
  88. package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +70 -0
  89. package/src/Carousel/__tests__/Carousel.translations.test.tsx +157 -0
  90. package/src/Carousel/__tests__/Carousel.uncontrolled-state.test.tsx +146 -0
  91. package/src/Carousel/hooks/index.ts +4 -0
  92. package/src/Carousel/hooks/useCarouselContext.ts +13 -0
  93. package/src/Carousel/hooks/useCarouselRoot.ts +450 -0
  94. package/src/Carousel/hooks/useCarouselSlide.ts +45 -0
  95. package/src/Carousel/hooks/useCarouselViewport.ts +290 -0
  96. package/src/Carousel/index.ts +3 -0
  97. package/src/Carousel/types.ts +400 -0
  98. package/src/Checkbox/Checkbox.tsx +228 -0
  99. package/src/Checkbox/CheckboxContext.ts +12 -0
  100. package/src/Checkbox/README.md +156 -0
  101. package/src/Checkbox/__tests__/Checkbox.asChild.test.tsx +69 -0
  102. package/src/Checkbox/__tests__/Checkbox.basic-rendering.test.tsx +41 -0
  103. package/src/Checkbox/__tests__/Checkbox.controlled-state.test.tsx +82 -0
  104. package/src/Checkbox/__tests__/Checkbox.disabled.test.tsx +15 -0
  105. package/src/Checkbox/__tests__/Checkbox.indeterminate.test.tsx +82 -0
  106. package/src/Checkbox/__tests__/Checkbox.indicator.test.tsx +117 -0
  107. package/src/Checkbox/__tests__/Checkbox.uncontrolled-state.test.tsx +89 -0
  108. package/src/Checkbox/hooks/index.ts +2 -0
  109. package/src/Checkbox/hooks/useCheckboxContext.ts +1 -0
  110. package/src/Checkbox/hooks/useCheckboxRoot.ts +32 -0
  111. package/src/Checkbox/index.ts +1 -0
  112. package/src/Checkbox/types.ts +33 -0
  113. package/src/CheckboxCard/CheckboxCard.tsx +208 -0
  114. package/src/CheckboxCard/CheckboxCardContext.ts +12 -0
  115. package/src/CheckboxCard/README.md +114 -0
  116. package/src/CheckboxCard/__tests__/CheckboxCard.asChild.test.tsx +54 -0
  117. package/src/CheckboxCard/__tests__/CheckboxCard.basic-rendering.test.tsx +58 -0
  118. package/src/CheckboxCard/__tests__/CheckboxCard.controlled-state.test.tsx +77 -0
  119. package/src/CheckboxCard/__tests__/CheckboxCard.disabled.test.tsx +55 -0
  120. package/src/CheckboxCard/__tests__/CheckboxCard.error-handling.test.tsx +20 -0
  121. package/src/CheckboxCard/__tests__/CheckboxCard.indeterminate.test.tsx +60 -0
  122. package/src/CheckboxCard/__tests__/CheckboxCard.indicator.test.tsx +136 -0
  123. package/src/CheckboxCard/__tests__/CheckboxCard.uncontrolled-state.test.tsx +73 -0
  124. package/src/CheckboxCard/hooks/index.ts +2 -0
  125. package/src/CheckboxCard/hooks/useCheckboxCardContext.ts +1 -0
  126. package/src/CheckboxCard/hooks/useCheckboxCardRoot.ts +30 -0
  127. package/src/CheckboxCard/index.ts +3 -0
  128. package/src/CheckboxCard/types.ts +33 -0
  129. package/src/Collapsible/Collapsible.tsx +316 -0
  130. package/src/Collapsible/CollapsibleContext.ts +7 -0
  131. package/src/Collapsible/README.md +174 -0
  132. package/src/Collapsible/__tests__/Collapsible.asChild.test.tsx +240 -0
  133. package/src/Collapsible/__tests__/Collapsible.basic-rendering.test.tsx +118 -0
  134. package/src/Collapsible/__tests__/Collapsible.controlled-state.test.tsx +134 -0
  135. package/src/Collapsible/__tests__/Collapsible.disabled.test.tsx +132 -0
  136. package/src/Collapsible/__tests__/Collapsible.error-handling.test.tsx +40 -0
  137. package/src/Collapsible/__tests__/Collapsible.forceMount.test.tsx +111 -0
  138. package/src/Collapsible/__tests__/Collapsible.triggerIcon.test.tsx +93 -0
  139. package/src/Collapsible/__tests__/Collapsible.uncontrolled-state.test.tsx +125 -0
  140. package/src/Collapsible/hooks/index.ts +2 -0
  141. package/src/Collapsible/hooks/useCollapsibleRoot.ts +34 -0
  142. package/src/Collapsible/hooks/useCollapsibleTrigger.ts +49 -0
  143. package/src/Collapsible/index.ts +1 -0
  144. package/src/Collapsible/types.ts +48 -0
  145. package/src/ContextMenu/ContextMenu.tsx +1004 -0
  146. package/src/ContextMenu/ContextMenuContentContext.ts +15 -0
  147. package/src/ContextMenu/ContextMenuContext.ts +21 -0
  148. package/src/ContextMenu/ContextMenuGroupContext.ts +8 -0
  149. package/src/ContextMenu/ContextMenuItemIndicatorContext.ts +8 -0
  150. package/src/ContextMenu/ContextMenuRadioGroupContext.ts +9 -0
  151. package/src/ContextMenu/ContextMenuSubContext.ts +15 -0
  152. package/src/ContextMenu/README.md +275 -0
  153. package/src/ContextMenu/__tests__/ContextMenu.asChild.test.tsx +186 -0
  154. package/src/ContextMenu/__tests__/ContextMenu.basic-rendering.test.tsx +39 -0
  155. package/src/ContextMenu/__tests__/ContextMenu.checkbox-item.test.tsx +145 -0
  156. package/src/ContextMenu/__tests__/ContextMenu.error-handling.test.tsx +113 -0
  157. package/src/ContextMenu/__tests__/ContextMenu.group-label.test.tsx +48 -0
  158. package/src/ContextMenu/__tests__/ContextMenu.item-indicator.test.tsx +88 -0
  159. package/src/ContextMenu/__tests__/ContextMenu.item.test.tsx +106 -0
  160. package/src/ContextMenu/__tests__/ContextMenu.keyboard-interaction.test.tsx +172 -0
  161. package/src/ContextMenu/__tests__/ContextMenu.mouse-interaction.test.tsx +227 -0
  162. package/src/ContextMenu/__tests__/ContextMenu.radio-item.test.tsx +127 -0
  163. package/src/ContextMenu/__tests__/ContextMenu.reading-direction.test.tsx +152 -0
  164. package/src/ContextMenu/__tests__/ContextMenu.separator.test.tsx +47 -0
  165. package/src/ContextMenu/__tests__/ContextMenu.state-modes.test.tsx +119 -0
  166. package/src/ContextMenu/__tests__/ContextMenu.sub.test.tsx +262 -0
  167. package/src/ContextMenu/__tests__/ContextMenu.typeahead.test.tsx +89 -0
  168. package/src/ContextMenu/constants.ts +4 -0
  169. package/src/ContextMenu/index.ts +1 -0
  170. package/src/ContextMenu/types.ts +199 -0
  171. package/src/DirectionProvider/DirectionContext.ts +21 -0
  172. package/src/DirectionProvider/DirectionProvider.tsx +31 -0
  173. package/src/DirectionProvider/README.md +62 -0
  174. package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +29 -0
  175. package/src/DirectionProvider/index.ts +3 -0
  176. package/src/DirectionProvider/types.ts +10 -0
  177. package/src/Divider/Divider.tsx +57 -0
  178. package/src/Divider/README.md +57 -0
  179. package/src/Divider/__tests__/Divider.test.tsx +41 -0
  180. package/src/Divider/index.ts +1 -0
  181. package/src/Divider/types.ts +5 -0
  182. package/src/Dropdown/Dropdown.tsx +842 -0
  183. package/src/Dropdown/DropdownContentContext.ts +15 -0
  184. package/src/Dropdown/DropdownContext.ts +17 -0
  185. package/src/Dropdown/DropdownGroupContext.ts +8 -0
  186. package/src/Dropdown/DropdownItemIndicatorContext.ts +13 -0
  187. package/src/Dropdown/DropdownRadioGroupContext.ts +9 -0
  188. package/src/Dropdown/DropdownSubContext.ts +15 -0
  189. package/src/Dropdown/README.md +284 -0
  190. package/src/Dropdown/__tests__/Dropdown.asChild.test.tsx +286 -0
  191. package/src/Dropdown/__tests__/Dropdown.basic-rendering.test.tsx +43 -0
  192. package/src/Dropdown/__tests__/Dropdown.checkbox-item.test.tsx +121 -0
  193. package/src/Dropdown/__tests__/Dropdown.disabled.test.tsx +143 -0
  194. package/src/Dropdown/__tests__/Dropdown.error-handling.test.tsx +85 -0
  195. package/src/Dropdown/__tests__/Dropdown.group-label.test.tsx +68 -0
  196. package/src/Dropdown/__tests__/Dropdown.item-indicator.test.tsx +260 -0
  197. package/src/Dropdown/__tests__/Dropdown.item.test.tsx +72 -0
  198. package/src/Dropdown/__tests__/Dropdown.keyboard-edge-cases.test.tsx +77 -0
  199. package/src/Dropdown/__tests__/Dropdown.keyboard-interaction.test.tsx +310 -0
  200. package/src/Dropdown/__tests__/Dropdown.mouse-interaction.test.tsx +347 -0
  201. package/src/Dropdown/__tests__/Dropdown.radio-item.test.tsx +134 -0
  202. package/src/Dropdown/__tests__/Dropdown.reading-direction.test.tsx +153 -0
  203. package/src/Dropdown/__tests__/Dropdown.separator.test.tsx +46 -0
  204. package/src/Dropdown/__tests__/Dropdown.state-modes.test.tsx +100 -0
  205. package/src/Dropdown/__tests__/Dropdown.sub.test.tsx +185 -0
  206. package/src/Dropdown/__tests__/Dropdown.trigger.test.tsx +110 -0
  207. package/src/Dropdown/__tests__/Dropdown.typeahead.test.tsx +133 -0
  208. package/src/Dropdown/constants.ts +4 -0
  209. package/src/Dropdown/hooks/index.ts +9 -0
  210. package/src/Dropdown/hooks/useCloseSiblingSub.ts +13 -0
  211. package/src/Dropdown/hooks/useDropdownContent.ts +162 -0
  212. package/src/Dropdown/hooks/useDropdownContext.ts +1 -0
  213. package/src/Dropdown/hooks/useDropdownGroup.ts +18 -0
  214. package/src/Dropdown/hooks/useDropdownItem.ts +49 -0
  215. package/src/Dropdown/hooks/useDropdownLabel.ts +15 -0
  216. package/src/Dropdown/hooks/useDropdownRoot.ts +57 -0
  217. package/src/Dropdown/hooks/useDropdownSubContext.ts +1 -0
  218. package/src/Dropdown/hooks/useDropdownTrigger.ts +31 -0
  219. package/src/Dropdown/index.ts +1 -0
  220. package/src/Dropdown/types.ts +200 -0
  221. package/src/EmptyState/EmptyState.tsx +245 -0
  222. package/src/EmptyState/README.md +129 -0
  223. package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +32 -0
  224. package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +30 -0
  225. package/src/EmptyState/__tests__/EmptyState.Media.test.tsx +34 -0
  226. package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +28 -0
  227. package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +26 -0
  228. package/src/EmptyState/index.ts +2 -0
  229. package/src/EmptyState/types.ts +21 -0
  230. package/src/Field/Field.tsx +239 -0
  231. package/src/Field/FieldContext.ts +22 -0
  232. package/src/Field/README.md +167 -0
  233. package/src/Field/__tests__/Field.asChild.test.tsx +83 -0
  234. package/src/Field/__tests__/Field.basic-rendering.test.tsx +104 -0
  235. package/src/Field/__tests__/Field.state-cascade.test.tsx +75 -0
  236. package/src/Field/hooks/index.ts +2 -0
  237. package/src/Field/hooks/useFieldContext.ts +1 -0
  238. package/src/Field/hooks/useFieldProps.ts +57 -0
  239. package/src/Field/index.ts +2 -0
  240. package/src/Field/types.ts +33 -0
  241. package/src/Fieldset/Fieldset.tsx +104 -0
  242. package/src/Fieldset/README.md +74 -0
  243. package/src/Fieldset/__tests__/Fieldset.basic-rendering.test.tsx +81 -0
  244. package/src/Fieldset/__tests__/Fieldset.disabled.test.tsx +41 -0
  245. package/src/Fieldset/index.ts +2 -0
  246. package/src/Fieldset/types.ts +5 -0
  247. package/src/Input/Input.tsx +120 -0
  248. package/src/Input/README.md +180 -0
  249. package/src/Input/__tests__/Input.asChild.test.tsx +85 -0
  250. package/src/Input/__tests__/Input.basic-rendering.test.tsx +118 -0
  251. package/src/Input/__tests__/Input.disabled.test.tsx +49 -0
  252. package/src/Input/__tests__/Input.field-integration.test.tsx +148 -0
  253. package/src/Input/index.ts +2 -0
  254. package/src/Input/types.ts +7 -0
  255. package/src/InputGroup/InputGroup.tsx +228 -0
  256. package/src/InputGroup/README.md +178 -0
  257. package/src/InputGroup/__tests__/InputGroup.asChild.test.tsx +109 -0
  258. package/src/InputGroup/__tests__/InputGroup.basic-rendering.test.tsx +106 -0
  259. package/src/InputGroup/index.ts +2 -0
  260. package/src/InputGroup/types.ts +13 -0
  261. package/src/MillerColumns/MillerColumns.tsx +329 -0
  262. package/src/MillerColumns/MillerColumnsContext.ts +25 -0
  263. package/src/MillerColumns/README.md +278 -0
  264. package/src/MillerColumns/__tests__/MillerColumns.aria.test.tsx +82 -0
  265. package/src/MillerColumns/__tests__/MillerColumns.asChild.test.tsx +106 -0
  266. package/src/MillerColumns/__tests__/MillerColumns.auto-scroll.test.tsx +68 -0
  267. package/src/MillerColumns/__tests__/MillerColumns.basic-rendering.test.tsx +52 -0
  268. package/src/MillerColumns/__tests__/MillerColumns.column-projection.test.tsx +161 -0
  269. package/src/MillerColumns/__tests__/MillerColumns.controlled-state.test.tsx +90 -0
  270. package/src/MillerColumns/__tests__/MillerColumns.data-attributes.test.tsx +77 -0
  271. package/src/MillerColumns/__tests__/MillerColumns.disabled-items.test.tsx +65 -0
  272. package/src/MillerColumns/__tests__/MillerColumns.error-handling.test.tsx +57 -0
  273. package/src/MillerColumns/__tests__/MillerColumns.fixtures.ts +15 -0
  274. package/src/MillerColumns/__tests__/MillerColumns.item-indicator.test.tsx +57 -0
  275. package/src/MillerColumns/__tests__/MillerColumns.keyboard-interaction.test.tsx +181 -0
  276. package/src/MillerColumns/__tests__/MillerColumns.preview-panel.test.tsx +47 -0
  277. package/src/MillerColumns/__tests__/MillerColumns.resize.test.tsx +137 -0
  278. package/src/MillerColumns/__tests__/MillerColumns.roving-tabindex.test.tsx +91 -0
  279. package/src/MillerColumns/__tests__/MillerColumns.selection.test.tsx +54 -0
  280. package/src/MillerColumns/__tests__/MillerColumns.uncontrolled-state.test.tsx +70 -0
  281. package/src/MillerColumns/hooks/index.ts +7 -0
  282. package/src/MillerColumns/hooks/useMillerColumnsColumn.ts +23 -0
  283. package/src/MillerColumns/hooks/useMillerColumnsColumnContext.ts +1 -0
  284. package/src/MillerColumns/hooks/useMillerColumnsContext.ts +1 -0
  285. package/src/MillerColumns/hooks/useMillerColumnsItem.ts +157 -0
  286. package/src/MillerColumns/hooks/useMillerColumnsItemContext.ts +1 -0
  287. package/src/MillerColumns/hooks/useMillerColumnsResizeHandle.ts +76 -0
  288. package/src/MillerColumns/hooks/useMillerColumnsRoot.ts +0 -0
  289. package/src/MillerColumns/index.ts +3 -0
  290. package/src/MillerColumns/types.ts +93 -0
  291. package/src/MillerColumns/useMillerColumnsSelection.ts +31 -0
  292. package/src/MillerColumns/utils.ts +75 -0
  293. package/src/Modal/Modal.tsx +474 -0
  294. package/src/Modal/ModalContext.ts +13 -0
  295. package/src/Modal/README.md +207 -0
  296. package/src/Modal/__tests__/Modal.accessibility.test.tsx +167 -0
  297. package/src/Modal/__tests__/Modal.asChild.test.tsx +162 -0
  298. package/src/Modal/__tests__/Modal.click-outside.test.tsx +115 -0
  299. package/src/Modal/__tests__/Modal.content.test.tsx +193 -0
  300. package/src/Modal/__tests__/Modal.controlled-state.test.tsx +120 -0
  301. package/src/Modal/__tests__/Modal.error-handling.test.tsx +30 -0
  302. package/src/Modal/__tests__/Modal.escape-hatches.test.tsx +99 -0
  303. package/src/Modal/__tests__/Modal.imperative-api.test.tsx +119 -0
  304. package/src/Modal/__tests__/Modal.nested.test.tsx +106 -0
  305. package/src/Modal/__tests__/Modal.overlay.test.tsx +99 -0
  306. package/src/Modal/__tests__/Modal.portal.test.tsx +90 -0
  307. package/src/Modal/__tests__/Modal.presence.test.tsx +111 -0
  308. package/src/Modal/__tests__/Modal.trigger.test.tsx +70 -0
  309. package/src/Modal/__tests__/Modal.uncontrolled-state.test.tsx +72 -0
  310. package/src/Modal/__tests__/dialog-polyfill.ts +40 -0
  311. package/src/Modal/hooks/index.ts +4 -0
  312. package/src/Modal/hooks/useModalContent.ts +62 -0
  313. package/src/Modal/hooks/useModalContext.ts +1 -0
  314. package/src/Modal/hooks/useModalRoot.ts +81 -0
  315. package/src/Modal/hooks/useModalTrigger.ts +25 -0
  316. package/src/Modal/index.ts +3 -0
  317. package/src/Modal/types.ts +76 -0
  318. package/src/Portal/Portal.tsx +28 -0
  319. package/src/Portal/README.md +70 -0
  320. package/src/Portal/__tests__/Portal.basic-rendering.test.tsx +17 -0
  321. package/src/Portal/index.ts +2 -0
  322. package/src/Portal/types.ts +6 -0
  323. package/src/Progress/Progress.tsx +178 -0
  324. package/src/Progress/ProgressContext.ts +15 -0
  325. package/src/Progress/README.md +112 -0
  326. package/src/Progress/__tests__/Progress.asChild.test.tsx +37 -0
  327. package/src/Progress/__tests__/Progress.basic-rendering.test.tsx +65 -0
  328. package/src/Progress/__tests__/Progress.error-handling.test.tsx +40 -0
  329. package/src/Progress/__tests__/Progress.fixtures.ts +7 -0
  330. package/src/Progress/__tests__/Progress.value.test.tsx +83 -0
  331. package/src/Progress/hooks/index.ts +2 -0
  332. package/src/Progress/hooks/useProgressContext.ts +1 -0
  333. package/src/Progress/hooks/useProgressRoot.ts +45 -0
  334. package/src/Progress/index.ts +3 -0
  335. package/src/Progress/types.ts +43 -0
  336. package/src/RadioCard/README.md +133 -0
  337. package/src/RadioCard/RadioCard.tsx +334 -0
  338. package/src/RadioCard/RadioCardContext.ts +23 -0
  339. package/src/RadioCard/RadioCardItemContext.ts +10 -0
  340. package/src/RadioCard/__tests__/RadioCard.asChild.test.tsx +76 -0
  341. package/src/RadioCard/__tests__/RadioCard.basic-rendering.test.tsx +87 -0
  342. package/src/RadioCard/__tests__/RadioCard.controlled-state.test.tsx +107 -0
  343. package/src/RadioCard/__tests__/RadioCard.disabled-items.test.tsx +61 -0
  344. package/src/RadioCard/__tests__/RadioCard.error-handling.test.tsx +35 -0
  345. package/src/RadioCard/__tests__/RadioCard.indicator.test.tsx +119 -0
  346. package/src/RadioCard/__tests__/RadioCard.keyboard-interaction.test.tsx +158 -0
  347. package/src/RadioCard/__tests__/RadioCard.orientation.test.tsx +90 -0
  348. package/src/RadioCard/__tests__/RadioCard.reading-direction.test.tsx +65 -0
  349. package/src/RadioCard/__tests__/RadioCard.uncontrolled-state.test.tsx +108 -0
  350. package/src/RadioCard/hooks/index.ts +3 -0
  351. package/src/RadioCard/hooks/useRadioCardContext.ts +1 -0
  352. package/src/RadioCard/hooks/useRadioCardItemContext.ts +1 -0
  353. package/src/RadioCard/hooks/useRadioCardRoot.ts +77 -0
  354. package/src/RadioCard/index.ts +4 -0
  355. package/src/RadioCard/types.ts +51 -0
  356. package/src/RadioGroup/README.md +185 -0
  357. package/src/RadioGroup/RadioGroup.tsx +353 -0
  358. package/src/RadioGroup/RadioGroupContext.ts +23 -0
  359. package/src/RadioGroup/RadioGroupItemContext.ts +10 -0
  360. package/src/RadioGroup/__tests__/RadioGroup.asChild.test.tsx +105 -0
  361. package/src/RadioGroup/__tests__/RadioGroup.basic-rendering.test.tsx +72 -0
  362. package/src/RadioGroup/__tests__/RadioGroup.controlled-state.test.tsx +109 -0
  363. package/src/RadioGroup/__tests__/RadioGroup.disabled-keydown-guards.test.tsx +68 -0
  364. package/src/RadioGroup/__tests__/RadioGroup.disabled.test.tsx +79 -0
  365. package/src/RadioGroup/__tests__/RadioGroup.error-handling.test.tsx +33 -0
  366. package/src/RadioGroup/__tests__/RadioGroup.indicator.test.tsx +85 -0
  367. package/src/RadioGroup/__tests__/RadioGroup.keyboard-interaction.test.tsx +135 -0
  368. package/src/RadioGroup/__tests__/RadioGroup.orientation.test.tsx +90 -0
  369. package/src/RadioGroup/__tests__/RadioGroup.reading-direction.test.tsx +65 -0
  370. package/src/RadioGroup/__tests__/RadioGroup.ref-forwarding.test.tsx +45 -0
  371. package/src/RadioGroup/__tests__/RadioGroup.roving-tabindex.test.tsx +105 -0
  372. package/src/RadioGroup/__tests__/RadioGroup.uncontrolled-state.test.tsx +96 -0
  373. package/src/RadioGroup/hooks/index.ts +3 -0
  374. package/src/RadioGroup/hooks/useRadioGroupContext.ts +1 -0
  375. package/src/RadioGroup/hooks/useRadioGroupItemContext.ts +1 -0
  376. package/src/RadioGroup/hooks/useRadioGroupRoot.ts +87 -0
  377. package/src/RadioGroup/index.ts +1 -0
  378. package/src/RadioGroup/types.ts +51 -0
  379. package/src/Select/README.md +203 -0
  380. package/src/Select/Select.tsx +204 -0
  381. package/src/Select/__tests__/Select.asChild.test.tsx +36 -0
  382. package/src/Select/__tests__/Select.basic-rendering.test.tsx +17 -0
  383. package/src/Select/__tests__/Select.controlled-state.test.tsx +69 -0
  384. package/src/Select/__tests__/Select.data-attributes.test.tsx +29 -0
  385. package/src/Select/__tests__/Select.field-integration.test.tsx +150 -0
  386. package/src/Select/__tests__/Select.group.test.tsx +42 -0
  387. package/src/Select/__tests__/Select.placeholder.test.tsx +32 -0
  388. package/src/Select/index.ts +2 -0
  389. package/src/Select/types.ts +89 -0
  390. package/src/SkipNav/README.md +87 -0
  391. package/src/SkipNav/SkipNav.tsx +116 -0
  392. package/src/SkipNav/__tests__/SkipNav.basic-rendering.test.tsx +23 -0
  393. package/src/SkipNav/__tests__/SkipNav.ids.test.tsx +19 -0
  394. package/src/SkipNav/index.ts +1 -0
  395. package/src/SkipNav/types.ts +26 -0
  396. package/src/Slider/README.md +215 -0
  397. package/src/Slider/Slider.tsx +308 -0
  398. package/src/Slider/SliderContext.ts +24 -0
  399. package/src/Slider/__tests__/Slider.asChild.test.tsx +119 -0
  400. package/src/Slider/__tests__/Slider.basic-rendering.test.tsx +157 -0
  401. package/src/Slider/__tests__/Slider.controlled-state.test.tsx +78 -0
  402. package/src/Slider/__tests__/Slider.disabled.test.tsx +82 -0
  403. package/src/Slider/__tests__/Slider.error-handling.test.tsx +45 -0
  404. package/src/Slider/__tests__/Slider.fixtures.ts +53 -0
  405. package/src/Slider/__tests__/Slider.form.test.tsx +67 -0
  406. package/src/Slider/__tests__/Slider.inverted.test.tsx +112 -0
  407. package/src/Slider/__tests__/Slider.keyboard-interaction.test.tsx +118 -0
  408. package/src/Slider/__tests__/Slider.multiple-thumbs.test.tsx +84 -0
  409. package/src/Slider/__tests__/Slider.orientation.test.tsx +101 -0
  410. package/src/Slider/__tests__/Slider.pointer-interaction.test.tsx +205 -0
  411. package/src/Slider/__tests__/Slider.reading-direction.test.tsx +99 -0
  412. package/src/Slider/__tests__/Slider.uncontrolled-state.test.tsx +69 -0
  413. package/src/Slider/__tests__/Slider.value-commit.test.tsx +103 -0
  414. package/src/Slider/hooks/index.ts +3 -0
  415. package/src/Slider/hooks/useSliderContext.ts +1 -0
  416. package/src/Slider/hooks/useSliderRoot.ts +197 -0
  417. package/src/Slider/hooks/useSliderThumb.ts +77 -0
  418. package/src/Slider/index.ts +3 -0
  419. package/src/Slider/types.ts +48 -0
  420. package/src/Slider/utils.ts +155 -0
  421. package/src/Slot/Slot.tsx +158 -0
  422. package/src/Slot/__tests__/Slot.test.tsx +163 -0
  423. package/src/Slot/__tests__/composeEventHandlers.test.ts +74 -0
  424. package/src/Slot/composeEventHandlers.ts +38 -0
  425. package/src/Slot/index.ts +3 -0
  426. package/src/Slot/types.ts +5 -0
  427. package/src/Status/README.md +50 -0
  428. package/src/Status/Status.tsx +44 -0
  429. package/src/Status/__tests__/Status.test.tsx +28 -0
  430. package/src/Status/index.ts +2 -0
  431. package/src/Status/types.ts +5 -0
  432. package/src/Switch/README.md +121 -0
  433. package/src/Switch/Switch.tsx +167 -0
  434. package/src/Switch/SwitchContext.ts +10 -0
  435. package/src/Switch/__tests__/Switch.asChild.test.tsx +56 -0
  436. package/src/Switch/__tests__/Switch.basic-rendering.test.tsx +76 -0
  437. package/src/Switch/__tests__/Switch.contract.test.tsx +109 -0
  438. package/src/Switch/__tests__/Switch.controlled-state.test.tsx +79 -0
  439. package/src/Switch/__tests__/Switch.disabled.test.tsx +60 -0
  440. package/src/Switch/__tests__/Switch.error-handling.test.tsx +20 -0
  441. package/src/Switch/__tests__/Switch.keyboard-interaction.test.tsx +56 -0
  442. package/src/Switch/__tests__/Switch.thumb.test.tsx +84 -0
  443. package/src/Switch/__tests__/Switch.uncontrolled-state.test.tsx +83 -0
  444. package/src/Switch/hooks/index.ts +2 -0
  445. package/src/Switch/hooks/useSwitchContext.ts +1 -0
  446. package/src/Switch/hooks/useSwitchRoot.ts +28 -0
  447. package/src/Switch/index.ts +3 -0
  448. package/src/Switch/types.ts +37 -0
  449. package/src/Table/README.md +205 -0
  450. package/src/Table/Table.tsx +380 -0
  451. package/src/Table/__tests__/Table.Body.test.tsx +61 -0
  452. package/src/Table/__tests__/Table.Caption.test.tsx +70 -0
  453. package/src/Table/__tests__/Table.Cell.test.tsx +73 -0
  454. package/src/Table/__tests__/Table.Footer.test.tsx +61 -0
  455. package/src/Table/__tests__/Table.Head.test.tsx +61 -0
  456. package/src/Table/__tests__/Table.Header.test.tsx +73 -0
  457. package/src/Table/__tests__/Table.Root.test.tsx +49 -0
  458. package/src/Table/__tests__/Table.Row.test.tsx +67 -0
  459. package/src/Table/__tests__/Table.ScrollArea.test.tsx +83 -0
  460. package/src/Table/index.ts +1 -0
  461. package/src/Table/types.ts +63 -0
  462. package/src/Tabs/README.md +110 -0
  463. package/src/Tabs/Tabs.tsx +434 -0
  464. package/src/Tabs/TabsContext.ts +13 -0
  465. package/src/Tabs/__tests__/Tabs.activation-mode.test.tsx +114 -0
  466. package/src/Tabs/__tests__/Tabs.asChild.test.tsx +91 -0
  467. package/src/Tabs/__tests__/Tabs.basic-rendering.test.tsx +483 -0
  468. package/src/Tabs/__tests__/Tabs.change-event-callbacks.test.tsx +133 -0
  469. package/src/Tabs/__tests__/Tabs.controlled-state.test.tsx +152 -0
  470. package/src/Tabs/__tests__/Tabs.disabled-tabs.test.tsx +203 -0
  471. package/src/Tabs/__tests__/Tabs.error-handling.test.tsx +82 -0
  472. package/src/Tabs/__tests__/Tabs.fixtures.ts +171 -0
  473. package/src/Tabs/__tests__/Tabs.imperative-api.test.tsx +118 -0
  474. package/src/Tabs/__tests__/Tabs.keyboard-interaction.test.tsx +192 -0
  475. package/src/Tabs/__tests__/Tabs.lazy-mount.test.tsx +61 -0
  476. package/src/Tabs/__tests__/Tabs.mouse-interaction.test.tsx +216 -0
  477. package/src/Tabs/__tests__/Tabs.reading-direction.test.tsx +58 -0
  478. package/src/Tabs/__tests__/Tabs.uncontrolled-state.test.tsx +197 -0
  479. package/src/Tabs/hooks/index.ts +4 -0
  480. package/src/Tabs/hooks/useTabsContent.ts +27 -0
  481. package/src/Tabs/hooks/useTabsContext.ts +1 -0
  482. package/src/Tabs/hooks/useTabsRoot.ts +148 -0
  483. package/src/Tabs/hooks/useTabsTrigger.ts +111 -0
  484. package/src/Tabs/index.ts +3 -0
  485. package/src/Tabs/types.ts +99 -0
  486. package/src/Tabs/utils.ts +8 -0
  487. package/src/Textarea/README.md +98 -0
  488. package/src/Textarea/Textarea.tsx +93 -0
  489. package/src/Textarea/__tests__/Textarea.asChild.test.tsx +85 -0
  490. package/src/Textarea/__tests__/Textarea.basic-rendering.test.tsx +107 -0
  491. package/src/Textarea/__tests__/Textarea.disabled.test.tsx +49 -0
  492. package/src/Textarea/__tests__/Textarea.field-integration.test.tsx +134 -0
  493. package/src/Textarea/index.ts +2 -0
  494. package/src/Textarea/types.ts +7 -0
  495. package/src/Toggle/README.md +97 -0
  496. package/src/Toggle/Toggle.tsx +81 -0
  497. package/src/Toggle/__tests__/Toggle.asChild.test.tsx +42 -0
  498. package/src/Toggle/__tests__/Toggle.basic-rendering.test.tsx +28 -0
  499. package/src/Toggle/__tests__/Toggle.controlled-state.test.tsx +60 -0
  500. package/src/Toggle/__tests__/Toggle.disabled.test.tsx +34 -0
  501. package/src/Toggle/__tests__/Toggle.keyboard-interaction.test.tsx +42 -0
  502. package/src/Toggle/__tests__/Toggle.uncontrolled-state.test.tsx +40 -0
  503. package/src/Toggle/index.ts +2 -0
  504. package/src/Toggle/types.ts +23 -0
  505. package/src/ToggleGroup/README.md +137 -0
  506. package/src/ToggleGroup/ToggleGroup.tsx +298 -0
  507. package/src/ToggleGroup/ToggleGroupContext.ts +9 -0
  508. package/src/ToggleGroup/__tests__/ToggleGroup.asChild.test.tsx +65 -0
  509. package/src/ToggleGroup/__tests__/ToggleGroup.basic-rendering.test.tsx +50 -0
  510. package/src/ToggleGroup/__tests__/ToggleGroup.disabled.test.tsx +54 -0
  511. package/src/ToggleGroup/__tests__/ToggleGroup.keyboard-interaction.test.tsx +151 -0
  512. package/src/ToggleGroup/__tests__/ToggleGroup.multiple-mode.test.tsx +144 -0
  513. package/src/ToggleGroup/__tests__/ToggleGroup.reading-direction.test.tsx +28 -0
  514. package/src/ToggleGroup/__tests__/ToggleGroup.single-mode.test.tsx +139 -0
  515. package/src/ToggleGroup/hooks/index.ts +2 -0
  516. package/src/ToggleGroup/hooks/useToggleGroupContext.ts +1 -0
  517. package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +110 -0
  518. package/src/ToggleGroup/index.ts +2 -0
  519. package/src/ToggleGroup/types.ts +72 -0
  520. package/src/Tooltip/README.md +214 -0
  521. package/src/Tooltip/Tooltip.tsx +260 -0
  522. package/src/Tooltip/TooltipContext.ts +20 -0
  523. package/src/Tooltip/__tests__/Tooltip.asChild.test.tsx +77 -0
  524. package/src/Tooltip/__tests__/Tooltip.basic-rendering.test.tsx +180 -0
  525. package/src/Tooltip/__tests__/Tooltip.controlled-state.test.tsx +128 -0
  526. package/src/Tooltip/__tests__/Tooltip.escape-hatches.test.tsx +73 -0
  527. package/src/Tooltip/__tests__/Tooltip.focus-interaction.test.tsx +88 -0
  528. package/src/Tooltip/__tests__/Tooltip.hover-interaction.test.tsx +179 -0
  529. package/src/Tooltip/__tests__/Tooltip.keyboard-interaction.test.tsx +85 -0
  530. package/src/Tooltip/__tests__/Tooltip.uncontrolled-state.test.tsx +67 -0
  531. package/src/Tooltip/hooks/index.ts +4 -0
  532. package/src/Tooltip/hooks/useTooltipContent.ts +53 -0
  533. package/src/Tooltip/hooks/useTooltipProvider.ts +41 -0
  534. package/src/Tooltip/hooks/useTooltipRoot.ts +106 -0
  535. package/src/Tooltip/hooks/useTooltipTrigger.ts +44 -0
  536. package/src/Tooltip/index.ts +1 -0
  537. package/src/Tooltip/types.ts +64 -0
  538. package/src/Tree/README.md +339 -0
  539. package/src/Tree/Tree.tsx +571 -0
  540. package/src/Tree/TreeContext.ts +24 -0
  541. package/src/Tree/__tests__/Tree.aria.test.tsx +53 -0
  542. package/src/Tree/__tests__/Tree.asChild.test.tsx +134 -0
  543. package/src/Tree/__tests__/Tree.basic-rendering.test.tsx +111 -0
  544. package/src/Tree/__tests__/Tree.branch-behaviour.test.tsx +87 -0
  545. package/src/Tree/__tests__/Tree.controlled-expansion.test.tsx +92 -0
  546. package/src/Tree/__tests__/Tree.data-attributes.test.tsx +88 -0
  547. package/src/Tree/__tests__/Tree.disabled-items.test.tsx +196 -0
  548. package/src/Tree/__tests__/Tree.error-handling.test.tsx +71 -0
  549. package/src/Tree/__tests__/Tree.forceMount.test.tsx +72 -0
  550. package/src/Tree/__tests__/Tree.keyboard-interaction.test.tsx +150 -0
  551. package/src/Tree/__tests__/Tree.multiple-selection.test.tsx +151 -0
  552. package/src/Tree/__tests__/Tree.range-selection.test.tsx +200 -0
  553. package/src/Tree/__tests__/Tree.recursion-depth.test.tsx +73 -0
  554. package/src/Tree/__tests__/Tree.roving-tabindex.test.tsx +117 -0
  555. package/src/Tree/__tests__/Tree.selection-path.test.tsx +404 -0
  556. package/src/Tree/__tests__/Tree.single-selection.test.tsx +108 -0
  557. package/src/Tree/__tests__/Tree.uncontrolled-expansion.test.tsx +69 -0
  558. package/src/Tree/hooks/index.ts +3 -0
  559. package/src/Tree/hooks/useTreeItemKeyboard.ts +86 -0
  560. package/src/Tree/hooks/useTreePath.ts +68 -0
  561. package/src/Tree/hooks/useTreeRoot.ts +279 -0
  562. package/src/Tree/index.ts +3 -0
  563. package/src/Tree/types.ts +224 -0
  564. package/src/Tree/utils.ts +59 -0
  565. package/src/VisuallyHidden/README.md +58 -0
  566. package/src/VisuallyHidden/VisuallyHidden.tsx +67 -0
  567. package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +59 -0
  568. package/src/VisuallyHidden/index.ts +2 -0
  569. package/src/VisuallyHidden/types.ts +5 -0
  570. package/src/hooks/index.ts +3 -0
  571. package/src/hooks/useCollection.ts +74 -0
  572. package/src/hooks/useControllableState.ts +81 -0
  573. package/src/hooks/useRovingTabindex.ts +178 -0
  574. package/src/index.ts +38 -0
  575. package/src/test/intersectionObserverPolyfill.ts +83 -0
  576. package/src/test/popoverPolyfill.ts +86 -0
  577. package/src/test/scrollPolyfill.ts +23 -0
  578. package/src/types.ts +13 -0
  579. package/src/utils/__tests__/createStrictContext.test.tsx +69 -0
  580. package/src/utils/__tests__/deriveId.test.ts +28 -0
  581. package/src/utils/__tests__/getKeyToActionMap.test.ts +106 -0
  582. package/src/utils/createStrictContext.ts +49 -0
  583. package/src/utils/deriveId.ts +31 -0
  584. package/src/utils/getKeyToActionMap.ts +95 -0
  585. package/src/utils/index.ts +3 -0
@@ -0,0 +1,234 @@
1
+ import { Slot } from "../Slot";
2
+
3
+ import {
4
+ BreadcrumbItemProps,
5
+ BreadcrumbLinkProps,
6
+ BreadcrumbListProps,
7
+ BreadcrumbPageProps,
8
+ BreadcrumbRootProps,
9
+ BreadcrumbSeparatorProps,
10
+ } from "./types";
11
+
12
+ /**
13
+ * The root of a Breadcrumb — a `<nav>` landmark that wraps the breadcrumb
14
+ * trail.
15
+ *
16
+ * The `<nav>` defaults to `aria-label="Breadcrumb"` so assistive technology
17
+ * announces it as the breadcrumb navigation landmark. Override `aria-label`
18
+ * if your product uses different terminology.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <Breadcrumb.Root>
23
+ * <Breadcrumb.List>…</Breadcrumb.List>
24
+ * </Breadcrumb.Root>
25
+ * ```
26
+ */
27
+ function BreadcrumbRoot({ children, ...rest }: BreadcrumbRootProps) {
28
+ return (
29
+ <nav aria-label="Breadcrumb" {...rest}>
30
+ {children}
31
+ </nav>
32
+ );
33
+ }
34
+
35
+ BreadcrumbRoot.displayName = "BreadcrumbRoot";
36
+
37
+ /**
38
+ * The ordered list of breadcrumb entries — renders an `<ol>`.
39
+ *
40
+ * An ordered list is the correct semantic: breadcrumb entries have a
41
+ * meaningful sequence from the site root to the current page. Contains
42
+ * {@link BreadcrumbItem | `Breadcrumb.Item`}s interleaved with
43
+ * {@link BreadcrumbSeparator | `Breadcrumb.Separator`}s.
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * <Breadcrumb.List>
48
+ * <Breadcrumb.Item>…</Breadcrumb.Item>
49
+ * <Breadcrumb.Separator />
50
+ * <Breadcrumb.Item>…</Breadcrumb.Item>
51
+ * </Breadcrumb.List>
52
+ * ```
53
+ */
54
+ function BreadcrumbList({ children, ...rest }: BreadcrumbListProps) {
55
+ return <ol {...rest}>{children}</ol>;
56
+ }
57
+
58
+ BreadcrumbList.displayName = "BreadcrumbList";
59
+
60
+ /**
61
+ * A single entry in the breadcrumb trail — renders an `<li>`.
62
+ *
63
+ * Wraps either a {@link BreadcrumbLink | `Breadcrumb.Link`} (an ancestor
64
+ * page) or a {@link BreadcrumbPage | `Breadcrumb.Page`} (the current page).
65
+ *
66
+ * @example
67
+ * ```tsx
68
+ * <Breadcrumb.Item>
69
+ * <Breadcrumb.Link href="/library">Library</Breadcrumb.Link>
70
+ * </Breadcrumb.Item>
71
+ * ```
72
+ */
73
+ function BreadcrumbItem({ children, ...rest }: BreadcrumbItemProps) {
74
+ return <li {...rest}>{children}</li>;
75
+ }
76
+
77
+ BreadcrumbItem.displayName = "BreadcrumbItem";
78
+
79
+ /**
80
+ * A link to an ancestor page — renders an `<a>`.
81
+ *
82
+ * Use for every entry except the current page. Pass `href` (and any other
83
+ * anchor attributes) directly.
84
+ *
85
+ * **`asChild` prop.** Pass `asChild` to render a consumer-supplied element —
86
+ * typically a routing library's `<Link>` — with the breadcrumb link's props
87
+ * merged in. The native `<a>` is dropped.
88
+ *
89
+ * @example
90
+ * ```tsx
91
+ * <Breadcrumb.Link href="/library">Library</Breadcrumb.Link>
92
+ * ```
93
+ *
94
+ * @example With a router link
95
+ * ```tsx
96
+ * <Breadcrumb.Link asChild>
97
+ * <RouterLink to="/library">Library</RouterLink>
98
+ * </Breadcrumb.Link>
99
+ * ```
100
+ */
101
+ function BreadcrumbLink({
102
+ children,
103
+ asChild = false,
104
+ ...rest
105
+ }: BreadcrumbLinkProps) {
106
+ if (asChild) {
107
+ return <Slot {...rest}>{children}</Slot>;
108
+ }
109
+ return <a {...rest}>{children}</a>;
110
+ }
111
+
112
+ BreadcrumbLink.displayName = "BreadcrumbLink";
113
+
114
+ /**
115
+ * The current page in the trail — renders a `<span aria-current="page">`.
116
+ *
117
+ * The last entry of a breadcrumb is the page the user is on, so it is not a
118
+ * link. `aria-current="page"` tells assistive technology this entry
119
+ * represents the current location.
120
+ *
121
+ * @example
122
+ * ```tsx
123
+ * <Breadcrumb.Item>
124
+ * <Breadcrumb.Page>Current article</Breadcrumb.Page>
125
+ * </Breadcrumb.Item>
126
+ * ```
127
+ */
128
+ function BreadcrumbPage({ children, ...rest }: BreadcrumbPageProps) {
129
+ return (
130
+ <span aria-current="page" {...rest}>
131
+ {children}
132
+ </span>
133
+ );
134
+ }
135
+
136
+ BreadcrumbPage.displayName = "BreadcrumbPage";
137
+
138
+ /**
139
+ * The visual divider between two breadcrumb entries — renders an
140
+ * `<li role="presentation" aria-hidden="true">`.
141
+ *
142
+ * The separator sits inside the `<ol>` as a sibling of the
143
+ * {@link BreadcrumbItem | `Breadcrumb.Item`}s, but `role="presentation"`
144
+ * removes it from the list semantics and `aria-hidden` hides it from the
145
+ * accessibility tree — it is purely decorative.
146
+ *
147
+ * Defaults to a `"/"` glyph; pass `children` to use a custom separator (an
148
+ * icon, a chevron, etc.).
149
+ *
150
+ * @example Default separator
151
+ * ```tsx
152
+ * <Breadcrumb.Separator />
153
+ * ```
154
+ *
155
+ * @example Custom separator
156
+ * ```tsx
157
+ * <Breadcrumb.Separator><ChevronRight /></Breadcrumb.Separator>
158
+ * ```
159
+ */
160
+ function BreadcrumbSeparator({
161
+ children = "/",
162
+ ...rest
163
+ }: BreadcrumbSeparatorProps) {
164
+ return (
165
+ <li role="presentation" aria-hidden="true" {...rest}>
166
+ {children}
167
+ </li>
168
+ );
169
+ }
170
+
171
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
172
+
173
+ type TBreadcrumbCompound = typeof BreadcrumbRoot & {
174
+ Root: typeof BreadcrumbRoot;
175
+ List: typeof BreadcrumbList;
176
+ Item: typeof BreadcrumbItem;
177
+ Link: typeof BreadcrumbLink;
178
+ Page: typeof BreadcrumbPage;
179
+ Separator: typeof BreadcrumbSeparator;
180
+ };
181
+
182
+ /**
183
+ * Headless, accessible **Breadcrumb** — a compound component implementing the
184
+ * [WAI-ARIA breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/):
185
+ * a `<nav>` landmark wrapping an ordered list of links to ancestor pages,
186
+ * ending with the current page. Stateless — purely structural. Zero styles
187
+ * ship.
188
+ *
189
+ * `Breadcrumb` is both callable (an alias of
190
+ * {@link BreadcrumbRoot | `Breadcrumb.Root`}) and carries its sub-components
191
+ * as static properties.
192
+ *
193
+ * - {@link BreadcrumbRoot | `Breadcrumb.Root`} — `<nav aria-label="Breadcrumb">`.
194
+ * - {@link BreadcrumbList | `Breadcrumb.List`} — `<ol>`, the ordered trail.
195
+ * - {@link BreadcrumbItem | `Breadcrumb.Item`} — `<li>`, one entry.
196
+ * - {@link BreadcrumbLink | `Breadcrumb.Link`} — `<a>`, an ancestor-page link.
197
+ * - {@link BreadcrumbPage | `Breadcrumb.Page`} — `<span aria-current="page">`, the current page.
198
+ * - {@link BreadcrumbSeparator | `Breadcrumb.Separator`} — decorative `<li>` divider.
199
+ *
200
+ * @example Minimal usage
201
+ * ```tsx
202
+ * import { Breadcrumb } from "@primitiv-ui/react";
203
+ *
204
+ * <Breadcrumb.Root>
205
+ * <Breadcrumb.List>
206
+ * <Breadcrumb.Item>
207
+ * <Breadcrumb.Link href="/">Home</Breadcrumb.Link>
208
+ * </Breadcrumb.Item>
209
+ * <Breadcrumb.Separator />
210
+ * <Breadcrumb.Item>
211
+ * <Breadcrumb.Link href="/library">Library</Breadcrumb.Link>
212
+ * </Breadcrumb.Item>
213
+ * <Breadcrumb.Separator />
214
+ * <Breadcrumb.Item>
215
+ * <Breadcrumb.Page>Current article</Breadcrumb.Page>
216
+ * </Breadcrumb.Item>
217
+ * </Breadcrumb.List>
218
+ * </Breadcrumb.Root>
219
+ * ```
220
+ *
221
+ * @see {@link BreadcrumbSeparator} for customising the divider glyph.
222
+ */
223
+ const BreadcrumbCompound: TBreadcrumbCompound = Object.assign(BreadcrumbRoot, {
224
+ Root: BreadcrumbRoot,
225
+ List: BreadcrumbList,
226
+ Item: BreadcrumbItem,
227
+ Link: BreadcrumbLink,
228
+ Page: BreadcrumbPage,
229
+ Separator: BreadcrumbSeparator,
230
+ });
231
+
232
+ BreadcrumbCompound.displayName = "Breadcrumb";
233
+
234
+ export { BreadcrumbCompound as Breadcrumb };
@@ -0,0 +1,111 @@
1
+ # Breadcrumb
2
+
3
+ Headless, accessible **Breadcrumb** — a compound component implementing the
4
+ [WAI-ARIA breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/):
5
+ a `<nav>` landmark wrapping an ordered list of links to ancestor pages, ending
6
+ with the current page. Stateless — purely structural. Zero styles ship.
7
+
8
+ ```tsx
9
+ import { Breadcrumb } from "@primitiv-ui/react";
10
+
11
+ <Breadcrumb.Root>
12
+ <Breadcrumb.List>
13
+ <Breadcrumb.Item>
14
+ <Breadcrumb.Link href="/">Home</Breadcrumb.Link>
15
+ </Breadcrumb.Item>
16
+ <Breadcrumb.Separator />
17
+ <Breadcrumb.Item>
18
+ <Breadcrumb.Link href="/library">Library</Breadcrumb.Link>
19
+ </Breadcrumb.Item>
20
+ <Breadcrumb.Separator />
21
+ <Breadcrumb.Item>
22
+ <Breadcrumb.Page>Current article</Breadcrumb.Page>
23
+ </Breadcrumb.Item>
24
+ </Breadcrumb.List>
25
+ </Breadcrumb.Root>;
26
+ ```
27
+
28
+ ## Sub-components
29
+
30
+ | Export | Element | ARIA hooks | `asChild` |
31
+ |--------|---------|------------|-----------|
32
+ | `Breadcrumb.Root` | `<nav>` | `aria-label="Breadcrumb"` (overridable) | — |
33
+ | `Breadcrumb.List` | `<ol>` | — | — |
34
+ | `Breadcrumb.Item` | `<li>` | — | — |
35
+ | `Breadcrumb.Link` | `<a>` | — | yes |
36
+ | `Breadcrumb.Page` | `<span>` | `aria-current="page"` | — |
37
+ | `Breadcrumb.Separator` | `<li>` | `role="presentation"`, `aria-hidden="true"` | — |
38
+
39
+ Every sub-component passes all of its element's native attributes straight
40
+ through to the DOM.
41
+
42
+ ## Structure
43
+
44
+ A breadcrumb is an **ordered list** — its entries have a meaningful sequence
45
+ from the site root to the current page — wrapped in a `<nav>` landmark.
46
+
47
+ - The `<nav>` defaults to `aria-label="Breadcrumb"`, which is how assistive
48
+ technology identifies the breadcrumb navigation landmark. Override it only
49
+ if your product uses different terminology.
50
+ - The final entry is the **current page**, so it is a `Breadcrumb.Page`
51
+ (a non-link `<span>`) rather than a `Breadcrumb.Link`.
52
+ - `Breadcrumb.Page` carries `aria-current="page"` to mark the user's current
53
+ location.
54
+
55
+ ## The current page
56
+
57
+ The last entry should always be a `Breadcrumb.Page`:
58
+
59
+ ```tsx
60
+ <Breadcrumb.Item>
61
+ <Breadcrumb.Page>Current article</Breadcrumb.Page>
62
+ </Breadcrumb.Item>
63
+ ```
64
+
65
+ It renders a `<span aria-current="page">` — not a link — because the page the
66
+ user is already on is not a navigation target.
67
+
68
+ ## Separators
69
+
70
+ `Breadcrumb.Separator` is a decorative `<li role="presentation" aria-hidden="true">`.
71
+ It sits inside the `<ol>` as a sibling of the items, but `role="presentation"`
72
+ removes it from the list semantics and `aria-hidden` hides it from the
73
+ accessibility tree — screen readers never announce it.
74
+
75
+ It defaults to a `"/"` glyph. Pass `children` to use a custom separator:
76
+
77
+ ```tsx
78
+ <Breadcrumb.Separator>›</Breadcrumb.Separator>
79
+ <Breadcrumb.Separator><ChevronRight /></Breadcrumb.Separator>
80
+ ```
81
+
82
+ ## `asChild` composition
83
+
84
+ `Breadcrumb.Link` accepts `asChild` so it can render a routing library's link
85
+ component instead of a bare `<a>`. The breadcrumb link's props are merged onto
86
+ the consumer's element and the native `<a>` is dropped.
87
+
88
+ ```tsx
89
+ import { Link as RouterLink } from "react-router-dom";
90
+
91
+ <Breadcrumb.Link asChild>
92
+ <RouterLink to="/library">Library</RouterLink>
93
+ </Breadcrumb.Link>;
94
+ ```
95
+
96
+ ## Styling hooks
97
+
98
+ No styles ship with the component. Target the rendered elements directly or
99
+ pass a `className` to any sub-component:
100
+
101
+ ```css
102
+ nav[aria-label="Breadcrumb"] ol {
103
+ display: flex;
104
+ gap: 0.5rem;
105
+ list-style: none;
106
+ }
107
+
108
+ nav[aria-label="Breadcrumb"] [aria-current="page"] {
109
+ font-weight: 600;
110
+ }
111
+ ```
@@ -0,0 +1,33 @@
1
+ import { render, screen } from "@testing-library/react";
2
+
3
+ import { Breadcrumb } from "../Breadcrumb";
4
+
5
+ describe("Breadcrumb asChild", () => {
6
+ it("renders Breadcrumb.Link as the consumer element, with no wrapping <a>", () => {
7
+ // Arrange & Act
8
+ render(
9
+ <Breadcrumb.Link asChild>
10
+ <button type="button">Library</button>
11
+ </Breadcrumb.Link>,
12
+ );
13
+ const link = screen.getByRole("button", { name: "Library" });
14
+
15
+ // Assert — the native <a> is dropped entirely
16
+ expect(link.closest("a")).toBeNull();
17
+ });
18
+
19
+ it("merges Breadcrumb.Link props onto the asChild element", () => {
20
+ // Arrange & Act
21
+ render(
22
+ <Breadcrumb.Link asChild className="crumb" data-testid="link">
23
+ <a href="/library">Library</a>
24
+ </Breadcrumb.Link>,
25
+ );
26
+ const link = screen.getByTestId("link");
27
+
28
+ // Assert — consumer's <a> kept; className and href both present on it
29
+ expect(link.tagName).toBe("A");
30
+ expect(link).toHaveClass("crumb");
31
+ expect(link).toHaveAttribute("href", "/library");
32
+ });
33
+ });
@@ -0,0 +1,132 @@
1
+ import { render, screen } from "@testing-library/react";
2
+
3
+ import { Breadcrumb } from "../Breadcrumb";
4
+
5
+ describe("Breadcrumb basic rendering", () => {
6
+ it('renders the Root as a <nav aria-label="Breadcrumb">', () => {
7
+ // Arrange & Act
8
+ render(<Breadcrumb.Root />);
9
+ const nav = screen.getByRole("navigation", { name: "Breadcrumb" });
10
+
11
+ // Assert
12
+ expect(nav.tagName).toBe("NAV");
13
+ });
14
+
15
+ it("lets the consumer override the Root aria-label", () => {
16
+ // Arrange & Act
17
+ render(<Breadcrumb.Root aria-label="You are here" />);
18
+
19
+ // Assert
20
+ expect(
21
+ screen.getByRole("navigation", { name: "You are here" }),
22
+ ).toBeInTheDocument();
23
+ });
24
+
25
+ it("renders the List as an <ol>", () => {
26
+ // Arrange & Act
27
+ render(
28
+ <Breadcrumb.Root>
29
+ <Breadcrumb.List />
30
+ </Breadcrumb.Root>,
31
+ );
32
+
33
+ // Assert
34
+ expect(screen.getByRole("list").tagName).toBe("OL");
35
+ });
36
+
37
+ it("renders the Item as an <li>", () => {
38
+ // Arrange & Act
39
+ render(
40
+ <Breadcrumb.Root>
41
+ <Breadcrumb.List>
42
+ <Breadcrumb.Item>Home</Breadcrumb.Item>
43
+ </Breadcrumb.List>
44
+ </Breadcrumb.Root>,
45
+ );
46
+
47
+ // Assert
48
+ expect(screen.getByRole("listitem").tagName).toBe("LI");
49
+ });
50
+
51
+ it("renders the Link as an <a> carrying its href", () => {
52
+ // Arrange & Act
53
+ render(<Breadcrumb.Link href="/library">Library</Breadcrumb.Link>);
54
+ const link = screen.getByRole("link", { name: "Library" });
55
+
56
+ // Assert
57
+ expect(link.tagName).toBe("A");
58
+ expect(link).toHaveAttribute("href", "/library");
59
+ });
60
+
61
+ it('renders the Page as a <span aria-current="page">', () => {
62
+ // Arrange & Act
63
+ render(<Breadcrumb.Page>Current article</Breadcrumb.Page>);
64
+ const page = screen.getByText("Current article");
65
+
66
+ // Assert
67
+ expect(page.tagName).toBe("SPAN");
68
+ expect(page).toHaveAttribute("aria-current", "page");
69
+ });
70
+
71
+ it("renders the Separator as a decorative <li> with a default glyph", () => {
72
+ // Arrange & Act
73
+ render(
74
+ <Breadcrumb.Root>
75
+ <Breadcrumb.List>
76
+ <Breadcrumb.Separator data-testid="sep" />
77
+ </Breadcrumb.List>
78
+ </Breadcrumb.Root>,
79
+ );
80
+ const separator = screen.getByTestId("sep");
81
+
82
+ // Assert — presentation role keeps it out of the list semantics
83
+ expect(separator.tagName).toBe("LI");
84
+ expect(separator).toHaveAttribute("role", "presentation");
85
+ expect(separator).toHaveAttribute("aria-hidden", "true");
86
+ expect(separator).toHaveTextContent("/");
87
+ expect(screen.queryByRole("listitem")).not.toBeInTheDocument();
88
+ });
89
+
90
+ it("lets the consumer override the Separator glyph", () => {
91
+ // Arrange & Act
92
+ render(
93
+ <Breadcrumb.Root>
94
+ <Breadcrumb.List>
95
+ <Breadcrumb.Separator data-testid="sep">&rsaquo;</Breadcrumb.Separator>
96
+ </Breadcrumb.List>
97
+ </Breadcrumb.Root>,
98
+ );
99
+
100
+ // Assert
101
+ expect(screen.getByTestId("sep")).toHaveTextContent("›");
102
+ });
103
+
104
+ it("composes a full trail of links, separators, and the current page", () => {
105
+ // Arrange & Act
106
+ render(
107
+ <Breadcrumb.Root>
108
+ <Breadcrumb.List>
109
+ <Breadcrumb.Item>
110
+ <Breadcrumb.Link href="/">Home</Breadcrumb.Link>
111
+ </Breadcrumb.Item>
112
+ <Breadcrumb.Separator />
113
+ <Breadcrumb.Item>
114
+ <Breadcrumb.Link href="/library">Library</Breadcrumb.Link>
115
+ </Breadcrumb.Item>
116
+ <Breadcrumb.Separator />
117
+ <Breadcrumb.Item>
118
+ <Breadcrumb.Page>Current article</Breadcrumb.Page>
119
+ </Breadcrumb.Item>
120
+ </Breadcrumb.List>
121
+ </Breadcrumb.Root>,
122
+ );
123
+
124
+ // Assert — two links, three list items, current page marked
125
+ expect(screen.getAllByRole("link")).toHaveLength(2);
126
+ expect(screen.getAllByRole("listitem")).toHaveLength(3);
127
+ expect(screen.getByText("Current article")).toHaveAttribute(
128
+ "aria-current",
129
+ "page",
130
+ );
131
+ });
132
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./Breadcrumb";
2
+ export * from "./types";
@@ -0,0 +1,22 @@
1
+ import { ComponentProps } from "react";
2
+
3
+ /** Props for {@link Breadcrumb.Root} — all `<nav>` attributes. */
4
+ export type BreadcrumbRootProps = ComponentProps<"nav">;
5
+
6
+ /** Props for {@link Breadcrumb.List} — all `<ol>` attributes. */
7
+ export type BreadcrumbListProps = ComponentProps<"ol">;
8
+
9
+ /** Props for {@link Breadcrumb.Item} — all `<li>` attributes. */
10
+ export type BreadcrumbItemProps = ComponentProps<"li">;
11
+
12
+ /** Props for {@link Breadcrumb.Link} — all `<a>` attributes plus `asChild`. */
13
+ export type BreadcrumbLinkProps = ComponentProps<"a"> & {
14
+ /** Render the consumer's own element instead of the native `<a>`. */
15
+ asChild?: boolean;
16
+ };
17
+
18
+ /** Props for {@link Breadcrumb.Page} — all `<span>` attributes. */
19
+ export type BreadcrumbPageProps = ComponentProps<"span">;
20
+
21
+ /** Props for {@link Breadcrumb.Separator} — all `<li>` attributes. */
22
+ export type BreadcrumbSeparatorProps = ComponentProps<"li">;
@@ -0,0 +1,95 @@
1
+ import { Slot } from "../Slot";
2
+ import { ButtonProps } from "./types";
3
+
4
+ /**
5
+ * A headless, accessible `<button>` element.
6
+ *
7
+ * Renders a native `<button type="button">` by default, preventing
8
+ * accidental form submission. The `type` prop can override this to
9
+ * `"submit"` or `"reset"` when the button lives inside a form.
10
+ *
11
+ * **Ref forwarding.** Pass a `ref` prop to access the underlying
12
+ * `HTMLButtonElement` directly:
13
+ *
14
+ * ```tsx
15
+ * const ref = useRef<HTMLButtonElement>(null);
16
+ * <Button ref={ref}>Click me</Button>
17
+ * ```
18
+ *
19
+ * **Disabled.** Sets native `disabled` (removing the button from the
20
+ * tab order and suppressing clicks) plus `data-disabled=""` so CSS can
21
+ * target `[data-disabled]` without relying on the `:disabled` pseudo-class:
22
+ *
23
+ * ```tsx
24
+ * <Button disabled>Save</Button>
25
+ * ```
26
+ *
27
+ * ```css
28
+ * button[data-disabled] { opacity: 0.5; cursor: not-allowed; }
29
+ * ```
30
+ *
31
+ * **`asChild` composition.** Renders the consumer's element instead of
32
+ * `<button>`, merging all props (aria-*, data-*, event handlers, ref)
33
+ * via the {@link Slot} utility. Event handlers compose with the child's
34
+ * own handlers (child runs first). `type` is **not** forwarded in this
35
+ * mode — the child element owns its own type semantics.
36
+ *
37
+ * **Icon-only buttons.** Provide `aria-label` or `aria-labelledby`
38
+ * when the button has no visible text label. Mark decorative icons
39
+ * `aria-hidden="true"`:
40
+ *
41
+ * ```tsx
42
+ * <Button aria-label="Close dialog">
43
+ * <CloseIcon aria-hidden="true" />
44
+ * </Button>
45
+ * ```
46
+ *
47
+ * @example Text button
48
+ * ```tsx
49
+ * <Button onClick={handleSave}>Save</Button>
50
+ * ```
51
+ *
52
+ * @example Submit button inside a form
53
+ * ```tsx
54
+ * <Button type="submit">Send</Button>
55
+ * ```
56
+ *
57
+ * @example Disabled
58
+ * ```tsx
59
+ * <Button disabled>Save</Button>
60
+ * ```
61
+ *
62
+ * @example asChild — render a router link styled as a button
63
+ * ```tsx
64
+ * <Button asChild>
65
+ * <a href="/settings">Settings</a>
66
+ * </Button>
67
+ * ```
68
+ */
69
+ export function Button({
70
+ asChild = false,
71
+ type = "button",
72
+ disabled,
73
+ children,
74
+ ref,
75
+ ...rest
76
+ }: ButtonProps) {
77
+ const rootProps = {
78
+ ...rest,
79
+ ref,
80
+ disabled,
81
+ "data-disabled": disabled ? "" : undefined,
82
+ };
83
+
84
+ if (asChild) {
85
+ return <Slot {...rootProps}>{children}</Slot>;
86
+ }
87
+
88
+ return (
89
+ <button type={type} {...rootProps}>
90
+ {children}
91
+ </button>
92
+ );
93
+ }
94
+
95
+ Button.displayName = "Button";