@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2

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 (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  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.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +59 -8
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -0,0 +1,407 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTableStickyColumns.tsx
7
+ * @input React, StyleX, theme tokens, Table types
8
+ * @output Exports useTableStickyColumns hook and UseTableStickyColumnsConfig type
9
+ * @position Sticky-columns plugin; consumed by Table via plugins prop
10
+ *
11
+ * SYNC: When modified, update these files to stay in sync:
12
+ * - /packages/core/src/Table/Table.doc.mjs (sticky-columns documentation)
13
+ * - /packages/core/src/Table/index.ts (exports)
14
+ */
15
+
16
+ import {useCallback, useMemo, useRef, type CSSProperties} from 'react';
17
+ import * as stylex from '@stylexjs/stylex';
18
+ import {colorVars, durationVars, easeVars} from '../../../theme/tokens.stylex';
19
+ import type {
20
+ TableColumn,
21
+ TablePlugin,
22
+ HeaderCellRenderProps,
23
+ BodyCellRenderProps,
24
+ ScrollWrapperRenderProps,
25
+ } from '../../types';
26
+ import {DEFAULT_MIN_COLUMN_WIDTH} from '../../columnUtils';
27
+
28
+ // =============================================================================
29
+ // Config
30
+ // =============================================================================
31
+
32
+ /**
33
+ * Config for {@link useTableStickyColumns}. Provide at least one of
34
+ * `startKeys` / `endKeys` to pin columns.
35
+ *
36
+ * @remarks Every field is optional by design, so `useTableStickyColumns({})`
37
+ * compiles and is an intentional no-op that pins nothing — the hook returns a
38
+ * plugin that passes every cell through untouched. This lets callers compute
39
+ * the config conditionally (e.g. `endKeys: enabled ? ['notes'] : undefined`)
40
+ * without branching on whether to install the plugin at all.
41
+ */
42
+ export interface UseTableStickyColumnsConfig {
43
+ /**
44
+ * Column keys pinned to the START (inline-start / left in LTR) edge — the
45
+ * contiguous run from the first column through the last listed key.
46
+ */
47
+ startKeys?: string[];
48
+ /**
49
+ * Column keys pinned to the END (inline-end / right in LTR) edge — the
50
+ * contiguous run from the first listed key through the last column.
51
+ */
52
+ endKeys?: string[];
53
+ }
54
+
55
+ // =============================================================================
56
+ // Width helpers
57
+ // =============================================================================
58
+
59
+ /**
60
+ * Resolve a column's pixel width for cumulative offset math. Mirrors the
61
+ * resize plugin's fallback so offsets line up with rendered widths:
62
+ * pixel columns use their value; proportional columns use their declared
63
+ * minWidth (or the default); unknown widths use the default.
64
+ */
65
+ function getColumnWidth(col: TableColumn<Record<string, unknown>>): number {
66
+ const w = col.width;
67
+ if (!w) {
68
+ return DEFAULT_MIN_COLUMN_WIDTH;
69
+ }
70
+ if (w.type === 'pixel') {
71
+ return w.value;
72
+ }
73
+ // proportional
74
+ return w.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
75
+ }
76
+
77
+ /**
78
+ * Columns pinned to the START edge → cumulative inline offset (px). The pinned
79
+ * block is the contiguous run from column 0 through the last key in `startKeys`
80
+ * (inclusive of any synthetic columns before it). `null` if none / no context.
81
+ */
82
+ function computeStartOffsets(
83
+ columns: ReadonlyArray<TableColumn<Record<string, unknown>>> | undefined,
84
+ startKeys: string[],
85
+ ): Map<string, number> | null {
86
+ if (!columns || columns.length === 0 || startKeys.length === 0) {
87
+ return null;
88
+ }
89
+ let lastStickyIndex = -1;
90
+ for (let i = 0; i < columns.length; i++) {
91
+ if (startKeys.includes(columns[i].key)) {
92
+ lastStickyIndex = i;
93
+ }
94
+ }
95
+ if (lastStickyIndex === -1) {
96
+ return null;
97
+ }
98
+ const offsets = new Map<string, number>();
99
+ let cumulative = 0;
100
+ for (let i = 0; i <= lastStickyIndex; i++) {
101
+ offsets.set(columns[i].key, cumulative);
102
+ cumulative += getColumnWidth(columns[i]);
103
+ }
104
+ return offsets;
105
+ }
106
+
107
+ /**
108
+ * Mirror of {@link computeStartOffsets} for the END edge — contiguous run from
109
+ * the first key in `endKeys` through the last column, offsets accumulating from
110
+ * the end (last column = 0). `null` if none.
111
+ */
112
+ function computeEndOffsets(
113
+ columns: ReadonlyArray<TableColumn<Record<string, unknown>>> | undefined,
114
+ endKeys: string[],
115
+ ): Map<string, number> | null {
116
+ if (!columns || columns.length === 0 || endKeys.length === 0) {
117
+ return null;
118
+ }
119
+ let firstStickyIndex = -1;
120
+ for (let i = 0; i < columns.length; i++) {
121
+ if (endKeys.includes(columns[i].key)) {
122
+ firstStickyIndex = i;
123
+ break;
124
+ }
125
+ }
126
+ if (firstStickyIndex === -1) {
127
+ return null;
128
+ }
129
+ const offsets = new Map<string, number>();
130
+ let cumulative = 0;
131
+ for (let i = columns.length - 1; i >= firstStickyIndex; i--) {
132
+ offsets.set(columns[i].key, cumulative);
133
+ cumulative += getColumnWidth(columns[i]);
134
+ }
135
+ return offsets;
136
+ }
137
+
138
+ type StickySide = {edge: 'start' | 'end'; offset: number};
139
+
140
+ /**
141
+ * Resolve how a single column should be pinned given the start/end configs and
142
+ * the full column list. A key (mis)configured on both edges resolves to start.
143
+ * Returns `null` for columns that should not be pinned.
144
+ */
145
+ function resolveStickySide(
146
+ columns: ReadonlyArray<TableColumn<Record<string, unknown>>> | undefined,
147
+ columnKey: string,
148
+ startKeys: string[],
149
+ endKeys: string[],
150
+ ): StickySide | null {
151
+ const startOffsets = computeStartOffsets(columns, startKeys);
152
+ if (startOffsets?.has(columnKey)) {
153
+ return {edge: 'start', offset: startOffsets.get(columnKey) ?? 0};
154
+ }
155
+ const endOffsets = computeEndOffsets(columns, endKeys);
156
+ if (endOffsets?.has(columnKey)) {
157
+ return {edge: 'end', offset: endOffsets.get(columnKey) ?? 0};
158
+ }
159
+ return null;
160
+ }
161
+
162
+ // =============================================================================
163
+ // Styles
164
+ // =============================================================================
165
+
166
+ // Scroll-state CSS variables set on the scroll container by the layout ref;
167
+ // the cell ::after shadows read them so each edge's shadow only shows when
168
+ // content is hidden behind it. (A variable, not stylex.when.ancestor, because
169
+ // StyleX ancestor selectors are pseudo-class only — not scroll state.)
170
+ const SHADOW_VAR_START = '--table-sticky-shadow-start';
171
+ const SHADOW_VAR_END = '--table-sticky-shadow-end';
172
+
173
+ const stickyStyles = stylex.create({
174
+ cell: {
175
+ position: 'sticky',
176
+ // Opaque base (overridable; defaults to the card token, the common
177
+ // container) so scrolled content doesn't show through the pinned column.
178
+ backgroundColor: `var(--table-sticky-background, ${colorVars['--color-background-card']})`,
179
+ // Replay the row's overlay on a ::before layer (above the base, below text)
180
+ // so the pinned column mirrors the row's striping/hover exactly via
181
+ // --table-row-overlay. Transitions background-COLOR (not background-image)
182
+ // so hover stays smooth at rest, not just when GPU-promoted while scrolled.
183
+ '::before': {
184
+ content: '""',
185
+ position: 'absolute',
186
+ inset: 0,
187
+ zIndex: -1,
188
+ pointerEvents: 'none',
189
+ backgroundColor: 'var(--table-row-overlay, transparent)',
190
+ transitionProperty: 'background-color',
191
+ transitionDuration: durationVars['--duration-fast'],
192
+ transitionTimingFunction: easeVars['--ease-standard'],
193
+ },
194
+ // padding-box keeps the base off the cell's collapsed divider border.
195
+ backgroundClip: 'padding-box',
196
+ // Sticky cells need visible overflow so the shadow ::after can bleed out.
197
+ overflow: 'visible',
198
+ },
199
+ headerCell: {
200
+ // Header cells stack above body cells; both stack above non-sticky cells.
201
+ zIndex: 3,
202
+ },
203
+ bodyCell: {
204
+ zIndex: 1,
205
+ },
206
+ });
207
+
208
+ // Soft drop shadow over the scrolling region (matches the EPS sticky treatment).
209
+ // border-collapse tables don't paint box-shadow on cells in Chromium, so it's a
210
+ // ::after gradient strip just past the pinned edge (visible thanks to the cell's
211
+ // overflow: visible). Opacity is gated by the scroll-state variable above.
212
+ const SHADOW_WIDTH = '6px';
213
+ // --color-shadow is only ~10% alpha (too faint here); use a slightly stronger soft tint.
214
+ const SHADOW_TINT = 'light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.32))';
215
+ const shadowStyles = stylex.create({
216
+ // start-pinned: shadow falls to the right (inline-end), over scrolled content
217
+ start: {
218
+ '::after': {
219
+ content: '""',
220
+ position: 'absolute',
221
+ top: 0,
222
+ bottom: 0,
223
+ insetInlineEnd: 0,
224
+ width: SHADOW_WIDTH,
225
+ transform: 'translateX(100%)',
226
+ pointerEvents: 'none',
227
+ transition: 'opacity 150ms ease',
228
+ opacity: `var(${SHADOW_VAR_START}, 0)`,
229
+ backgroundImage: `linear-gradient(to right, ${SHADOW_TINT}, transparent)`,
230
+ },
231
+ },
232
+ // end-pinned: shadow falls to the left (inline-start), over scrolled content
233
+ end: {
234
+ '::after': {
235
+ content: '""',
236
+ position: 'absolute',
237
+ top: 0,
238
+ bottom: 0,
239
+ insetInlineStart: 0,
240
+ width: SHADOW_WIDTH,
241
+ transform: 'translateX(-100%)',
242
+ pointerEvents: 'none',
243
+ transition: 'opacity 150ms ease',
244
+ opacity: `var(${SHADOW_VAR_END}, 0)`,
245
+ backgroundImage: `linear-gradient(to left, ${SHADOW_TINT}, transparent)`,
246
+ },
247
+ },
248
+ });
249
+
250
+ // Stable empty default so an unset start/end doesn't allocate a fresh array
251
+ // (and bust the memo) on every render.
252
+ const EMPTY: string[] = [];
253
+
254
+ // =============================================================================
255
+ // Hook
256
+ // =============================================================================
257
+
258
+ export function useTableStickyColumns<T extends Record<string, unknown>>(
259
+ config: UseTableStickyColumnsConfig,
260
+ ): TablePlugin<T> {
261
+ const {startKeys, endKeys} = config;
262
+ const start = startKeys ?? EMPTY;
263
+ const end = endKeys ?? EMPTY;
264
+
265
+ const hasStart = start.length > 0;
266
+ const hasEnd = end.length > 0;
267
+
268
+ // Live snapshot of the resolved config, read inside the (memoized) transforms
269
+ // and the scroll-shadow callback. Keeping these in a ref lets the plugin memo
270
+ // be computed once (deps: only the stable scroll-shadow callback) while never
271
+ // reading stale config — consumers typically pass fresh array literals each
272
+ // render, so we never want array identity in the deps.
273
+ const stateRef = useRef({start, end, hasStart, hasEnd});
274
+ stateRef.current = {start, end, hasStart, hasEnd};
275
+
276
+ // Scroll-aware shadows: toggle CSS variables on the scroll container so each
277
+ // edge's shadow only paints when there is hidden, horizontally-scrolled
278
+ // content behind that edge. Implemented with a callback ref + scroll/resize
279
+ // listeners (synchronizing with the DOM) rather than React state, so
280
+ // scrolling never triggers re-renders.
281
+ const detachRef = useRef<(() => void) | null>(null);
282
+ const attachScrollShadow = useCallback((el: HTMLDivElement | null) => {
283
+ detachRef.current?.();
284
+ detachRef.current = null;
285
+ if (!el) {
286
+ return;
287
+ }
288
+ const update = () => {
289
+ const {hasStart: hs, hasEnd: he} = stateRef.current;
290
+ const maxScroll = el.scrollWidth - el.clientWidth;
291
+ const hasOverflow = maxScroll > 1;
292
+ if (hs) {
293
+ el.style.setProperty(
294
+ SHADOW_VAR_START,
295
+ hasOverflow && el.scrollLeft > 1 ? '1' : '0',
296
+ );
297
+ }
298
+ if (he) {
299
+ el.style.setProperty(
300
+ SHADOW_VAR_END,
301
+ hasOverflow && el.scrollLeft < maxScroll - 1 ? '1' : '0',
302
+ );
303
+ }
304
+ };
305
+ el.addEventListener('scroll', update, {passive: true});
306
+ const resizeObserver =
307
+ typeof ResizeObserver !== 'undefined' ? new ResizeObserver(update) : null;
308
+ resizeObserver?.observe(el);
309
+ update();
310
+ detachRef.current = () => {
311
+ el.removeEventListener('scroll', update);
312
+ resizeObserver?.disconnect();
313
+ };
314
+ }, []);
315
+
316
+ return useMemo(
317
+ (): TablePlugin<T> => ({
318
+ transformHeaderCell(
319
+ props: HeaderCellRenderProps,
320
+ column: TableColumn<T>,
321
+ ): HeaderCellRenderProps {
322
+ const {start: s, end: e} = stateRef.current;
323
+ const side = resolveStickySide(props.columns, column.key, s, e);
324
+ if (!side) {
325
+ return props;
326
+ }
327
+ // position/inline-offset are runtime values → set via inline style so
328
+ // they are authoritative regardless of plugin composition order (the
329
+ // resize plugin also writes inline style on header cells).
330
+ const offsetStyle: CSSProperties =
331
+ side.edge === 'start'
332
+ ? {insetInlineStart: `${side.offset}px`}
333
+ : {insetInlineEnd: `${side.offset}px`};
334
+ return {
335
+ ...props,
336
+ htmlProps: {
337
+ ...props.htmlProps,
338
+ style: {...props.htmlProps.style, ...offsetStyle},
339
+ },
340
+ styles: [
341
+ ...props.styles,
342
+ stickyStyles.cell,
343
+ stickyStyles.headerCell,
344
+ side.edge === 'start' ? shadowStyles.start : shadowStyles.end,
345
+ ],
346
+ };
347
+ },
348
+
349
+ transformBodyCell(
350
+ props: BodyCellRenderProps,
351
+ column: TableColumn<T>,
352
+ ): BodyCellRenderProps {
353
+ const {start: s, end: e} = stateRef.current;
354
+ const side = resolveStickySide(props.columns, column.key, s, e);
355
+ if (!side) {
356
+ return props;
357
+ }
358
+ const offsetStyle: CSSProperties =
359
+ side.edge === 'start'
360
+ ? {insetInlineStart: `${side.offset}px`}
361
+ : {insetInlineEnd: `${side.offset}px`};
362
+ return {
363
+ ...props,
364
+ htmlProps: {
365
+ ...props.htmlProps,
366
+ style: {...props.htmlProps.style, ...offsetStyle},
367
+ },
368
+ styles: [
369
+ ...props.styles,
370
+ stickyStyles.cell,
371
+ stickyStyles.bodyCell,
372
+ side.edge === 'start' ? shadowStyles.start : shadowStyles.end,
373
+ ],
374
+ };
375
+ },
376
+
377
+ transformScrollWrapper(
378
+ props: ScrollWrapperRenderProps,
379
+ ): ScrollWrapperRenderProps {
380
+ // No pinned edges → nothing to gate; leave the wrapper untouched.
381
+ if (!stateRef.current.hasStart && !stateRef.current.hasEnd) {
382
+ return props;
383
+ }
384
+ // Compose with any ref a prior plugin (e.g. virtualization) set on the
385
+ // scroll container.
386
+ const existingRef = props.htmlProps.ref;
387
+ const mergedRef = (node: HTMLDivElement | null) => {
388
+ attachScrollShadow(node);
389
+ if (typeof existingRef === 'function') {
390
+ existingRef(node);
391
+ } else if (existingRef != null) {
392
+ // RefObject — assign through its writable `.current`.
393
+ existingRef.current = node;
394
+ }
395
+ };
396
+ return {
397
+ ...props,
398
+ htmlProps: {...props.htmlProps, ref: mergedRef},
399
+ };
400
+ },
401
+ }),
402
+ // The returned plugin's transforms read live config from `stateRef` and
403
+ // `attachScrollShadow` is stable (empty deps), so the plugin object can be
404
+ // computed once and reused across renders.
405
+ [attachScrollShadow],
406
+ );
407
+ }
@@ -0,0 +1,157 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file tableContextMenu.test.tsx
5
+ * @output Tests for the Table context-menu system
6
+ * @position Validates contextMenuActions aggregation + header context menus
7
+ */
8
+
9
+ import {describe, it, expect, vi, beforeEach} from 'vitest';
10
+ import {render, screen, fireEvent} from '@testing-library/react';
11
+ import {Table} from './Table';
12
+ import type {TablePlugin, TableColumn} from './types';
13
+ import {resolveContextActions} from './tableContextMenu';
14
+
15
+ // jsdom doesn't implement the Popover API; mirror the ContextMenu test's mock
16
+ // so the menu can "open" and its items become queryable (as hidden).
17
+ beforeEach(() => {
18
+ HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
19
+ this.setAttribute('popover-open', '');
20
+ const event = new Event('toggle', {bubbles: false});
21
+ Object.defineProperty(event, 'newState', {value: 'open'});
22
+ this.dispatchEvent(event);
23
+ });
24
+ HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
25
+ this.removeAttribute('popover-open');
26
+ const event = new Event('toggle', {bubbles: false});
27
+ Object.defineProperty(event, 'newState', {value: 'closed'});
28
+ this.dispatchEvent(event);
29
+ });
30
+ const originalMatches = HTMLElement.prototype.matches;
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ (HTMLElement.prototype as any).matches = function (
33
+ selector: string,
34
+ ): boolean {
35
+ if (selector === ':popover-open') {
36
+ return this.hasAttribute('popover-open');
37
+ }
38
+ return originalMatches.call(this, selector);
39
+ };
40
+ });
41
+
42
+ interface Row extends Record<string, unknown> {
43
+ id: string;
44
+ name: string;
45
+ }
46
+
47
+ const data: Row[] = [
48
+ {id: '1', name: 'Alice'},
49
+ {id: '2', name: 'Bob'},
50
+ ];
51
+
52
+ const columns: TableColumn<Row>[] = [
53
+ {key: 'name', header: 'Name'},
54
+ {key: 'id', header: 'ID'},
55
+ ];
56
+
57
+ // =============================================================================
58
+ // contextMenuActions via transformHeaderCell
59
+ // =============================================================================
60
+
61
+ describe('Table header context menu', () => {
62
+ it('renders a menu from a plugin that sets contextMenuActions', () => {
63
+ const onSelect = vi.fn();
64
+ const plugin: TablePlugin<Row> = {
65
+ transformHeaderCell: props => ({
66
+ ...props,
67
+ contextMenuActions: [{id: 'pin', label: 'Pin column', onSelect}],
68
+ }),
69
+ };
70
+ render(
71
+ <Table data={data} columns={columns} idKey="id" plugins={{plugin}} />,
72
+ );
73
+ fireEvent.contextMenu(screen.getByText('Name'));
74
+ const items = screen.getAllByRole('menuitem', {
75
+ name: 'Pin column',
76
+ hidden: true,
77
+ });
78
+ expect(items.length).toBeGreaterThan(0);
79
+ fireEvent.click(items[0]);
80
+ expect(onSelect).toHaveBeenCalledTimes(1);
81
+ });
82
+
83
+ it('concatenates actions from multiple plugins (never overridden)', () => {
84
+ const a: TablePlugin<Row> = {
85
+ transformHeaderCell: props => ({
86
+ ...props,
87
+ contextMenuActions: [
88
+ ...resolveContextActions(props.contextMenuActions),
89
+ {id: 'a', label: 'Action A', onSelect: () => {}},
90
+ ],
91
+ }),
92
+ };
93
+ const b: TablePlugin<Row> = {
94
+ transformHeaderCell: props => ({
95
+ ...props,
96
+ contextMenuActions: [
97
+ ...resolveContextActions(props.contextMenuActions),
98
+ {id: 'b', label: 'Action B', onSelect: () => {}},
99
+ ],
100
+ }),
101
+ };
102
+ render(
103
+ <Table data={data} columns={columns} idKey="id" plugins={{a, b}} />,
104
+ );
105
+ fireEvent.contextMenu(screen.getByText('Name'));
106
+ expect(
107
+ screen.getAllByRole('menuitem', {name: 'Action A', hidden: true}).length,
108
+ ).toBeGreaterThan(0);
109
+ expect(
110
+ screen.getAllByRole('menuitem', {name: 'Action B', hidden: true}).length,
111
+ ).toBeGreaterThan(0);
112
+ });
113
+
114
+ it('does not render a menu when no plugin contributes actions', () => {
115
+ render(<Table data={data} columns={columns} idKey="id" />);
116
+ fireEvent.contextMenu(screen.getByText('Name'));
117
+ expect(
118
+ screen.queryByRole('menuitem', {hidden: true}),
119
+ ).not.toBeInTheDocument();
120
+ });
121
+ });
122
+
123
+ // =============================================================================
124
+ // Body / row context menu
125
+ // =============================================================================
126
+
127
+ describe('Table body context menu', () => {
128
+ it('renders row actions from a plugin that sets contextMenuActions in transformBodyCell', () => {
129
+ const onSelect = vi.fn();
130
+ const plugin: TablePlugin<Row> = {
131
+ transformBodyCell: (props, _column, item) => ({
132
+ ...props,
133
+ contextMenuActions: [
134
+ {
135
+ id: `delete-${item.id}`,
136
+ label: 'Delete row',
137
+ onSelect: () => {
138
+ onSelect(item.id);
139
+ },
140
+ },
141
+ ],
142
+ }),
143
+ };
144
+ render(
145
+ <Table data={data} columns={columns} idKey="id" plugins={{plugin}} />,
146
+ );
147
+ // Right-click the first body cell (Alice).
148
+ fireEvent.contextMenu(screen.getByText('Alice'));
149
+ const items = screen.getAllByRole('menuitem', {
150
+ name: 'Delete row',
151
+ hidden: true,
152
+ });
153
+ expect(items.length).toBeGreaterThan(0);
154
+ fireEvent.click(items[0]);
155
+ expect(onSelect).toHaveBeenCalledWith('1');
156
+ });
157
+ });
@@ -0,0 +1,142 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file tableContextMenu.tsx
7
+ * @input React, ContextMenu, Icon, types
8
+ * @output wrapInTableContextMenu helper
9
+ * @position Renders the aggregated `contextMenuActions` for a header cell / row
10
+ *
11
+ * SYNC: When modified, update these files to stay in sync:
12
+ * - /packages/core/src/Table/BaseTable.tsx (call sites)
13
+ * - /packages/core/src/Table/types.ts (TableContextAction)
14
+ */
15
+
16
+ import {useState, type ReactNode} from 'react';
17
+ import {ContextMenu, type ContextMenuOption} from '../ContextMenu';
18
+ import {Icon} from '../Icon';
19
+ import type {TableContextAction, TableContextActions} from './types';
20
+
21
+ /**
22
+ * Resolve a `contextMenuActions` value (array or getter) to a plain array.
23
+ * Use this when composing actions from a prior plugin's render props so you
24
+ * don't have to branch on the array-vs-getter form:
25
+ *
26
+ * ```
27
+ * contextMenuActions: () => [
28
+ * ...resolveContextActions(props.contextMenuActions),
29
+ * ...myActions,
30
+ * ]
31
+ * ```
32
+ */
33
+ export function resolveContextActions(
34
+ actions: TableContextActions | undefined,
35
+ ): TableContextAction[] {
36
+ if (typeof actions === 'function') {
37
+ return actions();
38
+ }
39
+ return actions ?? [];
40
+ }
41
+
42
+ /**
43
+ * Convert the flat action list into ContextMenu options, inserting a divider
44
+ * between groups (first-seen group order). Ungrouped actions form a trailing
45
+ * group. A `checked` action shows a trailing check icon.
46
+ */
47
+ function toContextMenuOptions(
48
+ actions: TableContextAction[],
49
+ ): ContextMenuOption[] {
50
+ const order: string[] = [];
51
+ const buckets = new Map<string, TableContextAction[]>();
52
+ for (const action of actions) {
53
+ const key = action.group ?? '__ungrouped__';
54
+ let bucket = buckets.get(key);
55
+ if (!bucket) {
56
+ bucket = [];
57
+ buckets.set(key, bucket);
58
+ order.push(key);
59
+ }
60
+ bucket.push(action);
61
+ }
62
+
63
+ const options: ContextMenuOption[] = [];
64
+ order.forEach((key, groupIndex) => {
65
+ if (groupIndex > 0) {
66
+ options.push({type: 'divider'});
67
+ }
68
+ for (const action of buckets.get(key) ?? []) {
69
+ options.push({
70
+ label: typeof action.label === 'string' ? action.label : action.id,
71
+ // A checked action (e.g. the active sort direction) shows a checkmark;
72
+ // otherwise the action's own icon. ContextMenu's data form takes a
73
+ // single leading icon, so checked state replaces the icon.
74
+ icon: action.checked ? (
75
+ <Icon icon="check" size="xsm" aria-hidden />
76
+ ) : (
77
+ action.icon
78
+ ),
79
+ isDisabled: action.disabled,
80
+ onClick: action.onSelect,
81
+ });
82
+ }
83
+ });
84
+ return options;
85
+ }
86
+
87
+ /**
88
+ * Wrap a table element (a header cell's content or a row) in a right-click
89
+ * context menu rendering the aggregated `actions`. When no plugin contributes
90
+ * actions the element is returned untouched so the native browser menu passes
91
+ * through.
92
+ */
93
+ /**
94
+ * A context menu whose actions are resolved lazily — only when the user opens
95
+ * the menu (right-click). Deferring the work means plugins that pass a getter
96
+ * don't build an action array (with its closures) for every cell on every
97
+ * render; it's computed on demand and memoized until the menu closes.
98
+ */
99
+ function LazyTableContextMenu({
100
+ element,
101
+ getActions,
102
+ }: {
103
+ element: ReactNode;
104
+ getActions: () => TableContextAction[];
105
+ }): ReactNode {
106
+ const [options, setOptions] = useState<ContextMenuOption[] | null>(null);
107
+ return (
108
+ <ContextMenu
109
+ items={options ?? []}
110
+ onOpenChange={open => {
111
+ // Resolve actions when opening; clear on close so state derived later
112
+ // (e.g. current sort direction) is always fresh next open.
113
+ setOptions(open ? toContextMenuOptions(getActions()) : null);
114
+ }}>
115
+ {element}
116
+ </ContextMenu>
117
+ );
118
+ }
119
+
120
+ /**
121
+ * Wrap a table element (a header cell's content or a body cell) in a right-click
122
+ * context menu rendering the aggregated `actions`. Accepts a static array or a
123
+ * getter (resolved lazily on open). When there are no actions the element is
124
+ * returned untouched so the native browser menu passes through.
125
+ */
126
+ export function wrapInTableContextMenu(
127
+ element: ReactNode,
128
+ actions: TableContextActions | undefined,
129
+ ): ReactNode {
130
+ // Getter form → resolve lazily on open.
131
+ if (typeof actions === 'function') {
132
+ return <LazyTableContextMenu element={element} getActions={actions} />;
133
+ }
134
+ if (!actions || actions.length === 0) {
135
+ return element;
136
+ }
137
+ // item (it looked like "ascending" was always selected). Focus moves only
138
+ // when the user arrow-keys into the menu.
139
+ return (
140
+ <ContextMenu items={toContextMenuOptions(actions)}>{element}</ContextMenu>
141
+ );
142
+ }