@astryxdesign/core 0.1.7 → 0.1.8-canary.0c4d8d7

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 (428) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +30 -19
  3. package/dist/AppShell/AppShell.d.ts.map +1 -1
  4. package/dist/AppShell/AppShell.js +6 -2
  5. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  6. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  7. package/dist/Avatar/Avatar.d.ts +10 -5
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +12 -8
  10. package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
  11. package/dist/Avatar/AvatarSizeContext.js +1 -1
  12. package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
  13. package/dist/Avatar/AvatarStatusDot.js +2 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
  15. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.js +2 -2
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
  20. package/dist/Banner/Banner.d.ts +9 -1
  21. package/dist/Banner/Banner.d.ts.map +1 -1
  22. package/dist/Banner/Banner.js +31 -2
  23. package/dist/Button/Button.d.ts +8 -2
  24. package/dist/Button/Button.d.ts.map +1 -1
  25. package/dist/Button/Button.js +31 -2
  26. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  27. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  28. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  29. package/dist/Card/Card.d.ts +8 -2
  30. package/dist/Card/Card.d.ts.map +1 -1
  31. package/dist/Card/Card.js +28 -2
  32. package/dist/Carousel/Carousel.d.ts +6 -2
  33. package/dist/Carousel/Carousel.d.ts.map +1 -1
  34. package/dist/Carousel/Carousel.js +55 -5
  35. package/dist/Chat/ChatComposer.d.ts +8 -0
  36. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  37. package/dist/Chat/ChatComposer.js +31 -6
  38. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  39. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  40. package/dist/Chat/ChatComposerDrawer.js +11 -1
  41. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +19 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  46. package/dist/Chat/ChatMessage.d.ts +1 -1
  47. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessage.js +1 -1
  49. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  50. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  51. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  52. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  53. package/dist/Chat/ChatToolCalls.js +12 -3
  54. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  55. package/dist/Citation/Citation.d.ts.map +1 -1
  56. package/dist/Citation/Citation.js +10 -4
  57. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  58. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  59. package/dist/ClickableCard/ClickableCard.js +3 -0
  60. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  61. package/dist/CodeBlock/CodeBlock.js +8 -1
  62. package/dist/Collapsible/Collapsible.d.ts +11 -1
  63. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  64. package/dist/Collapsible/Collapsible.js +36 -4
  65. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  66. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  67. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  68. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  69. package/dist/ContextMenu/ContextMenu.js +4 -3
  70. package/dist/ContextMenu/index.d.ts +3 -0
  71. package/dist/ContextMenu/index.d.ts.map +1 -1
  72. package/dist/ContextMenu/index.js +7 -1
  73. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  74. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  75. package/dist/Dialog/Dialog.d.ts.map +1 -1
  76. package/dist/Dialog/Dialog.js +2 -1
  77. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenu.js +8 -4
  79. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
  80. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
  82. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
  83. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
  85. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
  86. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
  88. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
  89. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
  90. package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
  91. package/dist/DropdownMenu/index.d.ts +5 -1
  92. package/dist/DropdownMenu/index.d.ts.map +1 -1
  93. package/dist/DropdownMenu/index.js +10 -1
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
  96. package/dist/DropdownMenu/menuItemRoles.js +15 -0
  97. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  98. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  99. package/dist/FieldStatus/FieldStatus.js +22 -4
  100. package/dist/FileInput/FileInput.d.ts.map +1 -1
  101. package/dist/FileInput/FileInput.js +10 -2
  102. package/dist/HoverCard/HoverCard.d.ts +9 -1
  103. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  104. package/dist/HoverCard/HoverCard.js +2 -0
  105. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  106. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  107. package/dist/HoverCard/useHoverCard.js +7 -2
  108. package/dist/Icon/Icon.d.ts +43 -13
  109. package/dist/Icon/Icon.d.ts.map +1 -1
  110. package/dist/Icon/Icon.js +62 -27
  111. package/dist/Layer/useLayer.d.ts +6 -0
  112. package/dist/Layer/useLayer.d.ts.map +1 -1
  113. package/dist/Layer/useLayer.js +2 -0
  114. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  115. package/dist/MobileNav/MobileNav.js +6 -1
  116. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  117. package/dist/MobileNav/MobileNavToggle.js +4 -0
  118. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  119. package/dist/MultiSelector/MultiSelector.js +39 -12
  120. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  121. package/dist/MultiSelector/hooks.js +16 -0
  122. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  123. package/dist/Outline/Outline.d.ts +68 -3
  124. package/dist/Outline/Outline.d.ts.map +1 -1
  125. package/dist/Outline/Outline.js +128 -26
  126. package/dist/Outline/useScrollSpy.d.ts +27 -7
  127. package/dist/Outline/useScrollSpy.d.ts.map +1 -1
  128. package/dist/Outline/useScrollSpy.js +202 -70
  129. package/dist/OverflowList/OverflowList.d.ts +20 -3
  130. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  131. package/dist/OverflowList/OverflowList.js +29 -4
  132. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  133. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  134. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  135. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  136. package/dist/SelectableCard/SelectableCard.js +14 -11
  137. package/dist/Selector/Selector.d.ts.map +1 -1
  138. package/dist/Selector/Selector.js +4 -2
  139. package/dist/Selector/hooks.d.ts.map +1 -1
  140. package/dist/Selector/hooks.js +16 -0
  141. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  142. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  143. package/dist/SideNav/SideNavHeading.js +21 -8
  144. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  145. package/dist/Slider/Slider.d.ts.map +1 -1
  146. package/dist/Slider/Slider.js +15 -1
  147. package/dist/TabList/Tab.d.ts.map +1 -1
  148. package/dist/TabList/Tab.js +2 -2
  149. package/dist/TabList/TabList.d.ts.map +1 -1
  150. package/dist/TabList/TabList.js +2 -0
  151. package/dist/TabList/TabMenu.d.ts.map +1 -1
  152. package/dist/TabList/TabMenu.js +1 -1
  153. package/dist/Table/Table.d.ts +1 -1
  154. package/dist/Table/Table.js +1 -1
  155. package/dist/Table/index.d.ts +4 -0
  156. package/dist/Table/index.d.ts.map +1 -1
  157. package/dist/Table/index.js +2 -0
  158. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  159. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  160. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
  161. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  162. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  163. package/dist/Table/plugins/rowStatus/index.js +3 -0
  164. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  165. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  166. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  167. package/dist/Table/plugins/selection/useTableSelection.d.ts +12 -0
  168. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  169. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  170. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  171. package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
  172. package/dist/Table/plugins/tree/index.d.ts +5 -0
  173. package/dist/Table/plugins/tree/index.d.ts.map +1 -0
  174. package/dist/Table/plugins/tree/index.js +4 -0
  175. package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
  176. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
  177. package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
  178. package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
  179. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
  180. package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
  181. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  182. package/dist/Table/useBaseTablePlugins.js +6 -3
  183. package/dist/Thumbnail/Thumbnail.d.ts +8 -1
  184. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  185. package/dist/Thumbnail/Thumbnail.js +46 -8
  186. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  187. package/dist/TimeInput/TimeInput.js +15 -4
  188. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  189. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  190. package/dist/Tokenizer/Tokenizer.js +16 -4
  191. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  192. package/dist/Toolbar/Toolbar.js +8 -1
  193. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  194. package/dist/TopNav/TopNavHeading.js +18 -8
  195. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  196. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  197. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  198. package/dist/TopNav/TopNavMenu.js +68 -6
  199. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  200. package/dist/TreeList/TreeListItem.js +5 -14
  201. package/dist/astryx.css +51 -16
  202. package/dist/astryx.umd.js +56 -56
  203. package/dist/astryx.umd.js.map +4 -4
  204. package/dist/authoring/doc.d.ts.map +1 -1
  205. package/dist/hooks/computeOverflow.d.ts +47 -0
  206. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  207. package/dist/hooks/computeOverflow.js +213 -0
  208. package/dist/hooks/useOverflow.d.ts +19 -0
  209. package/dist/hooks/useOverflow.d.ts.map +1 -1
  210. package/dist/hooks/useOverflow.js +37 -25
  211. package/dist/theme/Theme.js +1 -1
  212. package/dist/theme/defineTheme.d.ts +1 -1
  213. package/dist/theme/defineTheme.js +1 -1
  214. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  215. package/dist/theme/generateThemeRules.js +51 -0
  216. package/dist/theme/hct.d.ts +0 -1
  217. package/dist/theme/hct.d.ts.map +1 -1
  218. package/dist/theme/hct.js +0 -3
  219. package/dist/utils/types.d.ts +13 -0
  220. package/dist/utils/types.d.ts.map +1 -1
  221. package/docs.mjs +32 -461
  222. package/locales/en.json +212 -172
  223. package/locales/fr-FR.json +10 -0
  224. package/locales/pseudo.json +30 -0
  225. package/package.json +3 -1
  226. package/scripts/postinstall.mjs +93 -0
  227. package/src/AppShell/AppShell.tsx +6 -0
  228. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  229. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  230. package/src/Avatar/Avatar.doc.mjs +6 -6
  231. package/src/Avatar/Avatar.tsx +16 -11
  232. package/src/Avatar/AvatarSizeContext.ts +1 -1
  233. package/src/Avatar/AvatarStatusDot.tsx +2 -2
  234. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
  235. package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
  236. package/src/AvatarGroup/AvatarGroup.tsx +3 -3
  237. package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
  238. package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
  239. package/src/Banner/Banner.doc.mjs +9 -0
  240. package/src/Banner/Banner.test.tsx +30 -0
  241. package/src/Banner/Banner.tsx +47 -3
  242. package/src/Button/Button.doc.mjs +9 -0
  243. package/src/Button/Button.test.tsx +49 -0
  244. package/src/Button/Button.tsx +25 -1
  245. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  246. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  247. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  248. package/src/Card/Card.doc.mjs +9 -0
  249. package/src/Card/Card.test.tsx +21 -3
  250. package/src/Card/Card.tsx +35 -2
  251. package/src/Carousel/Carousel.doc.mjs +1 -0
  252. package/src/Carousel/Carousel.test.tsx +149 -1
  253. package/src/Carousel/Carousel.tsx +62 -5
  254. package/src/Chat/Chat.doc.mjs +1 -1
  255. package/src/Chat/ChatComposer.doc.mjs +9 -0
  256. package/src/Chat/ChatComposer.test.tsx +35 -0
  257. package/src/Chat/ChatComposer.tsx +58 -9
  258. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  259. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  260. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  261. package/src/Chat/ChatComposerInput.test.tsx +87 -0
  262. package/src/Chat/ChatComposerInput.tsx +35 -3
  263. package/src/Chat/ChatLayout.tsx +3 -1
  264. package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
  265. package/src/Chat/ChatMessage.tsx +4 -2
  266. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  267. package/src/Chat/ChatSendButton.tsx +5 -1
  268. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  269. package/src/Chat/ChatToolCalls.tsx +13 -1
  270. package/src/Chat/useTriggerMenu.tsx +3 -1
  271. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  272. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  273. package/src/CheckboxList/CheckboxList.test.tsx +9 -1
  274. package/src/Citation/Citation.test.tsx +51 -0
  275. package/src/Citation/Citation.tsx +8 -2
  276. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  277. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  278. package/src/ClickableCard/ClickableCard.tsx +11 -1
  279. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  280. package/src/CodeBlock/CodeBlock.tsx +9 -1
  281. package/src/Collapsible/Collapsible.doc.mjs +7 -0
  282. package/src/Collapsible/Collapsible.test.tsx +80 -0
  283. package/src/Collapsible/Collapsible.tsx +58 -5
  284. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  285. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  286. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  287. package/src/ContextMenu/ContextMenu.test.tsx +75 -1
  288. package/src/ContextMenu/ContextMenu.tsx +10 -5
  289. package/src/ContextMenu/index.ts +15 -0
  290. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  291. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  292. package/src/Dialog/Dialog.tsx +12 -2
  293. package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
  294. package/src/DropdownMenu/DropdownMenu.tsx +11 -6
  295. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
  296. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
  297. package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
  298. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
  299. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
  300. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
  301. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
  302. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
  303. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
  304. package/src/DropdownMenu/index.ts +23 -3
  305. package/src/DropdownMenu/menuItemRoles.ts +22 -0
  306. package/src/Field/Field.doc.mjs +1 -1
  307. package/src/Field/Field.test.tsx +57 -12
  308. package/src/Field/FieldLabel.doc.mjs +1 -1
  309. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  310. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  311. package/src/FieldStatus/FieldStatus.tsx +22 -4
  312. package/src/FileInput/FileInput.test.tsx +52 -3
  313. package/src/FileInput/FileInput.tsx +13 -2
  314. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  315. package/src/HoverCard/HoverCard.test.tsx +21 -2
  316. package/src/HoverCard/HoverCard.tsx +11 -0
  317. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  318. package/src/HoverCard/useHoverCard.tsx +24 -2
  319. package/src/Icon/Icon.doc.mjs +12 -0
  320. package/src/Icon/Icon.test.tsx +142 -0
  321. package/src/Icon/Icon.tsx +98 -30
  322. package/src/IconButton/IconButton.doc.mjs +8 -0
  323. package/src/IconButton/IconButton.test.tsx +15 -18
  324. package/src/Item/Item.doc.mjs +1 -1
  325. package/src/Layer/useLayer.tsx +8 -0
  326. package/src/MobileNav/MobileNav.tsx +12 -1
  327. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  328. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  329. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  330. package/src/MultiSelector/MultiSelector.test.tsx +162 -1
  331. package/src/MultiSelector/MultiSelector.tsx +55 -12
  332. package/src/MultiSelector/hooks.ts +17 -0
  333. package/src/NumberInput/NumberInput.doc.mjs +2 -2
  334. package/src/NumberInput/NumberInput.test.tsx +11 -7
  335. package/src/NumberInput/NumberInput.tsx +1 -2
  336. package/src/Outline/Outline.doc.mjs +123 -3
  337. package/src/Outline/Outline.test.tsx +735 -0
  338. package/src/Outline/Outline.tsx +183 -33
  339. package/src/Outline/useScrollSpy.ts +248 -72
  340. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  341. package/src/OverflowList/OverflowList.test.tsx +111 -0
  342. package/src/OverflowList/OverflowList.tsx +48 -8
  343. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  344. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  345. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  346. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  347. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  348. package/src/SelectableCard/SelectableCard.tsx +27 -14
  349. package/src/Selector/Selector.test.tsx +69 -0
  350. package/src/Selector/Selector.tsx +5 -1
  351. package/src/Selector/SelectorOption.doc.mjs +1 -1
  352. package/src/Selector/hooks.ts +17 -0
  353. package/src/SideNav/SideNav.test.tsx +119 -3
  354. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  355. package/src/SideNav/SideNavHeading.tsx +24 -8
  356. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  357. package/src/SideNav/SideNavItem.tsx +5 -1
  358. package/src/Slider/Slider.test.tsx +62 -2
  359. package/src/Slider/Slider.tsx +15 -1
  360. package/src/Switch/Switch.doc.mjs +1 -1
  361. package/src/Switch/Switch.test.tsx +32 -1
  362. package/src/TabList/Tab.tsx +5 -1
  363. package/src/TabList/TabList.doc.mjs +3 -3
  364. package/src/TabList/TabList.test.tsx +66 -0
  365. package/src/TabList/TabList.tsx +8 -0
  366. package/src/TabList/TabMenu.tsx +4 -1
  367. package/src/Table/Table.doc.mjs +2 -0
  368. package/src/Table/Table.tsx +1 -1
  369. package/src/Table/index.ts +12 -0
  370. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  371. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
  372. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
  373. package/src/Table/plugins/rowStatus/index.ts +7 -0
  374. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  375. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  376. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  377. package/src/Table/plugins/selection/useTableSelection.tsx +18 -1
  378. package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
  379. package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
  380. package/src/Table/plugins/tree/index.ts +12 -0
  381. package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
  382. package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
  383. package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
  384. package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
  385. package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
  386. package/src/Table/useBaseTablePlugins.ts +6 -2
  387. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  388. package/src/Table/useTableSelection.doc.mjs +65 -14
  389. package/src/Table/useTableTreeData.doc.mjs +75 -0
  390. package/src/Table/useTableTreeState.doc.mjs +98 -0
  391. package/src/TextArea/TextArea.doc.mjs +1 -1
  392. package/src/TextArea/TextArea.test.tsx +9 -1
  393. package/src/TextInput/TextInput.doc.mjs +1 -1
  394. package/src/Thumbnail/Thumbnail.doc.mjs +9 -0
  395. package/src/Thumbnail/Thumbnail.test.tsx +38 -8
  396. package/src/Thumbnail/Thumbnail.tsx +28 -1
  397. package/src/TimeInput/TimeInput.test.tsx +76 -1
  398. package/src/TimeInput/TimeInput.tsx +19 -6
  399. package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
  400. package/src/Tokenizer/Tokenizer.test.tsx +137 -2
  401. package/src/Tokenizer/Tokenizer.tsx +16 -4
  402. package/src/Toolbar/Toolbar.tsx +10 -3
  403. package/src/TopNav/TopNav.test.tsx +80 -1
  404. package/src/TopNav/TopNavHeading.tsx +24 -8
  405. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  406. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  407. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  408. package/src/TopNav/TopNavMenu.tsx +83 -5
  409. package/src/TreeList/TreeList.test.tsx +32 -1
  410. package/src/TreeList/TreeListItem.tsx +19 -10
  411. package/src/authoring/doc.ts +1 -3
  412. package/src/docs-script.test.mjs +18 -13
  413. package/src/hooks/computeOverflow.test.ts +422 -0
  414. package/src/hooks/computeOverflow.ts +290 -0
  415. package/src/hooks/useOverflow.test.ts +96 -4
  416. package/src/hooks/useOverflow.ts +77 -33
  417. package/src/postinstall.test.mjs +71 -0
  418. package/src/theme/Theme.doc.mjs +4 -4
  419. package/src/theme/Theme.tsx +1 -1
  420. package/src/theme/defineTheme.ts +1 -1
  421. package/src/theme/generateThemeRules.test.ts +65 -0
  422. package/src/theme/generateThemeRules.ts +56 -0
  423. package/src/theme/hct.ts +0 -4
  424. package/src/utils/types.ts +14 -0
  425. package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
  426. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
  427. package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
  428. package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
@@ -9,8 +9,9 @@
9
9
  * @position Core implementation; consumed by index.ts
10
10
  *
11
11
  * Renders a horizontal list of items, hiding those that don't fit in the
12
- * available width and optionally showing an overflow indicator.
13
- * Uses a hidden measurement container to avoid flickering.
12
+ * available width and optionally showing an overflow indicator. Supports an
13
+ * optional item cap (`maxVisibleItems`) and bounded multi-row wrapping
14
+ * (`maxRows`). Uses a hidden measurement container to avoid flickering.
14
15
  *
15
16
  * SYNC: When modified, update these files to stay in sync:
16
17
  * - /packages/core/src/OverflowList/index.ts (exports if types change)
@@ -34,6 +35,14 @@ const styles = stylex.create({
34
35
  whiteSpace: 'nowrap',
35
36
  minWidth: 0,
36
37
  },
38
+ containerMultiRow: {
39
+ display: 'flex',
40
+ flexWrap: 'wrap',
41
+ alignContent: 'flex-start',
42
+ overflow: 'hidden',
43
+ whiteSpace: 'normal',
44
+ minWidth: 0,
45
+ },
37
46
  fillParent: {
38
47
  width: '100%',
39
48
  },
@@ -52,6 +61,12 @@ const styles = stylex.create({
52
61
  },
53
62
  });
54
63
 
64
+ const multiRowHeight = stylex.create({
65
+ height: (maxRows: number, rowHeight: number, gapPx: number) => ({
66
+ maxHeight: `calc(${rowHeight}px * ${maxRows} + ${gapPx}px * ${maxRows - 1})`,
67
+ }),
68
+ });
69
+
55
70
  const gapStyles = stylex.create({
56
71
  0: {gap: spacingVars['--spacing-0']},
57
72
  0.5: {gap: spacingVars['--spacing-0-5']},
@@ -112,6 +127,24 @@ export interface OverflowListProps extends BaseProps<HTMLDivElement> {
112
127
  */
113
128
  minVisibleItems?: number;
114
129
 
130
+ /**
131
+ * Maximum number of items to ever show, even when they all fit. The ceiling
132
+ * partner to `minVisibleItems`; extra items collapse into the overflow
133
+ * indicator. Leave undefined for no cap. If it is less than
134
+ * `minVisibleItems`, the floor wins (and a dev-only warning is logged).
135
+ * @default undefined
136
+ */
137
+ maxVisibleItems?: number;
138
+
139
+ /**
140
+ * Wrap items across up to this many rows before collapsing the remainder
141
+ * into the overflow indicator. Leave undefined (or set `1`) for the default
142
+ * single-line behavior. A number, not a boolean — unbounded wrapping is a
143
+ * plain flex-wrap layout, not overflow collapse. Assumes uniform row height.
144
+ * @default undefined
145
+ */
146
+ maxRows?: number;
147
+
115
148
  /**
116
149
  * Which end to collapse items from.
117
150
  * @default 'end'
@@ -180,6 +213,8 @@ export function OverflowList({
180
213
  children,
181
214
  gap = 2,
182
215
  minVisibleItems = 0,
216
+ maxVisibleItems,
217
+ maxRows,
183
218
  collapseFrom = 'end',
184
219
  behavior = 'observeSelf',
185
220
  overflowRenderer,
@@ -195,16 +230,17 @@ export function OverflowList({
195
230
  const gapPx = spacingToPx[gap];
196
231
 
197
232
  const observeParent = behavior === 'observeParent';
233
+ const isMultiRow = maxRows != null && maxRows > 1;
198
234
 
199
- const {containerRef, measureRef, visibleCount, hasOverflow} = useOverflow(
200
- itemCount,
201
- {
235
+ const {containerRef, measureRef, visibleCount, hasOverflow, rowHeight} =
236
+ useOverflow(itemCount, {
202
237
  gap: gapPx,
203
238
  minVisibleItems,
239
+ maxVisibleItems,
240
+ maxRows,
204
241
  collapseFrom,
205
242
  behavior,
206
- },
207
- );
243
+ });
208
244
 
209
245
  const allItems: OverflowItem[] = childArray.map((child, index) => ({
210
246
  child,
@@ -248,8 +284,12 @@ export function OverflowList({
248
284
  {...mergeProps(
249
285
  themeProps('overflow-list'),
250
286
  stylex.props(
251
- styles.container,
287
+ isMultiRow ? styles.containerMultiRow : styles.container,
252
288
  gapStyles[gap],
289
+ isMultiRow &&
290
+ rowHeight > 0 &&
291
+ maxRows != null &&
292
+ multiRowHeight.height(maxRows, rowHeight, gapPx),
253
293
  observeParent && hasOverflow && styles.fillParent,
254
294
  xstyle,
255
295
  ),
@@ -13,6 +13,8 @@ import {
13
13
  import {render, screen, fireEvent, act} from '@testing-library/react';
14
14
  import React, {useState} from 'react';
15
15
  import {PowerSearch} from './PowerSearch';
16
+ import {PowerSearchEditPopover} from './PowerSearchEditPopover';
17
+ import {useInternalConfig} from './useInternalConfig';
16
18
  import type {PowerSearchConfig, PowerSearchFilter} from './types';
17
19
 
18
20
  // =============================================================================
@@ -216,4 +218,69 @@ describe('PowerSearch', () => {
216
218
  expect(popoverText).toContain('Priority');
217
219
  expect(popoverText).toContain('equals');
218
220
  });
221
+
222
+ it('does not save/close edit popover when Enter is consumed by child listbox option selection', () => {
223
+ const multiConfig: PowerSearchConfig = {
224
+ name: 'test-multi',
225
+ fields: [
226
+ {
227
+ key: 'status',
228
+ label: 'Status',
229
+ operators: [
230
+ {
231
+ key: 'any_of',
232
+ label: 'is any of',
233
+ value: {
234
+ type: 'enum_list',
235
+ values: [
236
+ {value: 'open', label: 'Open'},
237
+ {value: 'closed', label: 'Closed'},
238
+ ],
239
+ },
240
+ },
241
+ ],
242
+ },
243
+ ],
244
+ };
245
+
246
+ const onSave = vi.fn();
247
+ const onCancel = vi.fn();
248
+
249
+ function MultiSelectHarness() {
250
+ const internalConfig = useInternalConfig(multiConfig);
251
+ return (
252
+ <PowerSearchEditPopover
253
+ config={internalConfig}
254
+ filter={{
255
+ field: 'status',
256
+ operator: 'any_of',
257
+ value: {type: 'enum_list', value: ['open']},
258
+ }}
259
+ mode="edit"
260
+ onSave={onSave}
261
+ onCancel={onCancel}
262
+ />
263
+ );
264
+ }
265
+
266
+ const {container} = render(<MultiSelectHarness />);
267
+
268
+ const input = container.querySelector('input');
269
+ expect(input).not.toBeNull();
270
+
271
+ // Fire an Enter event that has been defaultPrevented (e.g. child typeahead option selection)
272
+ const enterEvent = new KeyboardEvent('keydown', {
273
+ key: 'Enter',
274
+ bubbles: true,
275
+ cancelable: true,
276
+ });
277
+ enterEvent.preventDefault();
278
+
279
+ act(() => {
280
+ input?.dispatchEvent(enterEvent);
281
+ });
282
+
283
+ // onSave should NOT be called because the event was already consumed (defaultPrevented)
284
+ expect(onSave).not.toHaveBeenCalled();
285
+ });
219
286
  });
@@ -702,7 +702,7 @@ export function PowerSearchEditPopover({
702
702
  // Handle Enter to save, Escape to cancel
703
703
  const handleKeyDown = useCallback(
704
704
  (e: React.KeyboardEvent) => {
705
- if (e.key === 'Enter' && !isSaveDisabled) {
705
+ if (e.key === 'Enter' && !isSaveDisabled && !e.defaultPrevented) {
706
706
  e.preventDefault();
707
707
  handleSave();
708
708
  } else if (e.key === 'Escape' && !e.defaultPrevented) {
@@ -76,8 +76,7 @@ export function usePowerSearchSource(
76
76
 
77
77
  // Check each field+operator combo against the query
78
78
  for (const op of field.operators) {
79
- const combinedLabel =
80
- `${field.label} ${opLabel(op)}`.toLowerCase();
79
+ const combinedLabel = `${field.label} ${opLabel(op)}`.toLowerCase();
81
80
  if (combinedLabel.includes(lower)) {
82
81
  const id = `${field.key}:${op.key}`;
83
82
  if (!seen.has(id)) {
@@ -27,6 +27,7 @@ export const docs = {
27
27
  {name: 'children', type: 'ReactNode', description: 'Card content.'},
28
28
  {name: 'padding', type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10', description: 'Inner padding.', default: '4'},
29
29
  {name: 'variant', type: "'default' | 'transparent' | 'muted' | 'blue' | 'cyan' | 'gray' | 'green' | 'orange' | 'pink' | 'purple' | 'red' | 'teal' | 'yellow'", description: 'Background color variant.', default: "'default'"},
30
+ {name: 'elevation', type: "'none' | 'low' | 'med' | 'high'", description: 'Resting shadow depth. The selection ring composes on top, so a selected card keeps its shadow.', default: "'none'"},
30
31
  {name: 'width', type: 'SizeValue', description: 'Card width.'},
31
32
  {name: 'height', type: 'SizeValue', description: 'Card height.'},
32
33
  {name: 'maxWidth', type: 'SizeValue', description: 'Maximum card width.'},
@@ -71,6 +72,7 @@ export const docsDense = {
71
72
  isDisabled: 'disables card',
72
73
  padding: 'inner padding',
73
74
  variant: 'background color variant',
75
+ elevation: 'resting shadow depth: none|low|med|high; selection ring composes on top',
74
76
  width: 'card width',
75
77
  height: 'card height',
76
78
  maxWidth: 'max card width',
@@ -85,8 +85,7 @@ describe('SelectableCard', () => {
85
85
  label="Disabled"
86
86
  isSelected={false}
87
87
  onChange={handleChange}
88
- isDisabled
89
- >
88
+ isDisabled>
90
89
  Content
91
90
  </SelectableCard>,
92
91
  );
@@ -101,12 +100,55 @@ describe('SelectableCard', () => {
101
100
  label="Disabled"
102
101
  isSelected={false}
103
102
  onChange={handleChange}
104
- isDisabled
105
- >
103
+ isDisabled>
106
104
  <span>Content</span>
107
105
  </SelectableCard>,
108
106
  );
109
107
  fireEvent.click(screen.getByText('Content'));
110
108
  expect(handleChange).not.toHaveBeenCalled();
111
109
  });
110
+
111
+ describe('elevation', () => {
112
+ const noop = () => {};
113
+
114
+ it('forwards a distinct elevation class to the card for each level', () => {
115
+ const classFor = (elevation: 'none' | 'low' | 'med' | 'high') => {
116
+ const {container} = render(
117
+ <SelectableCard
118
+ label="Card"
119
+ isSelected={false}
120
+ onChange={noop}
121
+ elevation={elevation}>
122
+ Content
123
+ </SelectableCard>,
124
+ );
125
+ return container.firstElementChild!.className;
126
+ };
127
+ const classes = new Set([
128
+ classFor('none'),
129
+ classFor('low'),
130
+ classFor('med'),
131
+ classFor('high'),
132
+ ]);
133
+ expect(classes.size).toBe(4);
134
+ });
135
+
136
+ it('still varies elevation while selected (ring composes with elevation)', () => {
137
+ const selectedClassFor = (elevation: 'none' | 'med') => {
138
+ const {container} = render(
139
+ <SelectableCard
140
+ label="Card"
141
+ isSelected
142
+ onChange={noop}
143
+ elevation={elevation}>
144
+ Content
145
+ </SelectableCard>,
146
+ );
147
+ return container.firstElementChild!.className;
148
+ };
149
+ // A selected card at 'med' must differ from a selected card at 'none' —
150
+ // proving the selection ring does not clobber the elevation shadow.
151
+ expect(selectedClassFor('med')).not.toBe(selectedClassFor('none'));
152
+ });
153
+ });
112
154
  });
@@ -14,6 +14,7 @@
14
14
  * - /apps/storybook/stories/SelectableCard.stories.tsx (storybook stories)
15
15
  * - /packages/cli/templates/blocks/components/Card/SelectableCardShowcase.tsx (showcase block)
16
16
  * - /packages/cli/templates/blocks/components/Card/SelectableCardMulti.tsx (block)
17
+ * - /packages/cli/templates/blocks/components/Card/SelectableCardElevated.tsx (block)
17
18
  *
18
19
  * Composes Card for all visual styling. Adds selection state with
19
20
  * an inset box-shadow (zero layout jitter) and useClickableContainer
@@ -36,7 +37,7 @@ import {
36
37
  import * as stylex from '@stylexjs/stylex';
37
38
  import type {StyleXStyles} from '@stylexjs/stylex';
38
39
  import {colorVars, durationVars, easeVars} from '../theme/tokens.stylex';
39
- import type {SizeValue, SpacingStep} from '../utils/types';
40
+ import type {Elevation, SizeValue, SpacingStep} from '../utils/types';
40
41
  import {mergeProps, mergeRefs} from '../utils';
41
42
  import {Card} from '../Card/Card';
42
43
  import type {CardVariant} from '../Card/Card';
@@ -103,51 +104,54 @@ const styles = stylex.create({
103
104
  whiteSpace: 'nowrap',
104
105
  borderWidth: 0,
105
106
  },
106
- // Selection indicator — inset box-shadow for the thick inner ring (zero layout
107
- // jitter) plus borderColor change on the card's own border for a cohesive look.
107
+ // Selection indicator — an inset ring drawn via the card's --_card-ring
108
+ // shadow slot (zero layout jitter) plus a borderColor change on the card's
109
+ // own border for a cohesive look. Routing the ring through --_card-ring
110
+ // (rather than box-shadow directly) lets it compose with the card's
111
+ // elevation instead of clobbering it.
108
112
  selected: {
109
113
  borderColor: colorVars['--color-accent'],
110
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-accent']}`,
114
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-accent']}`,
111
115
  },
112
116
  selectedBlue: {
113
117
  borderColor: colorVars['--color-border-blue'],
114
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-blue']}`,
118
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-blue']}`,
115
119
  },
116
120
  selectedCyan: {
117
121
  borderColor: colorVars['--color-border-cyan'],
118
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-cyan']}`,
122
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-cyan']}`,
119
123
  },
120
124
  selectedGray: {
121
125
  borderColor: colorVars['--color-border-gray'],
122
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-gray']}`,
126
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-gray']}`,
123
127
  },
124
128
  selectedGreen: {
125
129
  borderColor: colorVars['--color-border-green'],
126
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-green']}`,
130
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-green']}`,
127
131
  },
128
132
  selectedOrange: {
129
133
  borderColor: colorVars['--color-border-orange'],
130
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-orange']}`,
134
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-orange']}`,
131
135
  },
132
136
  selectedPink: {
133
137
  borderColor: colorVars['--color-border-pink'],
134
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-pink']}`,
138
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-pink']}`,
135
139
  },
136
140
  selectedPurple: {
137
141
  borderColor: colorVars['--color-border-purple'],
138
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-purple']}`,
142
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-purple']}`,
139
143
  },
140
144
  selectedRed: {
141
145
  borderColor: colorVars['--color-border-red'],
142
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-red']}`,
146
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-red']}`,
143
147
  },
144
148
  selectedTeal: {
145
149
  borderColor: colorVars['--color-border-teal'],
146
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-teal']}`,
150
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-teal']}`,
147
151
  },
148
152
  selectedYellow: {
149
153
  borderColor: colorVars['--color-border-yellow'],
150
- boxShadow: `inset 0 0 0 2px ${colorVars['--color-border-yellow']}`,
154
+ '--_card-ring': `inset 0 0 0 2px ${colorVars['--color-border-yellow']}`,
151
155
  },
152
156
  });
153
157
 
@@ -228,6 +232,13 @@ export interface SelectableCardProps extends Omit<BaseProps, 'onChange'> {
228
232
  */
229
233
  variant?: CardVariant;
230
234
 
235
+ /**
236
+ * Resting elevation — the shadow depth the card sits at.
237
+ * The selection ring composes on top, so a selected card keeps its shadow.
238
+ * @default 'none'
239
+ */
240
+ elevation?: Elevation;
241
+
231
242
  /** Width of the card. */
232
243
  width?: SizeValue;
233
244
 
@@ -278,6 +289,7 @@ export function SelectableCard({
278
289
  children,
279
290
  padding,
280
291
  variant = 'default',
292
+ elevation = 'none',
281
293
  width,
282
294
  height,
283
295
  maxWidth,
@@ -328,6 +340,7 @@ export function SelectableCard({
328
340
  maxWidth={maxWidth}
329
341
  padding={padding}
330
342
  variant={variant}
343
+ elevation={elevation}
331
344
  {...mergeProps(
332
345
  themeProps('selectable-card', {
333
346
  variant,
@@ -503,6 +503,58 @@ describe('Selector', () => {
503
503
  expect(search).toHaveAttribute('aria-activedescendant');
504
504
  });
505
505
 
506
+ it('PageDown/PageUp jump the highlight to the last/first filtered option', async () => {
507
+ const user = userEvent.setup();
508
+ render(
509
+ <Selector
510
+ label="Fruit"
511
+ options={OPTIONS}
512
+ value="Apple"
513
+ onChange={() => {}}
514
+ hasSearch
515
+ />,
516
+ );
517
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
518
+ const search = screen.getByRole('combobox', h);
519
+ // Filter to Apple and Banana so "last" means last *visible* option.
520
+ await user.type(search, 'a');
521
+ const options = screen.getAllByRole('option', h);
522
+ expect(options).toHaveLength(2);
523
+ await user.keyboard('{PageDown}');
524
+ expect(search).toHaveAttribute(
525
+ 'aria-activedescendant',
526
+ options[options.length - 1].id,
527
+ );
528
+ await user.keyboard('{PageUp}');
529
+ expect(search).toHaveAttribute('aria-activedescendant', options[0].id);
530
+ });
531
+
532
+ it('Home/End move the search caret, not the option highlight', async () => {
533
+ const user = userEvent.setup();
534
+ render(
535
+ <Selector
536
+ label="Fruit"
537
+ options={OPTIONS}
538
+ value="Apple"
539
+ onChange={() => {}}
540
+ hasSearch
541
+ />,
542
+ );
543
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
544
+ const search = screen.getByRole<HTMLInputElement>('combobox', h);
545
+ await user.type(search, 'an');
546
+ expect(search.selectionStart).toBe(2);
547
+ const activeBefore = search.getAttribute('aria-activedescendant');
548
+ // Home/End stay on the input for caret movement (APG editable
549
+ // combobox); the option highlight must not move.
550
+ await user.keyboard('{Home}');
551
+ expect(search.selectionStart).toBe(0);
552
+ expect(search.getAttribute('aria-activedescendant')).toBe(activeBefore);
553
+ await user.keyboard('{End}');
554
+ expect(search.selectionStart).toBe(2);
555
+ expect(search.getAttribute('aria-activedescendant')).toBe(activeBefore);
556
+ });
557
+
506
558
  it('does not render search input when hasSearch is false', async () => {
507
559
  const user = userEvent.setup();
508
560
  render(
@@ -722,6 +774,23 @@ describe('Selector', () => {
722
774
  expect(trigger).toHaveAttribute('aria-expanded', 'true');
723
775
  });
724
776
 
777
+ it('End/Home jump the highlight to the last/first option (non-search)', async () => {
778
+ const user = userEvent.setup();
779
+ render(<Selector label="Fruit" options={OPTIONS} />);
780
+
781
+ const trigger = screen.getByRole('combobox');
782
+ await user.click(trigger);
783
+ const options = screen.getAllByRole('option', h);
784
+
785
+ await user.keyboard('{End}');
786
+ expect(trigger).toHaveAttribute(
787
+ 'aria-activedescendant',
788
+ options[options.length - 1].id,
789
+ );
790
+ await user.keyboard('{Home}');
791
+ expect(trigger).toHaveAttribute('aria-activedescendant', options[0].id);
792
+ });
793
+
725
794
  it('opens and selects an option with Enter (no mouse)', async () => {
726
795
  const user = userEvent.setup();
727
796
  const onChange = vi.fn();
@@ -858,10 +858,14 @@ export function Selector<T extends SelectorOptionType>(
858
858
  onChange={handleSearchChange}
859
859
  onKeyDown={e => {
860
860
  // Arrow keys navigate options; Enter selects; Escape/Tab close.
861
- // Home/End are left to the input for caret movement.
861
+ // Home/End are left to the input for caret movement (APG editable
862
+ // combobox); PageUp/PageDown are the sanctioned substitute for
863
+ // jumping to the first/last option.
862
864
  if (
863
865
  e.key === 'ArrowDown' ||
864
866
  e.key === 'ArrowUp' ||
867
+ e.key === 'PageUp' ||
868
+ e.key === 'PageDown' ||
865
869
  e.key === 'Enter' ||
866
870
  e.key === 'Escape' ||
867
871
  e.key === 'Tab'
@@ -20,7 +20,7 @@ export const docs = {
20
20
  name: 'icon',
21
21
  type: 'IconType',
22
22
  description:
23
- 'Icon displayed before the label. See `npx astryx docs icons` for valid semantic names.',
23
+ 'Icon displayed before the label. See `astryx docs icons` for valid semantic names.',
24
24
  },
25
25
  {
26
26
  name: 'description',
@@ -318,6 +318,23 @@ export function useCombobox({
318
318
  }
319
319
  break;
320
320
 
321
+ // PageUp/PageDown mirror Home/End. In search mode Home/End stay on
322
+ // the input for caret movement (APG editable combobox), so these are
323
+ // the sanctioned substitute for jumping to the first/last option.
324
+ case 'PageUp':
325
+ e.preventDefault();
326
+ if (isOpen && enabledIndices.length > 0) {
327
+ setHighlightedIndex(enabledIndices[0]);
328
+ }
329
+ break;
330
+
331
+ case 'PageDown':
332
+ e.preventDefault();
333
+ if (isOpen && enabledIndices.length > 0) {
334
+ setHighlightedIndex(enabledIndices[enabledIndices.length - 1]);
335
+ }
336
+ break;
337
+
321
338
  case 'Delete':
322
339
  case 'Backspace':
323
340
  // Keyboard equivalent of the clear button (comboboxes-2): clear the