@astryxdesign/core 0.1.2 → 0.1.3-canary.102854d

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 (586) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +12 -12
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +31 -12
  10. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  11. package/dist/ButtonGroup/ButtonGroup.js +0 -1
  12. package/dist/Calendar/Calendar.d.ts +5 -4
  13. package/dist/Calendar/Calendar.d.ts.map +1 -1
  14. package/dist/Calendar/Calendar.js +124 -61
  15. package/dist/Calendar/dayCellUtils.d.ts +67 -5
  16. package/dist/Calendar/dayCellUtils.d.ts.map +1 -1
  17. package/dist/Calendar/dayCellUtils.js +139 -18
  18. package/dist/Calendar/hooks/index.d.ts +0 -2
  19. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  20. package/dist/Calendar/hooks/index.js +1 -2
  21. package/dist/Calendar/index.d.ts +3 -3
  22. package/dist/Calendar/index.d.ts.map +1 -1
  23. package/dist/Calendar/index.js +1 -1
  24. package/dist/Calendar/styles.d.ts +3 -9
  25. package/dist/Calendar/styles.d.ts.map +1 -1
  26. package/dist/Calendar/styles.js +3 -11
  27. package/dist/Carousel/Carousel.d.ts.map +1 -1
  28. package/dist/Carousel/Carousel.js +17 -3
  29. package/dist/Center/Center.d.ts +11 -1
  30. package/dist/Center/Center.d.ts.map +1 -1
  31. package/dist/Center/Center.js +9 -3
  32. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  33. package/dist/Chat/ChatComposerInput.js +0 -1
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts +8 -1
  38. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  39. package/dist/Chat/useTriggerMenu.js +15 -3
  40. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  43. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  44. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  45. package/dist/CheckboxList/CheckboxList.js +39 -9
  46. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  47. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  48. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  49. package/dist/CheckboxList/CheckboxListItem.js +0 -1
  50. package/dist/Citation/Citation.d.ts.map +1 -1
  51. package/dist/Citation/Citation.js +9 -5
  52. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  53. package/dist/CodeBlock/CodeBlock.js +51 -37
  54. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  55. package/dist/CommandPalette/CommandPalette.js +5 -5
  56. package/dist/ContextMenu/ContextMenu.d.ts +12 -5
  57. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  58. package/dist/ContextMenu/ContextMenu.js +96 -11
  59. package/dist/DateInput/DateInput.d.ts +23 -1
  60. package/dist/DateInput/DateInput.d.ts.map +1 -1
  61. package/dist/DateInput/DateInput.js +167 -99
  62. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  63. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  64. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  65. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  66. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  67. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  68. package/dist/Dialog/Dialog.d.ts.map +1 -1
  69. package/dist/Dialog/Dialog.js +12 -0
  70. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  71. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  72. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  73. package/dist/Field/Field.d.ts +19 -2
  74. package/dist/Field/Field.d.ts.map +1 -1
  75. package/dist/Field/Field.js +4 -0
  76. package/dist/Field/FieldLabel.d.ts +21 -2
  77. package/dist/Field/FieldLabel.d.ts.map +1 -1
  78. package/dist/Field/FieldLabel.js +41 -26
  79. package/dist/FileInput/FileInput.d.ts +22 -1
  80. package/dist/FileInput/FileInput.d.ts.map +1 -1
  81. package/dist/FileInput/FileInput.js +56 -10
  82. package/dist/Grid/Grid.d.ts +11 -1
  83. package/dist/Grid/Grid.d.ts.map +1 -1
  84. package/dist/Grid/Grid.js +32 -6
  85. package/dist/InputGroup/InputGroup.d.ts +4 -3
  86. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  87. package/dist/InputGroup/InputGroup.js +16 -6
  88. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  89. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  90. package/dist/InputGroup/InputGroupContext.js +2 -2
  91. package/dist/InputGroup/groupStyles.d.ts +1 -1
  92. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  93. package/dist/InputGroup/groupStyles.js +3 -3
  94. package/dist/InputGroup/index.d.ts +1 -0
  95. package/dist/InputGroup/index.d.ts.map +1 -1
  96. package/dist/Kbd/Kbd.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.js +33 -1
  98. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  99. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  100. package/dist/Layer/layerAnimations.stylex.js +8 -4
  101. package/dist/Layer/useLayer.d.ts +10 -0
  102. package/dist/Layer/useLayer.d.ts.map +1 -1
  103. package/dist/Layer/useLayer.js +64 -9
  104. package/dist/Layout/Layout.d.ts +4 -11
  105. package/dist/Layout/Layout.d.ts.map +1 -1
  106. package/dist/Layout/Layout.js +10 -11
  107. package/dist/Layout/LayoutFooter.d.ts +2 -2
  108. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  109. package/dist/Layout/LayoutHeader.d.ts +2 -2
  110. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  111. package/dist/Layout/LayoutPanel.d.ts +2 -2
  112. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  113. package/dist/Layout/padding.stylex.d.ts +51 -0
  114. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  115. package/dist/Layout/padding.stylex.js +63 -0
  116. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  117. package/dist/Lightbox/Lightbox.js +4 -2
  118. package/dist/Link/Link.d.ts +16 -1
  119. package/dist/Link/Link.d.ts.map +1 -1
  120. package/dist/Link/Link.js +18 -7
  121. package/dist/Markdown/Markdown.d.ts.map +1 -1
  122. package/dist/Markdown/Markdown.js +7 -0
  123. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  124. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  125. package/dist/MoreMenu/MoreMenu.js +0 -2
  126. package/dist/MultiSelector/MultiSelector.d.ts +27 -2
  127. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  128. package/dist/MultiSelector/MultiSelector.js +141 -42
  129. package/dist/MultiSelector/hooks.d.ts +12 -1
  130. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  131. package/dist/MultiSelector/hooks.js +14 -1
  132. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  133. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  134. package/dist/NumberInput/NumberInput.d.ts +23 -2
  135. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  136. package/dist/NumberInput/NumberInput.js +62 -12
  137. package/dist/Pagination/Pagination.d.ts +1 -1
  138. package/dist/Pagination/Pagination.d.ts.map +1 -1
  139. package/dist/Pagination/Pagination.js +15 -1
  140. package/dist/Popover/Popover.d.ts +18 -2
  141. package/dist/Popover/Popover.d.ts.map +1 -1
  142. package/dist/Popover/Popover.js +5 -2
  143. package/dist/Popover/usePopover.d.ts +38 -3
  144. package/dist/Popover/usePopover.d.ts.map +1 -1
  145. package/dist/Popover/usePopover.js +24 -9
  146. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +6 -1
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +3 -5
  151. package/dist/RadioList/RadioList.d.ts +20 -2
  152. package/dist/RadioList/RadioList.d.ts.map +1 -1
  153. package/dist/RadioList/RadioList.js +105 -9
  154. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  155. package/dist/RadioList/RadioListItem.js +13 -2
  156. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  157. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  159. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  160. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  161. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  162. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  163. package/dist/Selector/Selector.d.ts +24 -1
  164. package/dist/Selector/Selector.d.ts.map +1 -1
  165. package/dist/Selector/Selector.js +122 -43
  166. package/dist/Selector/hooks.d.ts +8 -1
  167. package/dist/Selector/hooks.d.ts.map +1 -1
  168. package/dist/Selector/hooks.js +14 -1
  169. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  170. package/dist/SideNav/SideNavHeading.js +2 -0
  171. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  172. package/dist/SideNav/SideNavItem.js +1 -1
  173. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  174. package/dist/Skeleton/Skeleton.js +8 -2
  175. package/dist/Slider/Slider.d.ts +21 -0
  176. package/dist/Slider/Slider.d.ts.map +1 -1
  177. package/dist/Slider/Slider.js +36 -9
  178. package/dist/Spinner/Spinner.d.ts.map +1 -1
  179. package/dist/Spinner/Spinner.js +1 -1
  180. package/dist/Stack/Stack.d.ts +38 -23
  181. package/dist/Stack/Stack.d.ts.map +1 -1
  182. package/dist/Stack/Stack.js +25 -1
  183. package/dist/Stack/StackItem.d.ts +12 -23
  184. package/dist/Stack/StackItem.d.ts.map +1 -1
  185. package/dist/Stack/StackItem.js +8 -1
  186. package/dist/Switch/Switch.d.ts +23 -2
  187. package/dist/Switch/Switch.d.ts.map +1 -1
  188. package/dist/Switch/Switch.js +41 -11
  189. package/dist/TabList/Tab.d.ts.map +1 -1
  190. package/dist/TabList/Tab.js +6 -0
  191. package/dist/TabList/TabList.d.ts +16 -4
  192. package/dist/TabList/TabList.d.ts.map +1 -1
  193. package/dist/TabList/TabList.js +87 -10
  194. package/dist/TabList/TabListContext.d.ts +5 -0
  195. package/dist/TabList/TabListContext.d.ts.map +1 -1
  196. package/dist/TabList/TabListContext.js +5 -0
  197. package/dist/TabList/TabMenu.d.ts.map +1 -1
  198. package/dist/TabList/TabMenu.js +6 -1
  199. package/dist/Table/BaseTable.d.ts.map +1 -1
  200. package/dist/Table/BaseTable.js +13 -9
  201. package/dist/Table/Table.d.ts.map +1 -1
  202. package/dist/Table/Table.js +10 -1
  203. package/dist/Table/TableCell.d.ts +8 -1
  204. package/dist/Table/TableCell.d.ts.map +1 -1
  205. package/dist/Table/TableCell.js +79 -10
  206. package/dist/Table/TableHeaderCell.d.ts +8 -1
  207. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  208. package/dist/Table/TableHeaderCell.js +16 -16
  209. package/dist/Table/index.d.ts +4 -1
  210. package/dist/Table/index.d.ts.map +1 -1
  211. package/dist/Table/index.js +3 -2
  212. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  213. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  214. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  215. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  216. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  217. package/dist/Table/plugins/rowExpansion/index.d.ts +3 -0
  218. package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -0
  219. package/dist/Table/plugins/rowExpansion/index.js +3 -0
  220. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +90 -0
  221. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -0
  222. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +398 -0
  223. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  225. package/dist/Table/tableContextMenu.d.ts +37 -0
  226. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  227. package/dist/Table/tableContextMenu.js +140 -0
  228. package/dist/Table/types.d.ts +66 -0
  229. package/dist/Table/types.d.ts.map +1 -1
  230. package/dist/TextArea/TextArea.d.ts +22 -1
  231. package/dist/TextArea/TextArea.d.ts.map +1 -1
  232. package/dist/TextArea/TextArea.js +41 -9
  233. package/dist/TextInput/TextInput.d.ts +23 -2
  234. package/dist/TextInput/TextInput.d.ts.map +1 -1
  235. package/dist/TextInput/TextInput.js +55 -10
  236. package/dist/TimeInput/TimeInput.d.ts +23 -1
  237. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  238. package/dist/TimeInput/TimeInput.js +147 -76
  239. package/dist/Toast/Toast.d.ts.map +1 -1
  240. package/dist/Toast/Toast.js +14 -0
  241. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  242. package/dist/Toast/ToastViewport.js +131 -3
  243. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  244. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  245. package/dist/Tokenizer/Tokenizer.js +26 -4
  246. package/dist/Toolbar/Toolbar.d.ts +3 -3
  247. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  248. package/dist/Toolbar/Toolbar.js +51 -7
  249. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  250. package/dist/Tooltip/useTooltip.js +56 -9
  251. package/dist/TopNav/TopNavHeading.d.ts +7 -1
  252. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  253. package/dist/TopNav/TopNavHeading.js +8 -0
  254. package/dist/TreeList/TreeList.d.ts +1 -1
  255. package/dist/TreeList/TreeList.d.ts.map +1 -1
  256. package/dist/TreeList/TreeList.js +72 -2
  257. package/dist/TreeList/TreeListItem.d.ts +11 -1
  258. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  259. package/dist/TreeList/TreeListItem.js +51 -12
  260. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  261. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  262. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  263. package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
  264. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  265. package/dist/Typeahead/BaseTypeahead.js +69 -6
  266. package/dist/Typeahead/Typeahead.d.ts +26 -3
  267. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  268. package/dist/Typeahead/Typeahead.js +65 -29
  269. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  270. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  271. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  272. package/dist/VisuallyHidden/index.d.ts +11 -0
  273. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  274. package/dist/VisuallyHidden/index.js +13 -0
  275. package/dist/astryx.css +22 -0
  276. package/dist/astryx.umd.js +55 -55
  277. package/dist/astryx.umd.js.map +4 -4
  278. package/dist/hooks/focusableSelector.d.ts +22 -0
  279. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  280. package/dist/hooks/focusableSelector.js +24 -0
  281. package/dist/hooks/index.d.ts +12 -2
  282. package/dist/hooks/index.d.ts.map +1 -1
  283. package/dist/hooks/index.js +7 -2
  284. package/dist/hooks/useAnnounce.d.ts +40 -0
  285. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  286. package/dist/hooks/useAnnounce.js +142 -0
  287. package/dist/hooks/useClickableContainer.d.ts +16 -1
  288. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  289. package/dist/hooks/useClickableContainer.js +16 -4
  290. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  291. package/dist/hooks/useEntryAnimation.js +4 -4
  292. package/dist/hooks/useFocusTrap.d.ts +18 -0
  293. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  294. package/dist/hooks/useFocusTrap.js +87 -3
  295. package/dist/hooks/useGridFocus.d.ts +88 -1
  296. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  297. package/dist/hooks/useGridFocus.js +261 -62
  298. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  299. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  300. package/dist/hooks/useKeyboardHint.js +220 -0
  301. package/dist/hooks/useListFocus.d.ts +78 -7
  302. package/dist/hooks/useListFocus.d.ts.map +1 -1
  303. package/dist/hooks/useListFocus.js +305 -52
  304. package/dist/hooks/useLongPress.d.ts +21 -0
  305. package/dist/hooks/useLongPress.d.ts.map +1 -0
  306. package/dist/hooks/useLongPress.js +89 -0
  307. package/dist/hooks/useTreeFocus.d.ts +157 -0
  308. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  309. package/dist/hooks/useTreeFocus.js +337 -0
  310. package/dist/hooks/useTypeahead.d.ts +63 -0
  311. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  312. package/dist/hooks/useTypeahead.js +117 -0
  313. package/dist/index.d.ts +1 -0
  314. package/dist/index.d.ts.map +1 -1
  315. package/dist/index.js +1 -0
  316. package/dist/utils/dateTypes.d.ts +12 -0
  317. package/dist/utils/dateTypes.d.ts.map +1 -1
  318. package/dist/utils/dateTypes.js +41 -1
  319. package/dist/utils/index.d.ts +4 -0
  320. package/dist/utils/index.d.ts.map +1 -1
  321. package/dist/utils/index.js +3 -1
  322. package/dist/utils/inputAria.d.ts +29 -0
  323. package/dist/utils/inputAria.d.ts.map +1 -0
  324. package/dist/utils/inputAria.js +30 -0
  325. package/dist/utils/timeParser.d.ts.map +1 -1
  326. package/dist/utils/timeParser.js +5 -2
  327. package/docs.mjs +2 -2
  328. package/package.json +12 -8
  329. package/src/Avatar/Avatar.doc.mjs +2 -2
  330. package/src/Avatar/Avatar.test.tsx +37 -0
  331. package/src/Avatar/Avatar.tsx +10 -5
  332. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  333. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  334. package/src/Button/Button.tsx +42 -22
  335. package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
  336. package/src/ButtonGroup/ButtonGroup.tsx +0 -1
  337. package/src/Calendar/Calendar.doc.mjs +4 -4
  338. package/src/Calendar/Calendar.test.tsx +247 -30
  339. package/src/Calendar/Calendar.tsx +173 -86
  340. package/src/Calendar/dayCellUtils.test.ts +306 -1
  341. package/src/Calendar/dayCellUtils.ts +206 -13
  342. package/src/Calendar/hooks/index.ts +0 -6
  343. package/src/Calendar/index.ts +1 -3
  344. package/src/Calendar/styles.ts +4 -9
  345. package/src/Carousel/Carousel.doc.mjs +7 -1
  346. package/src/Carousel/Carousel.test.tsx +20 -0
  347. package/src/Carousel/Carousel.tsx +16 -1
  348. package/src/Center/Center.doc.mjs +12 -2
  349. package/src/Center/Center.tsx +28 -3
  350. package/src/Chat/ChatComposerInput.test.tsx +26 -7
  351. package/src/Chat/ChatComposerInput.tsx +0 -1
  352. package/src/Chat/ChatDictationButton.doc.mjs +1 -1
  353. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  354. package/src/Chat/ChatMessageList.test.tsx +18 -0
  355. package/src/Chat/ChatMessageList.tsx +16 -0
  356. package/src/Chat/useTriggerMenu.tsx +22 -4
  357. package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
  358. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  359. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  360. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  361. package/src/CheckboxList/CheckboxList.test.tsx +192 -26
  362. package/src/CheckboxList/CheckboxList.tsx +56 -3
  363. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  364. package/src/CheckboxList/CheckboxListItem.tsx +0 -3
  365. package/src/Citation/Citation.doc.mjs +10 -0
  366. package/src/Citation/Citation.test.tsx +121 -0
  367. package/src/Citation/Citation.tsx +15 -3
  368. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  369. package/src/CodeBlock/CodeBlock.tsx +63 -33
  370. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  371. package/src/CommandPalette/CommandPalette.tsx +9 -21
  372. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  373. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  374. package/src/ContextMenu/ContextMenu.tsx +129 -16
  375. package/src/DateInput/DateInput.doc.mjs +291 -56
  376. package/src/DateInput/DateInput.test.tsx +248 -2
  377. package/src/DateInput/DateInput.tsx +196 -100
  378. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  379. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  380. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  381. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  382. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  383. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  384. package/src/Dialog/Dialog.doc.mjs +2 -2
  385. package/src/Dialog/Dialog.tsx +12 -0
  386. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  387. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  388. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  389. package/src/Field/Field.doc.mjs +59 -18
  390. package/src/Field/Field.test.tsx +32 -0
  391. package/src/Field/Field.tsx +23 -5
  392. package/src/Field/FieldLabel.tsx +55 -17
  393. package/src/FileInput/FileInput.doc.mjs +17 -0
  394. package/src/FileInput/FileInput.test.tsx +283 -49
  395. package/src/FileInput/FileInput.tsx +76 -4
  396. package/src/Grid/Grid.doc.mjs +17 -4
  397. package/src/Grid/Grid.test.tsx +43 -30
  398. package/src/Grid/Grid.tsx +38 -3
  399. package/src/Icon/Icon.doc.mjs +3 -3
  400. package/src/InputGroup/InputGroup.doc.mjs +111 -23
  401. package/src/InputGroup/InputGroup.test.tsx +257 -60
  402. package/src/InputGroup/InputGroup.tsx +23 -5
  403. package/src/InputGroup/InputGroupContext.ts +9 -4
  404. package/src/InputGroup/groupStyles.ts +7 -1
  405. package/src/InputGroup/index.ts +1 -0
  406. package/src/Kbd/Kbd.test.tsx +22 -4
  407. package/src/Kbd/Kbd.tsx +35 -3
  408. package/src/Layer/layerAnimations.stylex.ts +20 -4
  409. package/src/Layer/useLayer.doc.mjs +5 -0
  410. package/src/Layer/useLayer.test.tsx +111 -0
  411. package/src/Layer/useLayer.tsx +90 -9
  412. package/src/Layout/Layout.tsx +8 -16
  413. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  414. package/src/Layout/LayoutFooter.tsx +3 -3
  415. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  416. package/src/Layout/LayoutHeader.tsx +3 -3
  417. package/src/Layout/LayoutPanel.tsx +3 -3
  418. package/src/Layout/__tests__/edgeCompensation.test.tsx +72 -0
  419. package/src/Layout/padding.stylex.ts +52 -0
  420. package/src/Lightbox/Lightbox.doc.mjs +9 -0
  421. package/src/Lightbox/Lightbox.test.tsx +53 -5
  422. package/src/Lightbox/Lightbox.tsx +9 -4
  423. package/src/Link/Link.doc.mjs +14 -0
  424. package/src/Link/Link.test.tsx +50 -0
  425. package/src/Link/Link.tsx +39 -4
  426. package/src/Markdown/Markdown.test.tsx +11 -0
  427. package/src/Markdown/Markdown.tsx +7 -0
  428. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  429. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  430. package/src/MoreMenu/MoreMenu.tsx +0 -9
  431. package/src/MultiSelector/MultiSelector.doc.mjs +35 -0
  432. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  433. package/src/MultiSelector/MultiSelector.tsx +194 -48
  434. package/src/MultiSelector/hooks.ts +27 -0
  435. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  436. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  437. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  438. package/src/NumberInput/NumberInput.test.tsx +242 -40
  439. package/src/NumberInput/NumberInput.tsx +89 -11
  440. package/src/Pagination/Pagination.doc.mjs +3 -3
  441. package/src/Pagination/Pagination.test.tsx +117 -5
  442. package/src/Pagination/Pagination.tsx +21 -1
  443. package/src/Popover/Popover.doc.mjs +26 -0
  444. package/src/Popover/Popover.test.tsx +121 -0
  445. package/src/Popover/Popover.tsx +23 -2
  446. package/src/Popover/usePopover.doc.mjs +9 -3
  447. package/src/Popover/usePopover.tsx +70 -12
  448. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  449. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  450. package/src/PowerSearch/PowerSearch.tsx +16 -3
  451. package/src/ProgressBar/ProgressBar.test.tsx +34 -26
  452. package/src/ProgressBar/ProgressBar.tsx +3 -4
  453. package/src/RadioList/RadioList.doc.mjs +9 -0
  454. package/src/RadioList/RadioList.test.tsx +252 -16
  455. package/src/RadioList/RadioList.tsx +166 -10
  456. package/src/RadioList/RadioListItem.tsx +14 -2
  457. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  458. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  459. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  460. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  461. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  462. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  463. package/src/Selector/Selector.doc.mjs +31 -0
  464. package/src/Selector/Selector.test.tsx +387 -15
  465. package/src/Selector/Selector.tsx +175 -67
  466. package/src/Selector/hooks.ts +23 -0
  467. package/src/SideNav/SideNav.test.tsx +48 -0
  468. package/src/SideNav/SideNavHeading.tsx +8 -2
  469. package/src/SideNav/SideNavItem.tsx +2 -4
  470. package/src/Skeleton/Skeleton.test.tsx +25 -0
  471. package/src/Skeleton/Skeleton.tsx +11 -1
  472. package/src/Slider/Slider.doc.mjs +15 -0
  473. package/src/Slider/Slider.test.tsx +155 -2
  474. package/src/Slider/Slider.tsx +53 -5
  475. package/src/Spinner/Spinner.tsx +7 -1
  476. package/src/Stack/Stack.doc.mjs +77 -0
  477. package/src/Stack/Stack.test.tsx +55 -5
  478. package/src/Stack/Stack.tsx +73 -24
  479. package/src/Stack/StackItem.test.tsx +23 -0
  480. package/src/Stack/StackItem.tsx +20 -23
  481. package/src/Switch/Switch.doc.mjs +15 -0
  482. package/src/Switch/Switch.test.tsx +162 -27
  483. package/src/Switch/Switch.tsx +61 -21
  484. package/src/TabList/Tab.tsx +6 -0
  485. package/src/TabList/TabList.doc.mjs +9 -3
  486. package/src/TabList/TabList.test.tsx +251 -0
  487. package/src/TabList/TabList.tsx +103 -12
  488. package/src/TabList/TabListContext.ts +7 -3
  489. package/src/TabList/TabMenu.tsx +6 -3
  490. package/src/Table/BaseTable.tsx +24 -18
  491. package/src/Table/Table.doc.mjs +2 -0
  492. package/src/Table/Table.test.tsx +10 -0
  493. package/src/Table/Table.tsx +8 -0
  494. package/src/Table/TableCell.tsx +105 -24
  495. package/src/Table/TableHeaderCell.tsx +28 -29
  496. package/src/Table/index.ts +8 -2
  497. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  498. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  499. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  500. package/src/Table/plugins/rowExpansion/index.ts +7 -0
  501. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +155 -0
  502. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +590 -0
  503. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  504. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  505. package/src/Table/tableContextMenu.test.tsx +181 -0
  506. package/src/Table/tableContextMenu.tsx +160 -0
  507. package/src/Table/types.ts +74 -0
  508. package/src/Table/useTableColumnResize.doc.mjs +3 -3
  509. package/src/Table/useTablePagination.doc.mjs +4 -1
  510. package/src/Table/useTableRowExpansion.doc.mjs +63 -0
  511. package/src/Table/useTableSortable.doc.mjs +4 -1
  512. package/src/Table/useTableStickyColumns.doc.mjs +2 -2
  513. package/src/Text/Text.doc.mjs +18 -18
  514. package/src/TextArea/TextArea.doc.mjs +17 -0
  515. package/src/TextArea/TextArea.test.tsx +167 -35
  516. package/src/TextArea/TextArea.tsx +62 -15
  517. package/src/TextInput/TextInput.doc.mjs +17 -0
  518. package/src/TextInput/TextInput.test.tsx +168 -34
  519. package/src/TextInput/TextInput.tsx +77 -9
  520. package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
  521. package/src/TimeInput/TimeInput.doc.mjs +345 -60
  522. package/src/TimeInput/TimeInput.test.tsx +255 -7
  523. package/src/TimeInput/TimeInput.tsx +176 -70
  524. package/src/Timestamp/Timestamp.doc.mjs +19 -19
  525. package/src/Toast/Toast.tsx +14 -0
  526. package/src/Toast/ToastViewport.test.tsx +214 -0
  527. package/src/Toast/ToastViewport.tsx +167 -21
  528. package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
  529. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  530. package/src/Tokenizer/Tokenizer.tsx +37 -1
  531. package/src/Toolbar/Toolbar.test.tsx +109 -6
  532. package/src/Toolbar/Toolbar.tsx +59 -5
  533. package/src/Tooltip/Tooltip.test.tsx +68 -0
  534. package/src/Tooltip/useTooltip.tsx +55 -8
  535. package/src/TopNav/TopNav.test.tsx +50 -0
  536. package/src/TopNav/TopNavHeading.tsx +17 -1
  537. package/src/TreeList/TreeList.test.tsx +295 -6
  538. package/src/TreeList/TreeList.tsx +73 -1
  539. package/src/TreeList/TreeListItem.tsx +51 -5
  540. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  541. package/src/Typeahead/BaseTypeahead.tsx +93 -9
  542. package/src/Typeahead/Typeahead.doc.mjs +146 -30
  543. package/src/Typeahead/Typeahead.test.tsx +331 -1
  544. package/src/Typeahead/Typeahead.tsx +105 -33
  545. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  546. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  547. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  548. package/src/VisuallyHidden/index.ts +15 -0
  549. package/src/docs-script.test.mjs +50 -0
  550. package/src/hooks/focusableSelector.ts +25 -0
  551. package/src/hooks/index.ts +28 -2
  552. package/src/hooks/useAnnounce.test.tsx +101 -0
  553. package/src/hooks/useAnnounce.ts +168 -0
  554. package/src/hooks/useClickableContainer.ts +16 -4
  555. package/src/hooks/useEntryAnimation.ts +16 -4
  556. package/src/hooks/useFocusTrap.test.tsx +132 -0
  557. package/src/hooks/useFocusTrap.ts +98 -4
  558. package/src/hooks/useGridFocus.doc.mjs +40 -2
  559. package/src/hooks/useGridFocus.test.tsx +132 -0
  560. package/src/hooks/useGridFocus.ts +343 -67
  561. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  562. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  563. package/src/hooks/useKeyboardHint.tsx +332 -0
  564. package/src/hooks/useListFocus.doc.mjs +50 -10
  565. package/src/hooks/useListFocus.test.tsx +375 -0
  566. package/src/hooks/useListFocus.ts +395 -57
  567. package/src/hooks/useLongPress.test.tsx +134 -0
  568. package/src/hooks/useLongPress.ts +119 -0
  569. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  570. package/src/hooks/useTreeFocus.test.tsx +214 -0
  571. package/src/hooks/useTreeFocus.ts +559 -0
  572. package/src/hooks/useTypeahead.test.tsx +109 -0
  573. package/src/hooks/useTypeahead.ts +183 -0
  574. package/src/index.ts +1 -0
  575. package/src/theme/derivedVarRegistry.test.ts +10 -5
  576. package/src/utils/dateTypes.test.ts +40 -0
  577. package/src/utils/dateTypes.ts +40 -0
  578. package/src/utils/index.ts +9 -0
  579. package/src/utils/inputAria.test.ts +39 -0
  580. package/src/utils/inputAria.ts +58 -0
  581. package/src/utils/timeParser.test.ts +10 -0
  582. package/src/utils/timeParser.ts +5 -2
  583. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  584. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  585. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  586. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -0,0 +1,590 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTableRowExpansion.tsx
7
+ * @input React, StyleX, Icon, Table types
8
+ * @output Exports useTableRowExpansion hook + config/state types
9
+ * @position Row-expansion plugin; consumed by Table via plugins prop
10
+ *
11
+ * SYNC: When modified, update these files to stay in sync:
12
+ * - /packages/core/src/Table/index.ts (exports)
13
+ */
14
+
15
+ import {useCallback, useMemo, useRef, type ReactNode} from 'react';
16
+ import * as stylex from '@stylexjs/stylex';
17
+ import {spacingVars, colorVars, radiusVars} from '../../../theme/tokens.stylex';
18
+ import {Icon} from '../../../Icon';
19
+ import {resolveContextActions} from '../../tableContextMenu';
20
+ import type {
21
+ TablePlugin,
22
+ TableColumn,
23
+ BodyCellRenderProps,
24
+ BodyRowRenderProps,
25
+ HeaderCellRenderProps,
26
+ } from '../../types';
27
+
28
+ // =============================================================================
29
+ // Config
30
+ // =============================================================================
31
+
32
+ /**
33
+ * Configuration for useTableRowExpansion (inherited-columns mode).
34
+ *
35
+ * Child rows use the same columns as their parents, with indentation on the
36
+ * first content column. The consumer provides a **flat** data array (use
37
+ * {@link useTableRowExpansionState} to flatten a tree) and a `getDepth`
38
+ * function so the plugin knows each row's nesting level.
39
+ */
40
+ export interface UseTableRowExpansionConfig<T extends Record<string, unknown>> {
41
+ /** Set of currently-expanded row keys. */
42
+ expandedKeys: Set<string>;
43
+ /** Called when a row's expansion is toggled. */
44
+ onToggle: (key: string) => void;
45
+ /** Derive a stable unique key from a row item. */
46
+ getRowKey: (item: T) => string;
47
+ /** Return the children of a row (used to determine expandability). */
48
+ getChildren: (item: T) => T[];
49
+ /** Return the depth of a row in the hierarchy (0 = top-level). */
50
+ getDepth?: (item: T) => number;
51
+ /** Optionally control which rows are expandable. @default checks getChildren length */
52
+ getIsItemExpandable?: (item: T) => boolean;
53
+ /**
54
+ * When true, clicking anywhere on the row toggles expansion (in addition to
55
+ * the chevron button). @default false — only the chevron triggers expansion.
56
+ */
57
+ hasRowClickExpansion?: boolean;
58
+ /**
59
+ * State of the expand-all toggle in the header. `true` = all expanded,
60
+ * `false` = all collapsed, `'indeterminate'` = mixed. When provided
61
+ * (together with `onToggleExpandAll`), the header cell shows a toggle button.
62
+ */
63
+ isAllExpanded?: boolean | 'indeterminate';
64
+ /** Called when the expand-all header toggle is clicked. */
65
+ onToggleExpandAll?: (expand: boolean) => void;
66
+ }
67
+
68
+ /**
69
+ * Configuration for {@link useTableRowExpansionState}.
70
+ *
71
+ * Mirrors the shape of {@link useTableSelectionState}: you own the
72
+ * `expandedKeys` set (via `useState`), and the hook derives everything the
73
+ * plugin needs — the flattened `data`, per-row depth, expand/collapse
74
+ * handlers, and the expand-all toggle state.
75
+ */
76
+ export interface UseTableRowExpansionStateConfig<
77
+ T extends Record<string, unknown>,
78
+ > {
79
+ /** The full, un-flattened tree. */
80
+ baseData: T[];
81
+ /** Return the children of a row. Leaf rows return an empty array. */
82
+ getChildren: (item: T) => T[];
83
+ /** Derive a stable unique key from a row item. */
84
+ getRowKey: (item: T) => string;
85
+ /**
86
+ * Should this row be expandable? Rows that return `false` never show a
87
+ * chevron and are skipped by expand-all. @default rows with children
88
+ */
89
+ getIsItemExpandable?: (item: T) => boolean;
90
+ /** Controlled set of currently-expanded row keys. */
91
+ expandedKeys: Set<string>;
92
+ /** Setter for the controlled expanded keys. */
93
+ setExpandedKeys: React.Dispatch<React.SetStateAction<Set<string>>>;
94
+ }
95
+
96
+ export interface UseTableRowExpansionStateResult<
97
+ T extends Record<string, unknown>,
98
+ > {
99
+ /** The flattened, currently-visible rows. Pass to `<Table data>`. */
100
+ data: T[];
101
+ /** Ready-to-use config for {@link useTableRowExpansion}. */
102
+ expansionConfig: UseTableRowExpansionConfig<T>;
103
+ }
104
+
105
+ /**
106
+ * Manages row-expansion state and derives the config for
107
+ * {@link useTableRowExpansion}. This is the recommended entry point — it
108
+ * removes the boilerplate of flattening the tree, tracking depth, and
109
+ * computing the expand-all state.
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * const [expandedKeys, setExpandedKeys] = useState<Set<string>>(new Set());
114
+ * const {data, expansionConfig} = useTableRowExpansionState({
115
+ * baseData: tree,
116
+ * getChildren: item => item.children ?? [],
117
+ * getRowKey: item => item.id,
118
+ * expandedKeys,
119
+ * setExpandedKeys,
120
+ * });
121
+ * const expansion = useTableRowExpansion(expansionConfig);
122
+ * <Table data={data} columns={columns} idKey="id" plugins={{expansion}} />;
123
+ * ```
124
+ */
125
+ export function useTableRowExpansionState<T extends Record<string, unknown>>({
126
+ baseData,
127
+ getChildren,
128
+ getRowKey,
129
+ getIsItemExpandable,
130
+ expandedKeys,
131
+ setExpandedKeys,
132
+ }: UseTableRowExpansionStateConfig<T>): UseTableRowExpansionStateResult<T> {
133
+ const isExpandable = useCallback(
134
+ (item: T): boolean =>
135
+ getIsItemExpandable
136
+ ? getIsItemExpandable(item)
137
+ : getChildren(item).length > 0,
138
+ [getIsItemExpandable, getChildren],
139
+ );
140
+
141
+ const depthMap = useMemo(() => {
142
+ const map = new Map<string, number>();
143
+ function walk(items: T[], depth: number) {
144
+ for (const item of items) {
145
+ const key = getRowKey(item);
146
+ map.set(key, depth);
147
+ if (expandedKeys.has(key)) {
148
+ walk(getChildren(item), depth + 1);
149
+ }
150
+ }
151
+ }
152
+ walk(baseData, 0);
153
+ return map;
154
+ }, [baseData, getChildren, getRowKey, expandedKeys]);
155
+
156
+ const data = useMemo(() => {
157
+ const result: T[] = [];
158
+ function walk(items: T[]) {
159
+ for (const item of items) {
160
+ result.push(item);
161
+ const key = getRowKey(item);
162
+ if (expandedKeys.has(key)) {
163
+ walk(getChildren(item));
164
+ }
165
+ }
166
+ }
167
+ walk(baseData);
168
+ return result;
169
+ }, [baseData, getChildren, getRowKey, expandedKeys]);
170
+
171
+ // Every expandable key across the whole tree (drives expand-all).
172
+ const allExpandableKeys = useMemo(() => {
173
+ const keys: string[] = [];
174
+ function walk(items: T[]) {
175
+ for (const item of items) {
176
+ if (isExpandable(item)) {
177
+ keys.push(getRowKey(item));
178
+ walk(getChildren(item));
179
+ }
180
+ }
181
+ }
182
+ walk(baseData);
183
+ return keys;
184
+ }, [baseData, getChildren, getRowKey, isExpandable]);
185
+
186
+ const getDepth = useCallback(
187
+ (item: T) => depthMap.get(getRowKey(item)) ?? 0,
188
+ [depthMap, getRowKey],
189
+ );
190
+
191
+ const onToggle = useCallback(
192
+ (key: string) => {
193
+ setExpandedKeys(prev => {
194
+ const next = new Set(prev);
195
+ if (next.has(key)) {
196
+ next.delete(key);
197
+ } else {
198
+ next.add(key);
199
+ }
200
+ return next;
201
+ });
202
+ },
203
+ [setExpandedKeys],
204
+ );
205
+
206
+ const isAllExpanded: boolean | 'indeterminate' = useMemo(() => {
207
+ if (allExpandableKeys.length === 0) {
208
+ return false;
209
+ }
210
+ const expandedCount = allExpandableKeys.filter(k =>
211
+ expandedKeys.has(k),
212
+ ).length;
213
+ if (expandedCount === 0) {
214
+ return false;
215
+ }
216
+ if (expandedCount === allExpandableKeys.length) {
217
+ return true;
218
+ }
219
+ return 'indeterminate';
220
+ }, [allExpandableKeys, expandedKeys]);
221
+
222
+ const onToggleExpandAll = useCallback(
223
+ (expand: boolean) => {
224
+ setExpandedKeys(expand ? new Set(allExpandableKeys) : new Set());
225
+ },
226
+ [setExpandedKeys, allExpandableKeys],
227
+ );
228
+
229
+ const expansionConfig = useMemo(
230
+ (): UseTableRowExpansionConfig<T> => ({
231
+ expandedKeys,
232
+ onToggle,
233
+ getRowKey,
234
+ getChildren,
235
+ getDepth,
236
+ getIsItemExpandable,
237
+ isAllExpanded,
238
+ onToggleExpandAll,
239
+ }),
240
+ [
241
+ expandedKeys,
242
+ onToggle,
243
+ getRowKey,
244
+ getChildren,
245
+ getDepth,
246
+ getIsItemExpandable,
247
+ isAllExpanded,
248
+ onToggleExpandAll,
249
+ ],
250
+ );
251
+
252
+ return {data, expansionConfig};
253
+ }
254
+
255
+ // =============================================================================
256
+ // Styles
257
+ // =============================================================================
258
+
259
+ const EXPANSION_COLUMN_WIDTH = {type: 'pixel' as const, value: 40};
260
+
261
+ /** Indentation applied per depth level, in pixels. */
262
+ const INDENT_PER_DEPTH = 24;
263
+
264
+ const expansionStyles = stylex.create({
265
+ chevronButton: {
266
+ display: 'inline-flex',
267
+ alignItems: 'center',
268
+ justifyContent: 'center',
269
+ width: '24px',
270
+ height: '24px',
271
+ background: 'transparent',
272
+ border: 'none',
273
+ borderRadius: radiusVars['--radius-inner'],
274
+ cursor: 'pointer',
275
+ color: colorVars['--color-icon-secondary'],
276
+ transitionProperty: 'transform, color, background-color',
277
+ transitionDuration: '150ms',
278
+ padding: 0,
279
+ flexShrink: '0',
280
+ // Match IconButton ghost hover: subtle overlay background
281
+ backgroundImage: {
282
+ default: null,
283
+ ':hover': {
284
+ '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
285
+ },
286
+ },
287
+ ':hover': {
288
+ color: colorVars['--color-icon-primary'],
289
+ },
290
+ },
291
+ chevronExpanded: {
292
+ transform: 'rotate(90deg)',
293
+ },
294
+ chevronIcon: {
295
+ display: 'inline-flex',
296
+ transitionProperty: 'transform',
297
+ transitionDuration: '150ms',
298
+ },
299
+ indentedCell: {
300
+ display: 'flex',
301
+ alignItems: 'center',
302
+ gap: spacingVars['--spacing-1'],
303
+ },
304
+ indent: (px: number) => ({
305
+ paddingInlineStart: `${px}px`,
306
+ }),
307
+ placeholder: {
308
+ display: 'inline-block',
309
+ width: '24px',
310
+ height: '24px',
311
+ flexShrink: '0',
312
+ },
313
+ clickableRow: {
314
+ cursor: 'pointer',
315
+ },
316
+ });
317
+
318
+ // =============================================================================
319
+ // Chevron
320
+ // =============================================================================
321
+
322
+ function ExpansionChevron({
323
+ isExpanded,
324
+ onToggle,
325
+ ariaLabel,
326
+ }: {
327
+ isExpanded: boolean;
328
+ onToggle: () => void;
329
+ ariaLabel: string;
330
+ }) {
331
+ return (
332
+ <button
333
+ type="button"
334
+ {...stylex.props(expansionStyles.chevronButton)}
335
+ onClick={e => {
336
+ e.stopPropagation();
337
+ onToggle();
338
+ }}
339
+ aria-label={ariaLabel}
340
+ aria-expanded={isExpanded}>
341
+ <span
342
+ {...stylex.props(
343
+ expansionStyles.chevronIcon,
344
+ isExpanded && expansionStyles.chevronExpanded,
345
+ )}>
346
+ <Icon icon="chevronRight" size="xsm" />
347
+ </span>
348
+ </button>
349
+ );
350
+ }
351
+
352
+ // =============================================================================
353
+ // Hook
354
+ // =============================================================================
355
+
356
+ export function useTableRowExpansion<T extends Record<string, unknown>>(
357
+ config: UseTableRowExpansionConfig<T>,
358
+ ): TablePlugin<T> {
359
+ const {
360
+ expandedKeys,
361
+ onToggle,
362
+ getRowKey,
363
+ getChildren,
364
+ getDepth,
365
+ getIsItemExpandable,
366
+ hasRowClickExpansion = false,
367
+ isAllExpanded,
368
+ onToggleExpandAll,
369
+ } = config;
370
+
371
+ // Track the first non-plugin column key for indentation.
372
+ const firstUserColumnKeyRef = useRef<string | null>(null);
373
+
374
+ const expansionColumn = useMemo(
375
+ (): TableColumn<T> => ({
376
+ key: '__expansion',
377
+ header: '',
378
+ width: EXPANSION_COLUMN_WIDTH,
379
+ resizable: false,
380
+ renderCell: (item: T) => {
381
+ // Child rows (depth > 0) show their chevron inline in the first user
382
+ // column instead — don't double up here.
383
+ const depth = getDepth ? getDepth(item) : 0;
384
+ if (depth > 0) {
385
+ return null;
386
+ }
387
+
388
+ const key = getRowKey(item);
389
+ const expandable = getIsItemExpandable
390
+ ? getIsItemExpandable(item)
391
+ : getChildren(item).length > 0;
392
+ if (!expandable) {
393
+ return null;
394
+ }
395
+ const isExpanded = expandedKeys.has(key);
396
+ return (
397
+ <ExpansionChevron
398
+ isExpanded={isExpanded}
399
+ onToggle={() => onToggle(key)}
400
+ ariaLabel={isExpanded ? 'Collapse row' : 'Expand row'}
401
+ />
402
+ );
403
+ },
404
+ }),
405
+ [
406
+ expandedKeys,
407
+ onToggle,
408
+ getRowKey,
409
+ getChildren,
410
+ getIsItemExpandable,
411
+ getDepth,
412
+ ],
413
+ );
414
+
415
+ return useMemo(
416
+ (): TablePlugin<T> => ({
417
+ transformColumns(columns: TableColumn<T>[]) {
418
+ // Track the first user column for indentation.
419
+ const firstUserCol = columns.find(c => !c.key.startsWith('__'));
420
+ firstUserColumnKeyRef.current = firstUserCol?.key ?? null;
421
+
422
+ // Wrap the first user column's renderCell to add depth indentation +
423
+ // an inline chevron for child rows. This is the inherited-columns
424
+ // pattern: child rows use the same columns but indent their first
425
+ // content cell.
426
+ const wrappedColumns = columns.map(col => {
427
+ if (col.key !== firstUserColumnKeyRef.current) {
428
+ return col;
429
+ }
430
+ const originalRenderCell = col.renderCell;
431
+ return {
432
+ ...col,
433
+ renderCell: (item: T): ReactNode => {
434
+ const depth = getDepth ? getDepth(item) : 0;
435
+ const originalContent = originalRenderCell
436
+ ? originalRenderCell(item)
437
+ : String(
438
+ ((item as Record<string, unknown>)[col.key] as
439
+ | string
440
+ | number
441
+ | null
442
+ | undefined) ?? '',
443
+ );
444
+
445
+ if (depth === 0) {
446
+ return originalContent;
447
+ }
448
+
449
+ const indent = (depth - 1) * INDENT_PER_DEPTH;
450
+ const key = getRowKey(item);
451
+ const isExpanded = expandedKeys.has(key);
452
+ const expandable = getIsItemExpandable
453
+ ? getIsItemExpandable(item)
454
+ : getChildren(item).length > 0;
455
+
456
+ const chevron = expandable ? (
457
+ <ExpansionChevron
458
+ isExpanded={isExpanded}
459
+ onToggle={() => onToggle(key)}
460
+ ariaLabel={isExpanded ? 'Collapse row' : 'Expand row'}
461
+ />
462
+ ) : (
463
+ <span {...stylex.props(expansionStyles.placeholder)} />
464
+ );
465
+
466
+ return (
467
+ <div
468
+ {...stylex.props(
469
+ expansionStyles.indentedCell,
470
+ indent > 0 && expansionStyles.indent(indent),
471
+ )}>
472
+ {chevron}
473
+ {originalContent}
474
+ </div>
475
+ );
476
+ },
477
+ };
478
+ });
479
+
480
+ return [expansionColumn, ...wrappedColumns];
481
+ },
482
+
483
+ transformHeaderCell(
484
+ props: HeaderCellRenderProps,
485
+ column: TableColumn<T>,
486
+ ): HeaderCellRenderProps {
487
+ if (column.key !== '__expansion') {
488
+ return props;
489
+ }
490
+
491
+ // Show expand-all toggle when the consumer provides the state + callback.
492
+ if (isAllExpanded !== undefined && onToggleExpandAll) {
493
+ const allExpanded = isAllExpanded === true;
494
+ return {
495
+ ...props,
496
+ content: (
497
+ <button
498
+ type="button"
499
+ {...stylex.props(expansionStyles.chevronButton)}
500
+ onClick={() => onToggleExpandAll(!allExpanded)}
501
+ aria-label={
502
+ allExpanded ? 'Collapse all rows' : 'Expand all rows'
503
+ }>
504
+ <span
505
+ {...stylex.props(
506
+ expansionStyles.chevronIcon,
507
+ allExpanded && expansionStyles.chevronExpanded,
508
+ )}>
509
+ <Icon icon="chevronRight" size="xsm" />
510
+ </span>
511
+ </button>
512
+ ),
513
+ };
514
+ }
515
+
516
+ return {...props, content: null};
517
+ },
518
+
519
+ transformBodyCell(
520
+ props: BodyCellRenderProps,
521
+ column: TableColumn<T>,
522
+ item: T,
523
+ ): BodyCellRenderProps {
524
+ // Contribute "Expand/Collapse row" context-menu action on every cell
525
+ // so right-clicking anywhere in the row shows the option.
526
+ const expandable = getIsItemExpandable
527
+ ? getIsItemExpandable(item)
528
+ : getChildren(item).length > 0;
529
+ if (!expandable) {
530
+ return props;
531
+ }
532
+
533
+ const key = getRowKey(item);
534
+ const isExpanded = expandedKeys.has(key);
535
+ return {
536
+ ...props,
537
+ contextMenuActions: () => [
538
+ ...resolveContextActions(props.contextMenuActions),
539
+ {
540
+ id: 'row-expansion-toggle',
541
+ group: 'row-expansion',
542
+ label: isExpanded ? 'Collapse row' : 'Expand row',
543
+ icon: (
544
+ <Icon
545
+ icon={isExpanded ? 'chevronDown' : 'chevronRight'}
546
+ size="xsm"
547
+ aria-hidden
548
+ />
549
+ ),
550
+ onSelect: () => onToggle(key),
551
+ },
552
+ ],
553
+ };
554
+ },
555
+
556
+ transformBodyRow(props: BodyRowRenderProps, item: T): BodyRowRenderProps {
557
+ if (!hasRowClickExpansion) {
558
+ return props;
559
+ }
560
+ const expandable = getIsItemExpandable
561
+ ? getIsItemExpandable(item)
562
+ : getChildren(item).length > 0;
563
+ if (!expandable) {
564
+ return props;
565
+ }
566
+ const key = getRowKey(item);
567
+ return {
568
+ ...props,
569
+ htmlProps: {
570
+ ...props.htmlProps,
571
+ onClick: () => onToggle(key),
572
+ },
573
+ styles: [...props.styles, expansionStyles.clickableRow],
574
+ };
575
+ },
576
+ }),
577
+ [
578
+ expandedKeys,
579
+ getRowKey,
580
+ onToggle,
581
+ getChildren,
582
+ getDepth,
583
+ getIsItemExpandable,
584
+ hasRowClickExpansion,
585
+ isAllExpanded,
586
+ onToggleExpandAll,
587
+ expansionColumn,
588
+ ],
589
+ );
590
+ }
@@ -7,8 +7,8 @@
7
7
  * @position Testing; validates sortable plugin implementation
8
8
  */
9
9
 
10
- import {describe, it, expect, vi} from 'vitest';
11
- import {render, screen} from '@testing-library/react';
10
+ import {describe, it, expect, vi, beforeEach} from 'vitest';
11
+ import {render, screen, fireEvent} from '@testing-library/react';
12
12
  import userEvent from '@testing-library/user-event';
13
13
  import {useState} from 'react';
14
14
  import {Table} from '../../Table';
@@ -620,3 +620,92 @@ describe('useTableSortable', () => {
620
620
  });
621
621
  });
622
622
  });
623
+
624
+ // =============================================================================
625
+ // Context-menu actions (colocated with the sortable plugin)
626
+ // =============================================================================
627
+
628
+ describe('useTableSortable — context menu actions', () => {
629
+ // jsdom doesn't implement the Popover API; mock it so the menu can "open"
630
+ // and its items become queryable (as hidden).
631
+ beforeEach(() => {
632
+ HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
633
+ this.setAttribute('popover-open', '');
634
+ const event = new Event('toggle', {bubbles: false});
635
+ Object.defineProperty(event, 'newState', {value: 'open'});
636
+ this.dispatchEvent(event);
637
+ });
638
+ HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
639
+ this.removeAttribute('popover-open');
640
+ const event = new Event('toggle', {bubbles: false});
641
+ Object.defineProperty(event, 'newState', {value: 'closed'});
642
+ this.dispatchEvent(event);
643
+ });
644
+ const originalMatches = HTMLElement.prototype.matches;
645
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
646
+ (HTMLElement.prototype as any).matches = function (
647
+ selector: string,
648
+ ): boolean {
649
+ if (selector === ':popover-open') {
650
+ return this.hasAttribute('popover-open');
651
+ }
652
+ return originalMatches.call(this, selector);
653
+ };
654
+ });
655
+
656
+ it('offers Sort ascending/descending on a sortable header, and Clear sort once sorted', () => {
657
+ render(<SortableTable allowUnsortedState />);
658
+
659
+ // Unsorted: asc + desc, no "Clear sort".
660
+ fireEvent.contextMenu(screen.getByText('Name'));
661
+ expect(
662
+ screen.getAllByRole('menuitem', {name: 'Sort ascending', hidden: true})
663
+ .length,
664
+ ).toBeGreaterThan(0);
665
+ expect(
666
+ screen.getAllByRole('menuitem', {name: 'Sort descending', hidden: true})
667
+ .length,
668
+ ).toBeGreaterThan(0);
669
+ expect(
670
+ screen.queryByRole('menuitem', {name: 'Clear sort', hidden: true}),
671
+ ).not.toBeInTheDocument();
672
+
673
+ // Apply ascending → "Clear sort" now appears.
674
+ fireEvent.click(
675
+ screen.getAllByRole('menuitem', {name: 'Sort ascending', hidden: true})[0],
676
+ );
677
+ fireEvent.contextMenu(screen.getByText('Name'));
678
+ expect(
679
+ screen.getAllByRole('menuitem', {name: 'Clear sort', hidden: true}).length,
680
+ ).toBeGreaterThan(0);
681
+ });
682
+
683
+ it('resolves fresh actions on each open as sort state changes (lazy getter)', () => {
684
+ const onSortChange = vi.fn();
685
+ render(<SortableTable allowUnsortedState onSortChange={onSortChange} />);
686
+
687
+ // Open (unsorted) and pick descending.
688
+ fireEvent.contextMenu(screen.getByText('Name'));
689
+ fireEvent.click(
690
+ screen.getAllByRole('menuitem', {
691
+ name: 'Sort descending',
692
+ hidden: true,
693
+ })[0],
694
+ );
695
+ expect(onSortChange).toHaveBeenLastCalledWith([
696
+ {sortKey: 'name', direction: 'descending'},
697
+ ]);
698
+
699
+ // Re-open: the getter recomputes against the now-descending state, so
700
+ // "Clear sort" is present and clicking it clears — proving the actions are
701
+ // freshly derived on each open, not memoized from the first render.
702
+ fireEvent.contextMenu(screen.getByText('Name'));
703
+ const clear = screen.getAllByRole('menuitem', {
704
+ name: 'Clear sort',
705
+ hidden: true,
706
+ });
707
+ expect(clear.length).toBeGreaterThan(0);
708
+ fireEvent.click(clear[0]);
709
+ expect(onSortChange).toHaveBeenLastCalledWith([]);
710
+ });
711
+ });