@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,200 @@
1
+ import { ComponentProps, ReactNode, Ref } from "react";
2
+
3
+ import { CheckedState } from "../Checkbox/types";
4
+ import { Direction } from "../DirectionProvider";
5
+
6
+ type DropdownRootBaseProps = {
7
+ children?: ReactNode;
8
+ /**
9
+ * Reading direction for the menu. Affects which arrow key opens / closes
10
+ * a submenu — `ArrowRight` opens in `"ltr"`, `ArrowLeft` opens in
11
+ * `"rtl"`. Falls back to the inherited {@link DirectionProvider} value,
12
+ * or to `"ltr"` if no provider is present.
13
+ */
14
+ dir?: Direction;
15
+ };
16
+
17
+ type DropdownRootUncontrolledProps = DropdownRootBaseProps & {
18
+ defaultOpen?: boolean;
19
+ open?: never;
20
+ onOpenChange?: (open: boolean) => void;
21
+ };
22
+
23
+ type DropdownRootControlledProps = DropdownRootBaseProps & {
24
+ defaultOpen?: never;
25
+ open: boolean;
26
+ onOpenChange: (open: boolean) => void;
27
+ };
28
+
29
+ export type DropdownRootProps =
30
+ | DropdownRootUncontrolledProps
31
+ | DropdownRootControlledProps;
32
+
33
+ export type DropdownTriggerProps = Omit<
34
+ ComponentProps<"button">,
35
+ "aria-haspopup" | "aria-expanded" | "aria-controls"
36
+ > & {
37
+ children?: ReactNode;
38
+ ref?: Ref<HTMLButtonElement>;
39
+ asChild?: boolean;
40
+ };
41
+
42
+ export type DropdownContentProps = Omit<
43
+ ComponentProps<"menu">,
44
+ "role" | "popover" | "id"
45
+ > & {
46
+ children?: ReactNode;
47
+ ref?: Ref<HTMLMenuElement>;
48
+ asChild?: boolean;
49
+ };
50
+
51
+ export type DropdownItemProps = Omit<
52
+ ComponentProps<"li">,
53
+ "role" | "tabIndex" | "onSelect"
54
+ > & {
55
+ children?: ReactNode;
56
+ ref?: Ref<HTMLLIElement>;
57
+ asChild?: boolean;
58
+ disabled?: boolean;
59
+ /**
60
+ * Fires when the item is activated (click, Enter, or Space). Called
61
+ * with an event whose `preventDefault()` skips the auto-close that
62
+ * Dropdown performs after selection.
63
+ */
64
+ onSelect?: (event: Event) => void;
65
+ };
66
+
67
+ export type DropdownSeparatorProps = Omit<ComponentProps<"li">, "role"> & {
68
+ children?: ReactNode;
69
+ ref?: Ref<HTMLLIElement>;
70
+ asChild?: boolean;
71
+ };
72
+
73
+ export type DropdownItemIndicatorProps = ComponentProps<"span"> & {
74
+ children?: ReactNode;
75
+ ref?: Ref<HTMLSpanElement>;
76
+ asChild?: boolean;
77
+ /**
78
+ * Render the indicator even when its parent item is unchecked. The
79
+ * `data-state` attribute still reflects the live state (`"checked"` /
80
+ * `"unchecked"` / `"indeterminate"`), so consumers can animate the
81
+ * indicator in and out instead of mounting / unmounting it.
82
+ */
83
+ forceMount?: boolean;
84
+ };
85
+
86
+ export type DropdownGroupProps = Omit<ComponentProps<"li">, "role"> & {
87
+ children?: ReactNode;
88
+ ref?: Ref<HTMLLIElement>;
89
+ asChild?: boolean;
90
+ };
91
+
92
+ export type DropdownLabelProps = ComponentProps<"li"> & {
93
+ children?: ReactNode;
94
+ ref?: Ref<HTMLLIElement>;
95
+ asChild?: boolean;
96
+ };
97
+
98
+ type DropdownCheckboxItemBaseProps = Omit<
99
+ ComponentProps<"li">,
100
+ "role" | "tabIndex" | "aria-checked" | "defaultChecked" | "onSelect"
101
+ > & {
102
+ children?: ReactNode;
103
+ ref?: Ref<HTMLLIElement>;
104
+ asChild?: boolean;
105
+ disabled?: boolean;
106
+ /**
107
+ * Fires when activation completes and the close auto-fires. Call
108
+ * `event.preventDefault()` to keep the menu open after toggling.
109
+ */
110
+ onSelect?: (event: Event) => void;
111
+ };
112
+
113
+ type DropdownCheckboxItemUncontrolledProps = DropdownCheckboxItemBaseProps & {
114
+ defaultChecked?: CheckedState;
115
+ checked?: never;
116
+ onCheckedChange?: (checked: boolean) => void;
117
+ };
118
+
119
+ type DropdownCheckboxItemControlledProps = DropdownCheckboxItemBaseProps & {
120
+ defaultChecked?: never;
121
+ checked: CheckedState;
122
+ onCheckedChange: (checked: boolean) => void;
123
+ };
124
+
125
+ export type DropdownCheckboxItemProps =
126
+ | DropdownCheckboxItemUncontrolledProps
127
+ | DropdownCheckboxItemControlledProps;
128
+
129
+ type DropdownRadioGroupBaseProps = Omit<ComponentProps<"li">, "role"> & {
130
+ children?: ReactNode;
131
+ ref?: Ref<HTMLLIElement>;
132
+ asChild?: boolean;
133
+ };
134
+
135
+ type DropdownRadioGroupUncontrolledProps = DropdownRadioGroupBaseProps & {
136
+ defaultValue?: string;
137
+ value?: never;
138
+ onValueChange?: (value: string) => void;
139
+ };
140
+
141
+ type DropdownRadioGroupControlledProps = DropdownRadioGroupBaseProps & {
142
+ defaultValue?: never;
143
+ value: string;
144
+ onValueChange: (value: string) => void;
145
+ };
146
+
147
+ export type DropdownRadioGroupProps =
148
+ | DropdownRadioGroupUncontrolledProps
149
+ | DropdownRadioGroupControlledProps;
150
+
151
+ export type DropdownRadioItemProps = Omit<
152
+ ComponentProps<"li">,
153
+ "role" | "tabIndex" | "aria-checked" | "onSelect"
154
+ > & {
155
+ children?: ReactNode;
156
+ ref?: Ref<HTMLLIElement>;
157
+ asChild?: boolean;
158
+ disabled?: boolean;
159
+ value: string;
160
+ onSelect?: (event: Event) => void;
161
+ };
162
+
163
+ type DropdownSubBaseProps = {
164
+ children?: ReactNode;
165
+ };
166
+
167
+ type DropdownSubUncontrolledProps = DropdownSubBaseProps & {
168
+ defaultOpen?: boolean;
169
+ open?: never;
170
+ onOpenChange?: (open: boolean) => void;
171
+ };
172
+
173
+ type DropdownSubControlledProps = DropdownSubBaseProps & {
174
+ defaultOpen?: never;
175
+ open: boolean;
176
+ onOpenChange: (open: boolean) => void;
177
+ };
178
+
179
+ export type DropdownSubProps =
180
+ | DropdownSubUncontrolledProps
181
+ | DropdownSubControlledProps;
182
+
183
+ export type DropdownSubTriggerProps = Omit<
184
+ ComponentProps<"li">,
185
+ "role" | "tabIndex" | "aria-haspopup" | "aria-expanded" | "aria-controls"
186
+ > & {
187
+ children?: ReactNode;
188
+ ref?: Ref<HTMLLIElement>;
189
+ asChild?: boolean;
190
+ disabled?: boolean;
191
+ };
192
+
193
+ export type DropdownSubContentProps = Omit<
194
+ ComponentProps<"menu">,
195
+ "role" | "popover" | "id"
196
+ > & {
197
+ children?: ReactNode;
198
+ ref?: Ref<HTMLMenuElement>;
199
+ asChild?: boolean;
200
+ };
@@ -0,0 +1,245 @@
1
+ import { Slot } from "../Slot";
2
+ import {
3
+ EmptyStateActionsProps,
4
+ EmptyStateDescriptionProps,
5
+ EmptyStateMediaProps,
6
+ EmptyStateRootProps,
7
+ EmptyStateTitleProps,
8
+ } from "./types";
9
+
10
+ /**
11
+ * The root of an Empty State — renders a `<div role="status">` wrapping the
12
+ * placeholder shown when a collection, search, or view has no content.
13
+ *
14
+ * The `status` role makes the root a polite live region (implicit
15
+ * `aria-live="polite"`, `aria-atomic="true"`). Render the `EmptyState`
16
+ * conditionally — in place of the absent content — so that when it appears
17
+ * after a filter or search returns nothing, assistive technology announces
18
+ * it once the user is idle, without interrupting them.
19
+ *
20
+ * Opt out of the live region by passing `role={undefined}` for an empty
21
+ * state that is part of the initial, static page.
22
+ *
23
+ * **`asChild` composition.** Renders the consumer's element instead of a
24
+ * `<div>`, merging `role="status"` and all other props in via the
25
+ * {@link Slot} utility.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * {results.length === 0 && (
30
+ * <EmptyState.Root>
31
+ * <EmptyState.Title>No results</EmptyState.Title>
32
+ * <EmptyState.Description>Try a different search.</EmptyState.Description>
33
+ * </EmptyState.Root>
34
+ * )}
35
+ * ```
36
+ */
37
+ function EmptyStateRoot({
38
+ asChild = false,
39
+ children,
40
+ ...rest
41
+ }: EmptyStateRootProps) {
42
+ const rootProps = { role: "status", ...rest };
43
+
44
+ if (asChild) {
45
+ return <Slot {...rootProps}>{children}</Slot>;
46
+ }
47
+
48
+ return <div {...rootProps}>{children}</div>;
49
+ }
50
+
51
+ EmptyStateRoot.displayName = "EmptyStateRoot";
52
+
53
+ /**
54
+ * The illustration slot of an Empty State — renders a `<div aria-hidden="true">`
55
+ * wrapping a decorative icon or illustration.
56
+ *
57
+ * Empty-state artwork is decorative: the {@link EmptyStateTitle | `Title`} and
58
+ * {@link EmptyStateDescription | `Description`} carry the meaning. `Media` is
59
+ * therefore hidden from assistive technology by default, so screen-reader
60
+ * users are not read a redundant or meaningless image. If the artwork is
61
+ * genuinely informative, pass `aria-hidden={false}` and give it an accessible
62
+ * name yourself.
63
+ *
64
+ * **`asChild` composition.** Renders the consumer's element instead of a
65
+ * `<div>`, merging `aria-hidden="true"` and all other props in via the
66
+ * {@link Slot} utility.
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * <EmptyState.Media>
71
+ * <InboxIcon />
72
+ * </EmptyState.Media>
73
+ * ```
74
+ */
75
+ function EmptyStateMedia({
76
+ asChild = false,
77
+ children,
78
+ ...rest
79
+ }: EmptyStateMediaProps) {
80
+ const mediaProps = { "aria-hidden": true, ...rest };
81
+
82
+ if (asChild) {
83
+ return <Slot {...mediaProps}>{children}</Slot>;
84
+ }
85
+
86
+ return <div {...mediaProps}>{children}</div>;
87
+ }
88
+
89
+ EmptyStateMedia.displayName = "EmptyStateMedia";
90
+
91
+ /**
92
+ * The headline of an Empty State — renders a `<p>` with the short summary of
93
+ * why the view is empty (e.g. "No results found").
94
+ *
95
+ * A `<p>` is the safe default: a headless primitive cannot know the correct
96
+ * heading level for the surrounding document outline. When the empty state
97
+ * stands in for a titled section, promote the title to a real heading with
98
+ * `asChild` so it joins the page's heading hierarchy.
99
+ *
100
+ * **`asChild` composition.** Renders the consumer's element instead of a
101
+ * `<p>`, merging all props in via the {@link Slot} utility.
102
+ *
103
+ * @example Promote to a heading
104
+ * ```tsx
105
+ * <EmptyState.Title asChild>
106
+ * <h2>No results found</h2>
107
+ * </EmptyState.Title>
108
+ * ```
109
+ */
110
+ function EmptyStateTitle({
111
+ asChild = false,
112
+ children,
113
+ ...rest
114
+ }: EmptyStateTitleProps) {
115
+ if (asChild) {
116
+ return <Slot {...rest}>{children}</Slot>;
117
+ }
118
+
119
+ return <p {...rest}>{children}</p>;
120
+ }
121
+
122
+ EmptyStateTitle.displayName = "EmptyStateTitle";
123
+
124
+ /**
125
+ * The supporting copy of an Empty State — renders a `<p>` with the secondary
126
+ * text that explains the situation or suggests a next step (e.g. "Try
127
+ * adjusting your filters").
128
+ *
129
+ * Keep it to guidance the user can act on; the actionable controls themselves
130
+ * belong in {@link EmptyStateActions | `Actions`}.
131
+ *
132
+ * **`asChild` composition.** Renders the consumer's element instead of a
133
+ * `<p>`, merging all props in via the {@link Slot} utility.
134
+ *
135
+ * @example
136
+ * ```tsx
137
+ * <EmptyState.Description>Try adjusting your filters.</EmptyState.Description>
138
+ * ```
139
+ */
140
+ function EmptyStateDescription({
141
+ asChild = false,
142
+ children,
143
+ ...rest
144
+ }: EmptyStateDescriptionProps) {
145
+ if (asChild) {
146
+ return <Slot {...rest}>{children}</Slot>;
147
+ }
148
+
149
+ return <p {...rest}>{children}</p>;
150
+ }
151
+
152
+ EmptyStateDescription.displayName = "EmptyStateDescription";
153
+
154
+ /**
155
+ * The recovery-action slot of an Empty State — renders a `<div>` grouping the
156
+ * controls that let the user move on from the empty state (e.g. a "Clear
157
+ * filters" button or a "Create your first project" link).
158
+ *
159
+ * It is a plain grouping element with no role of its own, so the buttons and
160
+ * links inside keep their native semantics. As a child of
161
+ * {@link EmptyStateRoot | `Root`}'s live region, the control labels are
162
+ * included when the empty state is announced.
163
+ *
164
+ * **`asChild` composition.** Renders the consumer's element instead of a
165
+ * `<div>`, merging all props in via the {@link Slot} utility.
166
+ *
167
+ * @example
168
+ * ```tsx
169
+ * <EmptyState.Actions>
170
+ * <button onClick={clearFilters}>Clear filters</button>
171
+ * </EmptyState.Actions>
172
+ * ```
173
+ */
174
+ function EmptyStateActions({
175
+ asChild = false,
176
+ children,
177
+ ...rest
178
+ }: EmptyStateActionsProps) {
179
+ if (asChild) {
180
+ return <Slot {...rest}>{children}</Slot>;
181
+ }
182
+
183
+ return <div {...rest}>{children}</div>;
184
+ }
185
+
186
+ EmptyStateActions.displayName = "EmptyStateActions";
187
+
188
+ type EmptyStateCompound = typeof EmptyStateRoot & {
189
+ Root: typeof EmptyStateRoot;
190
+ Media: typeof EmptyStateMedia;
191
+ Title: typeof EmptyStateTitle;
192
+ Description: typeof EmptyStateDescription;
193
+ Actions: typeof EmptyStateActions;
194
+ };
195
+
196
+ const EmptyState: EmptyStateCompound = Object.assign(EmptyStateRoot, {
197
+ Root: EmptyStateRoot,
198
+ Media: EmptyStateMedia,
199
+ Title: EmptyStateTitle,
200
+ Description: EmptyStateDescription,
201
+ Actions: EmptyStateActions,
202
+ });
203
+
204
+ /**
205
+ * Headless, accessible **Empty State** — a stateless compound component for
206
+ * the placeholder shown when a collection, search, or view has no content.
207
+ *
208
+ * `EmptyState` is both callable (an alias of {@link EmptyStateRoot |
209
+ * `EmptyState.Root`}) and carries its sub-components as static properties.
210
+ * Prefer the namespaced form in application code:
211
+ *
212
+ * - {@link EmptyStateRoot | `EmptyState.Root`} — `<div role="status">`, the
213
+ * polite live region wrapping the placeholder.
214
+ * - {@link EmptyStateMedia | `EmptyState.Media`} — `<div aria-hidden="true">`,
215
+ * the decorative icon/illustration slot.
216
+ * - {@link EmptyStateTitle | `EmptyState.Title`} — `<p>`, the headline.
217
+ * - {@link EmptyStateDescription | `EmptyState.Description`} — `<p>`, the
218
+ * supporting copy.
219
+ * - {@link EmptyStateActions | `EmptyState.Actions`} — `<div>`, the
220
+ * recovery-action slot.
221
+ *
222
+ * All sub-components are stateless and optional — compose only the parts a
223
+ * given empty state needs.
224
+ *
225
+ * @example
226
+ * ```tsx
227
+ * import { EmptyState } from "@primitiv-ui/react";
228
+ *
229
+ * {results.length === 0 && (
230
+ * <EmptyState.Root>
231
+ * <EmptyState.Media>
232
+ * <SearchIcon />
233
+ * </EmptyState.Media>
234
+ * <EmptyState.Title>No results found</EmptyState.Title>
235
+ * <EmptyState.Description>Try adjusting your filters.</EmptyState.Description>
236
+ * <EmptyState.Actions>
237
+ * <button onClick={clearFilters}>Clear filters</button>
238
+ * </EmptyState.Actions>
239
+ * </EmptyState.Root>
240
+ * )}
241
+ * ```
242
+ */
243
+ EmptyState.displayName = "EmptyState";
244
+
245
+ export { EmptyState };
@@ -0,0 +1,129 @@
1
+ # EmptyState
2
+
3
+ A stateless compound component for the placeholder shown when a
4
+ collection, search, or view has no content. Renders a polite
5
+ [`status`](https://www.w3.org/TR/wai-aria-1.2/#status) live region so a
6
+ conditionally-mounted empty state is announced. Zero styles ship.
7
+
8
+ ```tsx
9
+ import { EmptyState } from "@primitiv-ui/react";
10
+
11
+ {
12
+ results.length === 0 && (
13
+ <EmptyState.Root>
14
+ <EmptyState.Media>
15
+ <SearchIcon />
16
+ </EmptyState.Media>
17
+ <EmptyState.Title>No results found</EmptyState.Title>
18
+ <EmptyState.Description>
19
+ Try adjusting your filters.
20
+ </EmptyState.Description>
21
+ <EmptyState.Actions>
22
+ <button onClick={clearFilters}>Clear filters</button>
23
+ </EmptyState.Actions>
24
+ </EmptyState.Root>
25
+ );
26
+ }
27
+ ```
28
+
29
+ ## Sub-components
30
+
31
+ All sub-components are stateless and optional — compose only the parts a
32
+ given empty state needs.
33
+
34
+ | Export | Renders | Notes |
35
+ | ------------------------ | ------------------------ | ------------------------------------------------------------------ |
36
+ | `EmptyState.Root` | `<div role="status">` | Polite live region wrapping the placeholder. |
37
+ | `EmptyState.Media` | `<div aria-hidden>` | Decorative icon/illustration slot — see [Media](#media). |
38
+ | `EmptyState.Title` | `<p>` | The headline — see [Title heading level](#title-heading-level). |
39
+ | `EmptyState.Description` | `<p>` | Secondary supporting copy. |
40
+ | `EmptyState.Actions` | `<div>` | Groups recovery controls (buttons/links). |
41
+
42
+ `EmptyState` is also callable directly as an alias of `EmptyState.Root`.
43
+
44
+ ## Props
45
+
46
+ Every sub-component accepts `asChild` plus all native props for the
47
+ element it renders:
48
+
49
+ | Prop | Type | Default | Notes |
50
+ | --------- | --------- | ------- | ---------------------------------------------------- |
51
+ | `asChild` | `boolean` | `false` | Render the consumer's own element instead — see below |
52
+ | `...rest` | native | — | All props for the rendered element, including `aria-*` |
53
+
54
+ ## Announce on appearance
55
+
56
+ `EmptyState.Root` renders `role="status"` — a polite live region with
57
+ implicit `aria-live="polite"` and `aria-atomic="true"`. Render the empty
58
+ state **conditionally**, in place of the absent content, so that when a
59
+ search or filter returns nothing the message is announced once the user
60
+ is idle:
61
+
62
+ ```tsx
63
+ {
64
+ results.length === 0 && <EmptyState.Root>No results found</EmptyState.Root>;
65
+ }
66
+ ```
67
+
68
+ For an empty state that is part of the initial, static page — and so has
69
+ nothing to announce — opt out of the live region:
70
+
71
+ ```tsx
72
+ <EmptyState.Root role={undefined}>…</EmptyState.Root>
73
+ ```
74
+
75
+ ## Media
76
+
77
+ Empty-state artwork is decorative: the `Title` and `Description` carry
78
+ the meaning. `EmptyState.Media` is therefore `aria-hidden` by default so
79
+ screen-reader users are not read a redundant image. If the artwork is
80
+ genuinely informative, opt back in and give it an accessible name:
81
+
82
+ ```tsx
83
+ <EmptyState.Media aria-hidden={false}>
84
+ <img src="/chart.svg" alt="Sales trending to zero" />
85
+ </EmptyState.Media>
86
+ ```
87
+
88
+ ## Title heading level
89
+
90
+ `EmptyState.Title` renders a `<p>` — a headless primitive cannot know
91
+ the correct heading level for the surrounding document outline. When the
92
+ empty state stands in for a titled section, promote the title to a real
93
+ heading with `asChild` so it joins the page's heading hierarchy:
94
+
95
+ ```tsx
96
+ <EmptyState.Title asChild>
97
+ <h2>No results found</h2>
98
+ </EmptyState.Title>
99
+ ```
100
+
101
+ ## asChild
102
+
103
+ Pass `asChild` to any sub-component to render the consumer's own element
104
+ instead of the default, merging in the sub-component's props (and, for
105
+ `Root` and `Media`, its `role` / `aria-hidden`) via the `Slot` utility:
106
+
107
+ ```tsx
108
+ <EmptyState.Root asChild>
109
+ <section>…</section>
110
+ </EmptyState.Root>
111
+ ```
112
+
113
+ ## Styling hooks
114
+
115
+ `EmptyState` emits no `data-*` attributes — it is a static layout
116
+ component. Style it with whatever system you use, targeting the rendered
117
+ elements directly or via your own `className`s:
118
+
119
+ ```tsx
120
+ <EmptyState.Root className="empty-state">
121
+ <EmptyState.Title className="empty-state__title">
122
+ No results found
123
+ </EmptyState.Title>
124
+ </EmptyState.Root>
125
+ ```
126
+
127
+ ---
128
+
129
+ [Back to @primitiv-ui/react](../../README.md)
@@ -0,0 +1,32 @@
1
+ import { EmptyState } from "..";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ describe("EmptyState.Actions component", () => {
5
+ it("should render a div containing its children", () => {
6
+ // Arrange
7
+ render(
8
+ <EmptyState.Actions>
9
+ <button>Clear filters</button>
10
+ </EmptyState.Actions>,
11
+ );
12
+
13
+ // Assert
14
+ const button = screen.getByRole("button", { name: "Clear filters" });
15
+ expect(button.parentElement?.tagName).toBe("DIV");
16
+ });
17
+
18
+ it("should render the consumer element with asChild", () => {
19
+ // Arrange
20
+ render(
21
+ <EmptyState.Actions asChild>
22
+ <nav>
23
+ <button>Clear filters</button>
24
+ </nav>
25
+ </EmptyState.Actions>,
26
+ );
27
+
28
+ // Assert
29
+ const button = screen.getByRole("button", { name: "Clear filters" });
30
+ expect(button.parentElement?.tagName).toBe("NAV");
31
+ });
32
+ });
@@ -0,0 +1,30 @@
1
+ import { EmptyState } from "..";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ describe("EmptyState.Description component", () => {
5
+ it("should render a paragraph containing its children", () => {
6
+ // Arrange
7
+ render(
8
+ <EmptyState.Description>
9
+ Try adjusting your filters.
10
+ </EmptyState.Description>,
11
+ );
12
+
13
+ // Assert
14
+ const description = screen.getByText("Try adjusting your filters.");
15
+ expect(description.tagName).toBe("P");
16
+ });
17
+
18
+ it("should render the consumer element with asChild", () => {
19
+ // Arrange
20
+ render(
21
+ <EmptyState.Description asChild>
22
+ <span>Try adjusting your filters.</span>
23
+ </EmptyState.Description>,
24
+ );
25
+
26
+ // Assert
27
+ const description = screen.getByText("Try adjusting your filters.");
28
+ expect(description.tagName).toBe("SPAN");
29
+ });
30
+ });
@@ -0,0 +1,34 @@
1
+ import { EmptyState } from "..";
2
+ import { render } from "@testing-library/react";
3
+
4
+ describe("EmptyState.Media component", () => {
5
+ it("should render a div hidden from assistive technology", () => {
6
+ // Arrange
7
+ const { container } = render(
8
+ <EmptyState.Media>
9
+ <svg />
10
+ </EmptyState.Media>,
11
+ );
12
+
13
+ // Assert
14
+ const media = container.firstChild as HTMLElement;
15
+ expect(media.tagName).toBe("DIV");
16
+ expect(media).toHaveAttribute("aria-hidden", "true");
17
+ });
18
+
19
+ it("should render the consumer element with asChild, keeping it hidden", () => {
20
+ // Arrange
21
+ const { container } = render(
22
+ <EmptyState.Media asChild>
23
+ <span>
24
+ <svg />
25
+ </span>
26
+ </EmptyState.Media>,
27
+ );
28
+
29
+ // Assert
30
+ const media = container.firstChild as HTMLElement;
31
+ expect(media.tagName).toBe("SPAN");
32
+ expect(media).toHaveAttribute("aria-hidden", "true");
33
+ });
34
+ });
@@ -0,0 +1,28 @@
1
+ import { EmptyState } from "..";
2
+ import { render, screen } from "@testing-library/react";
3
+
4
+ describe("EmptyState.Root component", () => {
5
+ it("should render a div with role status containing its children", () => {
6
+ // Arrange
7
+ render(<EmptyState.Root>No projects yet</EmptyState.Root>);
8
+
9
+ // Assert
10
+ const root = screen.getByRole("status");
11
+ expect(root.tagName).toBe("DIV");
12
+ expect(root).toHaveTextContent("No projects yet");
13
+ });
14
+
15
+ it("should render the consumer element with asChild, keeping role status", () => {
16
+ // Arrange
17
+ render(
18
+ <EmptyState.Root asChild>
19
+ <section>No projects yet</section>
20
+ </EmptyState.Root>,
21
+ );
22
+
23
+ // Assert
24
+ const root = screen.getByRole("status");
25
+ expect(root.tagName).toBe("SECTION");
26
+ expect(root).toHaveTextContent("No projects yet");
27
+ });
28
+ });