@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,205 @@
1
+ # Table
2
+
3
+ A compound component wrapping standard HTML table elements. Follows the
4
+ [WAI-ARIA table pattern](https://www.w3.org/WAI/ARIA/apg/patterns/table/)
5
+ with zero styles shipped.
6
+
7
+ ```tsx
8
+ import { Table } from "@primitiv-ui/react";
9
+
10
+ <Table.ScrollArea>
11
+ <Table.Root>
12
+ <Table.Caption>Team members</Table.Caption>
13
+ <Table.Head>
14
+ <Table.Row>
15
+ <Table.Header scope="col">Name</Table.Header>
16
+ <Table.Header scope="col">Role</Table.Header>
17
+ <Table.Header scope="col">Location</Table.Header>
18
+ </Table.Row>
19
+ </Table.Head>
20
+ <Table.Body>
21
+ <Table.Row>
22
+ <Table.Cell>Alice</Table.Cell>
23
+ <Table.Cell>Engineer</Table.Cell>
24
+ <Table.Cell>London</Table.Cell>
25
+ </Table.Row>
26
+ <Table.Row>
27
+ <Table.Cell>Bob</Table.Cell>
28
+ <Table.Cell>Designer</Table.Cell>
29
+ <Table.Cell>Berlin</Table.Cell>
30
+ </Table.Row>
31
+ </Table.Body>
32
+ <Table.Footer>
33
+ <Table.Row>
34
+ <Table.Cell colSpan={3}>2 team members</Table.Cell>
35
+ </Table.Row>
36
+ </Table.Footer>
37
+ </Table.Root>
38
+ </Table.ScrollArea>;
39
+ ```
40
+
41
+ ## Sub-components
42
+
43
+ | Export | Renders | Notes |
44
+ | ------------------ | ----------- | ------------------------------------------------------------------------------------------- |
45
+ | `Table.Root` | `<table>` | Implicit `role="table"`. Accepts all `TableHTMLAttributes`. |
46
+ | `Table.Head` | `<thead>` | Groups header rows. Browsers may repeat on printed pages. |
47
+ | `Table.Body` | `<tbody>` | Groups data rows. Multiple `<tbody>` elements are valid. |
48
+ | `Table.Footer` | `<tfoot>` | Groups footer/summary rows. Browsers may repeat on printed pages. |
49
+ | `Table.Row` | `<tr>` | Individual row. May contain `Table.Header` or `Table.Cell` children. |
50
+ | `Table.Header` | `<th>` | Header cell. Set `scope` for accessibility — see [Accessible headers](#accessible-headers). |
51
+ | `Table.Cell` | `<td>` | Data cell. Accepts `colSpan` and `rowSpan`. |
52
+ | `Table.ScrollArea` | `<div>` | Horizontal-scroll wrapper — see [Responsive scrolling](#responsive-scrolling). |
53
+ | `Table.Caption` | `<caption>` | Visible table label — see [Caption](#caption). |
54
+
55
+ ## Accessible headers
56
+
57
+ The `scope` attribute on `Table.Header` tells assistive technology which data
58
+ cells a header describes. Always set it — without `scope`, screen readers may
59
+ struggle to announce the correct header for each cell.
60
+
61
+ | Value | Associates header with |
62
+ | ------------ | ---------------------------------------------------- |
63
+ | `"col"` | All cells in the same column |
64
+ | `"row"` | All cells in the same row |
65
+ | `"colgroup"` | All cells in the column group spanned by this header |
66
+ | `"rowgroup"` | All cells in the row group spanned by this header |
67
+
68
+ ```tsx
69
+ <Table.Head>
70
+ <Table.Row>
71
+ <Table.Header scope="col">Name</Table.Header>
72
+ <Table.Header scope="col">Role</Table.Header>
73
+ </Table.Row>
74
+ </Table.Head>
75
+ <Table.Body>
76
+ <Table.Row>
77
+ {/* Row header for each data row */}
78
+ <Table.Header scope="row">Alice</Table.Header>
79
+ <Table.Cell>Engineer</Table.Cell>
80
+ </Table.Row>
81
+ </Table.Body>
82
+ ```
83
+
84
+ ## Caption
85
+
86
+ A `Table.Caption` is the preferred way to give a table an accessible name.
87
+ The browser programmatically associates `<caption>` with the `<table>`, so
88
+ assistive technology announces it when the user enters the table — no
89
+ `aria-label` on `Table.Root` is needed.
90
+
91
+ **`captionSide` prop.** Controls whether the caption appears above or below
92
+ the table via the CSS `caption-side` property. Defaults to `"bottom"`.
93
+
94
+ ```tsx
95
+ {
96
+ /* Caption below table (default) */
97
+ }
98
+ <Table.Root>
99
+ <Table.Caption>Q1 sales by region</Table.Caption>…
100
+ </Table.Root>;
101
+
102
+ {
103
+ /* Caption above table */
104
+ }
105
+ <Table.Root>
106
+ <Table.Caption captionSide="top">Q1 sales by region</Table.Caption>…
107
+ </Table.Root>;
108
+ ```
109
+
110
+ When a visible caption is not desirable, use `aria-label` directly on
111
+ `Table.Root` instead:
112
+
113
+ ```tsx
114
+ <Table.Root aria-label="Q1 sales by region">…</Table.Root>
115
+ ```
116
+
117
+ ## Responsive scrolling
118
+
119
+ Wrap `Table.Root` in `Table.ScrollArea` to allow horizontal scrolling on
120
+ narrow viewports instead of overflowing or compressing columns.
121
+
122
+ `Table.ScrollArea` applies `display: block`, `overflow-x: auto`, and
123
+ `max-width: 100%` as inline styles.
124
+
125
+ ```tsx
126
+ <Table.ScrollArea>
127
+ <Table.Root>…</Table.Root>
128
+ </Table.ScrollArea>
129
+ ```
130
+
131
+ Any `style` properties you pass are merged with (and take priority over) the
132
+ base scroll styles, so you can layer additional styles freely:
133
+
134
+ ```tsx
135
+ <Table.ScrollArea style={{ borderRadius: "8px" }}>
136
+ <Table.Root>…</Table.Root>
137
+ </Table.ScrollArea>
138
+ ```
139
+
140
+ ## Multiple `<tbody>` groups
141
+
142
+ Multiple `Table.Body` elements are valid HTML and useful for visually
143
+ separating logical row groups within a single table:
144
+
145
+ ```tsx
146
+ <Table.Root>
147
+ <Table.Head>…</Table.Head>
148
+ <Table.Body>
149
+ {/* Group 1 */}
150
+ <Table.Row>…</Table.Row>
151
+ </Table.Body>
152
+ <Table.Body>
153
+ {/* Group 2 */}
154
+ <Table.Row>…</Table.Row>
155
+ </Table.Body>
156
+ </Table.Root>
157
+ ```
158
+
159
+ ## Spanning cells
160
+
161
+ `Table.Cell` and `Table.Header` accept `colSpan` and `rowSpan`:
162
+
163
+ ```tsx
164
+ <Table.Row>
165
+ <Table.Cell colSpan={3}>Spans three columns</Table.Cell>
166
+ </Table.Row>
167
+ ```
168
+
169
+ ## Styling hooks
170
+
171
+ `Table` is a static layout component — it emits no `data-state` or
172
+ `data-orientation` attributes. Style it via element or attribute selectors
173
+ with whatever system you use:
174
+
175
+ ```css
176
+ table {
177
+ border-collapse: collapse;
178
+ width: 100%;
179
+ }
180
+ th,
181
+ td {
182
+ padding: 0.5rem 1rem;
183
+ text-align: left;
184
+ }
185
+ thead {
186
+ background: #f9fafb;
187
+ }
188
+ tfoot {
189
+ font-weight: bold;
190
+ }
191
+ tr:nth-child(even) td {
192
+ background: #f3f4f6;
193
+ }
194
+ [scope="col"] {
195
+ font-weight: 600;
196
+ }
197
+ caption {
198
+ font-size: 0.875rem;
199
+ color: #6b7280;
200
+ }
201
+ ```
202
+
203
+ ---
204
+
205
+ [Back to @primitiv-ui/react](../../README.md)
@@ -0,0 +1,380 @@
1
+ import {
2
+ TableBodyProps,
3
+ TableCaptionProps,
4
+ TableCellProps,
5
+ TableFooterProps,
6
+ TableHeaderProps,
7
+ TableHeadProps,
8
+ TableRootProps,
9
+ TableRowProps,
10
+ TableScrollAreaProps,
11
+ } from "./types";
12
+
13
+ /**
14
+ * The root of a Table widget — renders a plain `<table>` element and passes
15
+ * all `TableHTMLAttributes` through to the DOM.
16
+ *
17
+ * The `<table>` element carries an implicit `role="table"` in the accessibility
18
+ * tree. Assistive technology will announce it as a table and report the number
19
+ * of rows and columns to the user.
20
+ *
21
+ * Always pair column headers (`Table.Header`) with the correct
22
+ * {@link TableHeaderProps.scope | `scope`} attribute so screen readers can
23
+ * associate data cells with their headers.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <Table.Root>
28
+ * <Table.Head>
29
+ * <Table.Row>
30
+ * <Table.Header scope="col">Name</Table.Header>
31
+ * <Table.Header scope="col">Role</Table.Header>
32
+ * </Table.Row>
33
+ * </Table.Head>
34
+ * <Table.Body>
35
+ * <Table.Row>
36
+ * <Table.Cell>Alice</Table.Cell>
37
+ * <Table.Cell>Engineer</Table.Cell>
38
+ * </Table.Row>
39
+ * </Table.Body>
40
+ * </Table.Root>
41
+ * ```
42
+ */
43
+ function Table({ children, ...rest }: TableRootProps) {
44
+ return <table {...rest}>{children}</table>;
45
+ }
46
+
47
+ Table.displayName = "Table";
48
+
49
+ /**
50
+ * Groups header rows inside a `<thead>` element.
51
+ *
52
+ * Browsers and assistive technology treat rows inside `<thead>` as column
53
+ * headers. When a table is printed across multiple pages, the browser may
54
+ * repeat the `<thead>` content at the top of each page.
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * <Table.Head>
59
+ * <Table.Row>
60
+ * <Table.Header scope="col">Name</Table.Header>
61
+ * <Table.Header scope="col">Email</Table.Header>
62
+ * </Table.Row>
63
+ * </Table.Head>
64
+ * ```
65
+ */
66
+ function TableHead({ children, ...rest }: TableHeadProps) {
67
+ return <thead {...rest}>{children}</thead>;
68
+ }
69
+
70
+ TableHead.displayName = "TableHead";
71
+
72
+ /**
73
+ * Groups data rows inside a `<tbody>` element.
74
+ *
75
+ * Using `<tbody>` explicitly improves accessibility and allows browsers to
76
+ * scroll the body independently of a fixed header. Multiple `<tbody>` elements
77
+ * are valid and useful for visually separating logical row groups within a
78
+ * single table.
79
+ *
80
+ * @example
81
+ * ```tsx
82
+ * <Table.Body>
83
+ * <Table.Row>
84
+ * <Table.Cell>Alice</Table.Cell>
85
+ * <Table.Cell>alice@example.com</Table.Cell>
86
+ * </Table.Row>
87
+ * </Table.Body>
88
+ * ```
89
+ */
90
+ function TableBody({ children, ...rest }: TableBodyProps) {
91
+ return <tbody {...rest}>{children}</tbody>;
92
+ }
93
+
94
+ TableBody.displayName = "TableBody";
95
+
96
+ /**
97
+ * Groups footer rows inside a `<tfoot>` element.
98
+ *
99
+ * Typically used for summary rows — totals, averages, counts. When a table is
100
+ * printed across multiple pages some browsers repeat the `<tfoot>` content at
101
+ * the bottom of each page.
102
+ *
103
+ * @example
104
+ * ```tsx
105
+ * <Table.Footer>
106
+ * <Table.Row>
107
+ * <Table.Cell>Total</Table.Cell>
108
+ * <Table.Cell>£1,200</Table.Cell>
109
+ * </Table.Row>
110
+ * </Table.Footer>
111
+ * ```
112
+ */
113
+ function TableFooter({ children, ...rest }: TableFooterProps) {
114
+ return <tfoot {...rest}>{children}</tfoot>;
115
+ }
116
+
117
+ TableFooter.displayName = "TableFooter";
118
+
119
+ /**
120
+ * An individual table row — renders a `<tr>` element.
121
+ *
122
+ * May contain `Table.Header` (`<th>`) or `Table.Cell` (`<td>`) children, or a
123
+ * mix of both when the row contains both header and data cells (e.g. the first
124
+ * column of each row is a row header).
125
+ *
126
+ * @example
127
+ * ```tsx
128
+ * <Table.Row>
129
+ * <Table.Header scope="row">Alice</Table.Header>
130
+ * <Table.Cell>Engineer</Table.Cell>
131
+ * <Table.Cell>London</Table.Cell>
132
+ * </Table.Row>
133
+ * ```
134
+ */
135
+ function TableRow({ children, ...rest }: TableRowProps) {
136
+ return <tr {...rest}>{children}</tr>;
137
+ }
138
+
139
+ TableRow.displayName = "TableRow";
140
+
141
+ /**
142
+ * A header cell — renders a `<th>` element with an implicit
143
+ * `role="columnheader"` or `role="rowheader"` depending on context.
144
+ *
145
+ * **`scope` prop.** The `scope` attribute is the primary accessibility
146
+ * contract for table headers. Set it so assistive technology knows which data
147
+ * cells this header describes:
148
+ *
149
+ * | Value | Associates header with |
150
+ * | ----- | ---------------------- |
151
+ * | `"col"` | All cells in the same column |
152
+ * | `"row"` | All cells in the same row |
153
+ * | `"colgroup"` | All cells in the column group spanned by this header |
154
+ * | `"rowgroup"` | All cells in the row group spanned by this header |
155
+ *
156
+ * All `ThHTMLAttributes` (including `colSpan`, `rowSpan`, `abbr`) pass
157
+ * through to the DOM.
158
+ *
159
+ * @example Column header
160
+ * ```tsx
161
+ * <Table.Header scope="col">Name</Table.Header>
162
+ * ```
163
+ *
164
+ * @example Row header
165
+ * ```tsx
166
+ * <Table.Header scope="row">Alice</Table.Header>
167
+ * ```
168
+ */
169
+ function TableHeader({ children, ...rest }: TableHeaderProps) {
170
+ return <th {...rest}>{children}</th>;
171
+ }
172
+
173
+ TableHeader.displayName = "TableHeader";
174
+
175
+ /**
176
+ * A data cell — renders a `<td>` element.
177
+ *
178
+ * All `TdHTMLAttributes` pass through to the DOM, including `colSpan` and
179
+ * `rowSpan` for spanning multiple columns or rows.
180
+ *
181
+ * @example Basic cell
182
+ * ```tsx
183
+ * <Table.Cell>alice@example.com</Table.Cell>
184
+ * ```
185
+ *
186
+ * @example Spanning two columns
187
+ * ```tsx
188
+ * <Table.Cell colSpan={2}>Full-width note</Table.Cell>
189
+ * ```
190
+ */
191
+ function TableCell({ children, ...rest }: TableCellProps) {
192
+ return <td {...rest}>{children}</td>;
193
+ }
194
+
195
+ TableCell.displayName = "TableCell";
196
+
197
+ /**
198
+ * A horizontal-scroll wrapper for tables wider than their container.
199
+ *
200
+ * Renders a `<div>` with `display: block`, `overflow-x: auto`, and
201
+ * `max-width: 100%` applied as inline styles, then wraps a `Table.Root`
202
+ * inside it. Use this when your table has many columns and you want it to
203
+ * scroll horizontally on narrow viewports instead of overflowing or
204
+ * compressing.
205
+ *
206
+ * **Custom styles.** Any `style` properties you pass are merged with (and take priority
207
+ * over) the base scroll styles, so you can layer additional styles freely without
208
+ * repeating the scroll declarations:
209
+ *
210
+ * ```tsx
211
+ * <Table.ScrollArea style={{ borderRadius: "8px" }}>
212
+ * …
213
+ * </Table.ScrollArea>
214
+ * ```
215
+ *
216
+ * @example
217
+ * ```tsx
218
+ * <Table.ScrollArea>
219
+ * <Table.Root>
220
+ * …
221
+ * </Table.Root>
222
+ * </Table.ScrollArea>
223
+ * ```
224
+ */
225
+ function TableScrollArea({ children, style, ...rest }: TableScrollAreaProps) {
226
+ return (
227
+ <div
228
+ style={{
229
+ display: "block",
230
+ overflowX: "auto",
231
+ maxWidth: "100%",
232
+ ...style,
233
+ }}
234
+ {...rest}
235
+ >
236
+ {children}
237
+ </div>
238
+ );
239
+ }
240
+
241
+ TableScrollArea.displayName = "TableScrollArea";
242
+
243
+ /**
244
+ * A visible caption for the table — renders a `<caption>` element.
245
+ *
246
+ * A visible `<caption>` is the preferred way to label a table for
247
+ * accessibility. It is programmatically associated with the `<table>` element
248
+ * by the browser, so assistive technology announces it when the user enters
249
+ * the table — no `aria-label` or `aria-labelledby` on the table itself is
250
+ * needed.
251
+ *
252
+ * **`captionSide` prop.** Controls whether the caption appears above or below
253
+ * the table via the CSS `caption-side` property. Defaults to `"bottom"`.
254
+ *
255
+ * | Value | Position |
256
+ * | ----- | -------- |
257
+ * | `"bottom"` (default) | Below the table |
258
+ * | `"top"` | Above the table |
259
+ *
260
+ * @example Default (caption below table)
261
+ * ```tsx
262
+ * <Table.Root>
263
+ * <Table.Caption>Q1 sales figures by region</Table.Caption>
264
+ * …
265
+ * </Table.Root>
266
+ * ```
267
+ *
268
+ * @example Caption above table
269
+ * ```tsx
270
+ * <Table.Root>
271
+ * <Table.Caption captionSide="top">Q1 sales figures by region</Table.Caption>
272
+ * …
273
+ * </Table.Root>
274
+ * ```
275
+ */
276
+ function TableCaption({
277
+ children,
278
+ captionSide = "bottom",
279
+ ...rest
280
+ }: TableCaptionProps) {
281
+ return (
282
+ <caption style={{ captionSide }} {...rest}>
283
+ {children}
284
+ </caption>
285
+ );
286
+ }
287
+
288
+ TableCaption.displayName = "TableCaption";
289
+
290
+ type TableCompound = typeof Table & {
291
+ Root: typeof Table;
292
+ Head: typeof TableHead;
293
+ Body: typeof TableBody;
294
+ Footer: typeof TableFooter;
295
+ Row: typeof TableRow;
296
+ Header: typeof TableHeader;
297
+ Cell: typeof TableCell;
298
+ ScrollArea: typeof TableScrollArea;
299
+ Caption: typeof TableCaption;
300
+ };
301
+
302
+ const TableCompound: TableCompound = Object.assign(Table, {
303
+ Root: Table,
304
+ Head: TableHead,
305
+ Body: TableBody,
306
+ Footer: TableFooter,
307
+ Row: TableRow,
308
+ Header: TableHeader,
309
+ Cell: TableCell,
310
+ ScrollArea: TableScrollArea,
311
+ Caption: TableCaption,
312
+ });
313
+
314
+ /**
315
+ * Headless, accessible **Table** — a compound component wrapping standard
316
+ * HTML table elements with zero styles.
317
+ *
318
+ * `Table` is both callable (it's an alias of {@link Table | `Table.Root`})
319
+ * and carries its sub-components as static properties. Prefer the namespaced
320
+ * form in application code for readability:
321
+ *
322
+ * - {@link Table | `Table.Root`} — `<table>`, implicit `role="table"`.
323
+ * - {@link TableHead | `Table.Head`} — `<thead>`, groups header rows.
324
+ * - {@link TableBody | `Table.Body`} — `<tbody>`, groups data rows.
325
+ * - {@link TableFooter | `Table.Footer`} — `<tfoot>`, groups footer/summary rows.
326
+ * - {@link TableRow | `Table.Row`} — `<tr>`, individual row.
327
+ * - {@link TableHeader | `Table.Header`} — `<th>`, header cell; use `scope` for accessibility.
328
+ * - {@link TableCell | `Table.Cell`} — `<td>`, data cell.
329
+ * - {@link TableScrollArea | `Table.ScrollArea`} — horizontal-scroll wrapper for wide tables.
330
+ * - {@link TableCaption | `Table.Caption`} — `<caption>`, visible accessible table label.
331
+ *
332
+ * @example Minimal usage
333
+ * ```tsx
334
+ * import { Table } from "@primitiv-ui/react";
335
+ *
336
+ * export function Demo() {
337
+ * return (
338
+ * <Table.Root>
339
+ * <Table.Caption>Team members</Table.Caption>
340
+ * <Table.Head>
341
+ * <Table.Row>
342
+ * <Table.Header scope="col">Name</Table.Header>
343
+ * <Table.Header scope="col">Role</Table.Header>
344
+ * </Table.Row>
345
+ * </Table.Head>
346
+ * <Table.Body>
347
+ * <Table.Row>
348
+ * <Table.Cell>Alice</Table.Cell>
349
+ * <Table.Cell>Engineer</Table.Cell>
350
+ * </Table.Row>
351
+ * </Table.Body>
352
+ * </Table.Root>
353
+ * );
354
+ * }
355
+ * ```
356
+ *
357
+ * @example Responsive — horizontal scroll on narrow viewports
358
+ * ```tsx
359
+ * <Table.ScrollArea>
360
+ * <Table.Root>…</Table.Root>
361
+ * </Table.ScrollArea>
362
+ * ```
363
+ *
364
+ * @example Styling with any system
365
+ * Because no styles ship with the component, target the rendered elements
366
+ * with whatever system you use (CSS, Tailwind, design tokens, etc.):
367
+ *
368
+ * ```css
369
+ * table { border-collapse: collapse; width: 100%; }
370
+ * th, td { padding: 0.5rem 1rem; border: 1px solid #e5e7eb; }
371
+ * thead { background: #f9fafb; }
372
+ * ```
373
+ *
374
+ * @see {@link TableHeader} for the `scope` attribute and its accessibility contract.
375
+ * @see {@link TableCaption} for caption placement and why it beats `aria-label`.
376
+ * @see {@link TableScrollArea} for the horizontal-scroll style caveat.
377
+ */
378
+ TableCompound.displayName = "Table";
379
+
380
+ export { TableCompound as Table };
@@ -0,0 +1,61 @@
1
+ import { Table } from "..";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ describe("Table.Body rendering", () => {
5
+ it("should render tbody element", () => {
6
+ // Arrange
7
+ render(
8
+ <table>
9
+ <Table.Body />
10
+ </table>,
11
+ );
12
+ const tbody = screen.getByRole("rowgroup");
13
+
14
+ // Assert
15
+ expect(tbody).toBeVisible();
16
+ });
17
+
18
+ it("should render children correctly", () => {
19
+ // Arrange
20
+ render(
21
+ <table>
22
+ <Table.Body>
23
+ <tr>
24
+ <td>Example text</td>
25
+ </tr>
26
+ </Table.Body>
27
+ </table>,
28
+ );
29
+ const childElement = screen.getByText(/Example text/i);
30
+
31
+ // Assert
32
+ expect(childElement).toBeVisible();
33
+ });
34
+
35
+ it("should add className to tbody element", () => {
36
+ // Arrange
37
+ const testClass = "test-class";
38
+ render(
39
+ <table>
40
+ <Table.Body className={testClass} />
41
+ </table>,
42
+ );
43
+ const tbody = screen.getByRole("rowgroup");
44
+
45
+ // Assert
46
+ expect(tbody).toHaveClass(testClass);
47
+ });
48
+
49
+ it("should apply correct HTML attributes to tbody element", () => {
50
+ // Arrange
51
+ render(
52
+ <table>
53
+ <Table.Body id="test" />
54
+ </table>,
55
+ );
56
+ const tbody = screen.getByRole("rowgroup");
57
+
58
+ // Assert
59
+ expect(tbody).toHaveAttribute("id", "test");
60
+ });
61
+ });