@astryxdesign/core 0.4.7 → 0.5.0-canary.055e75f

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 (597) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +45 -20
  5. package/dist/AppShell/AppShell.d.ts.map +1 -1
  6. package/dist/AppShell/AppShell.js +3 -2
  7. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  8. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  9. package/dist/AspectRatio/AspectRatio.js +23 -4
  10. package/dist/Avatar/Avatar.d.ts +25 -1
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +58 -19
  13. package/dist/Avatar/index.d.ts +2 -2
  14. package/dist/Avatar/index.d.ts.map +1 -1
  15. package/dist/Avatar/index.js +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  17. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroup.js +8 -4
  19. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  20. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  21. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  22. package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
  23. package/dist/Banner/Banner.d.ts +40 -15
  24. package/dist/Banner/Banner.d.ts.map +1 -1
  25. package/dist/Banner/Banner.js +51 -18
  26. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  27. package/dist/BottomSheet/BottomSheet.js +17 -4
  28. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  29. package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
  30. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  31. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  32. package/dist/Button/Button.d.ts.map +1 -1
  33. package/dist/Button/Button.js +11 -6
  34. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  35. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  36. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  37. package/dist/Calendar/Calendar.d.ts +3 -1
  38. package/dist/Calendar/Calendar.d.ts.map +1 -1
  39. package/dist/Calendar/Calendar.js +14 -15
  40. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  41. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  42. package/dist/Calendar/getInitialFocusDate.js +62 -0
  43. package/dist/Calendar/styles.d.ts +5 -14
  44. package/dist/Calendar/styles.d.ts.map +1 -1
  45. package/dist/Calendar/styles.js +15 -15
  46. package/dist/Card/Card.d.ts +6 -9
  47. package/dist/Card/Card.d.ts.map +1 -1
  48. package/dist/Card/Card.js +6 -6
  49. package/dist/Carousel/Carousel.d.ts.map +1 -1
  50. package/dist/Carousel/Carousel.js +3 -3
  51. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  52. package/dist/Chat/ChatComposerInput.js +2 -3
  53. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  54. package/dist/Chat/ChatLayout.js +3 -3
  55. package/dist/Chat/ChatMessageList.d.ts +22 -3
  56. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  57. package/dist/Chat/ChatMessageList.js +6 -3
  58. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  59. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  60. package/dist/Chat/ChatTokenizedText.js +11 -6
  61. package/dist/Chat/useChatComposerTokens.js +1 -1
  62. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  63. package/dist/CheckboxInput/CheckboxInput.js +10 -3
  64. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  65. package/dist/ClickableCard/ClickableCard.js +3 -3
  66. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  67. package/dist/CodeBlock/CodeBlock.js +15 -4
  68. package/dist/CodeBlock/highlightRanges.js +1 -1
  69. package/dist/CodeBlock/highlightStyles.js +13 -13
  70. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  71. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  72. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  73. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  74. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  75. package/dist/ContextMenu/ContextMenu.js +4 -3
  76. package/dist/DateInput/DateInput.d.ts +43 -0
  77. package/dist/DateInput/DateInput.d.ts.map +1 -1
  78. package/dist/DateInput/DateInput.js +22 -2
  79. package/dist/DateInput/NativeDateField.d.ts +10 -0
  80. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  81. package/dist/DateInput/NativeDateField.js +494 -0
  82. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  83. package/dist/DateInput/TouchDateField.js +82 -23
  84. package/dist/DateInput/index.d.ts +1 -1
  85. package/dist/DateInput/index.d.ts.map +1 -1
  86. package/dist/DateInput/nativeDateSegments.d.ts +22 -0
  87. package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
  88. package/dist/DateInput/nativeDateSegments.js +111 -0
  89. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  90. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  91. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  92. package/dist/DateTimeInput/index.d.ts +1 -1
  93. package/dist/DateTimeInput/index.d.ts.map +1 -1
  94. package/dist/Dialog/Dialog.d.ts +21 -1
  95. package/dist/Dialog/Dialog.d.ts.map +1 -1
  96. package/dist/Dialog/Dialog.js +110 -58
  97. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  98. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  99. package/dist/Field/FieldLabel.d.ts.map +1 -1
  100. package/dist/Field/FieldLabel.js +9 -1
  101. package/dist/FileInput/FileInput.d.ts.map +1 -1
  102. package/dist/FileInput/FileInput.js +3 -2
  103. package/dist/Heading/Heading.d.ts.map +1 -1
  104. package/dist/Heading/Heading.js +6 -2
  105. package/dist/HoverCard/HoverCard.d.ts +6 -0
  106. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  107. package/dist/HoverCard/HoverCard.js +104 -3
  108. package/dist/HoverCard/useHoverCard.d.ts +19 -2
  109. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  110. package/dist/HoverCard/useHoverCard.js +74 -26
  111. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  112. package/dist/Indicator/CheckboxIndicator.js +6 -2
  113. package/dist/Item/Item.d.ts.map +1 -1
  114. package/dist/Item/Item.js +4 -2
  115. package/dist/Kbd/Kbd.d.ts.map +1 -1
  116. package/dist/Kbd/Kbd.js +2 -19
  117. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  118. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  119. package/dist/Layer/LayerDepthContext.js +66 -0
  120. package/dist/Layer/index.d.ts +3 -0
  121. package/dist/Layer/index.d.ts.map +1 -1
  122. package/dist/Layer/index.js +4 -0
  123. package/dist/Layer/layerStack.d.ts +98 -0
  124. package/dist/Layer/layerStack.d.ts.map +1 -0
  125. package/dist/Layer/layerStack.js +306 -0
  126. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  127. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  128. package/dist/Layer/useLayerDismissal.js +91 -0
  129. package/dist/Lightbox/Lightbox.d.ts +1 -0
  130. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  131. package/dist/Lightbox/Lightbox.js +103 -81
  132. package/dist/Markdown/Markdown.d.ts.map +1 -1
  133. package/dist/Markdown/Markdown.js +9 -4
  134. package/dist/Markdown/index.d.ts +1 -1
  135. package/dist/Markdown/index.d.ts.map +1 -1
  136. package/dist/Markdown/parser.d.ts +38 -1
  137. package/dist/Markdown/parser.d.ts.map +1 -1
  138. package/dist/Markdown/parser.js +182 -32
  139. package/dist/MobileNav/MobileNav.d.ts +6 -1
  140. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  141. package/dist/MobileNav/MobileNav.js +61 -36
  142. package/dist/MultiSelector/MultiSelector.d.ts +32 -1
  143. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  144. package/dist/MultiSelector/MultiSelector.js +63 -11
  145. package/dist/MultiSelector/index.d.ts +1 -1
  146. package/dist/MultiSelector/index.d.ts.map +1 -1
  147. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  148. package/dist/NavIcon/NavIcon.js +5 -1
  149. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  150. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  151. package/dist/NumberInput/NumberInput.d.ts +6 -1
  152. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  153. package/dist/NumberInput/NumberInput.js +54 -104
  154. package/dist/NumberInput/numberInputCommit.d.ts +34 -0
  155. package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
  156. package/dist/NumberInput/numberInputCommit.js +71 -0
  157. package/dist/NumberInput/numberParser.d.ts +64 -0
  158. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  159. package/dist/NumberInput/numberParser.js +350 -0
  160. package/dist/Outline/Outline.d.ts.map +1 -1
  161. package/dist/Outline/Outline.js +3 -2
  162. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  163. package/dist/OverflowList/OverflowList.js +3 -2
  164. package/dist/Overlay/Overlay.d.ts.map +1 -1
  165. package/dist/Overlay/Overlay.js +3 -2
  166. package/dist/Popover/usePopover.d.ts.map +1 -1
  167. package/dist/Popover/usePopover.js +20 -17
  168. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  169. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  170. package/dist/PowerSearch/PowerSearch.js +19 -6
  171. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  172. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  173. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  174. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  175. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  176. package/dist/PowerSearch/PowerSearchValueEditor.js +110 -27
  177. package/dist/PowerSearch/types.d.ts +2 -0
  178. package/dist/PowerSearch/types.d.ts.map +1 -1
  179. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  180. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  181. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  182. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  183. package/dist/ProgressBar/ProgressBar.js +18 -5
  184. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  185. package/dist/Resizable/ResizeHandle.js +3 -2
  186. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  187. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  188. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  189. package/dist/SelectableCard/SelectableCard.js +3 -3
  190. package/dist/Selector/Selector.d.ts +17 -0
  191. package/dist/Selector/Selector.d.ts.map +1 -1
  192. package/dist/Selector/Selector.js +81 -14
  193. package/dist/SideNav/SideNav.d.ts.map +1 -1
  194. package/dist/SideNav/SideNav.js +4 -3
  195. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  196. package/dist/SideNav/SideNavHeading.js +8 -7
  197. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  198. package/dist/SideNav/SideNavItem.js +4 -3
  199. package/dist/Slider/Slider.d.ts.map +1 -1
  200. package/dist/Slider/Slider.js +37 -5
  201. package/dist/Spinner/Spinner.d.ts +3 -3
  202. package/dist/Spinner/Spinner.d.ts.map +1 -1
  203. package/dist/Spinner/Spinner.js +130 -78
  204. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  205. package/dist/StatusDot/StatusDot.js +6 -1
  206. package/dist/Stepper/Step.d.ts +140 -0
  207. package/dist/Stepper/Step.d.ts.map +1 -0
  208. package/dist/Stepper/Step.js +1045 -0
  209. package/dist/Stepper/StepStatus.d.ts +24 -0
  210. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  211. package/dist/Stepper/StepStatus.js +1 -0
  212. package/dist/Stepper/Stepper.d.ts +93 -0
  213. package/dist/Stepper/Stepper.d.ts.map +1 -0
  214. package/dist/Stepper/Stepper.js +184 -0
  215. package/dist/Stepper/StepperContext.d.ts +41 -0
  216. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  217. package/dist/Stepper/StepperContext.js +34 -0
  218. package/dist/Stepper/index.d.ts +8 -0
  219. package/dist/Stepper/index.d.ts.map +1 -0
  220. package/dist/Stepper/index.js +7 -0
  221. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  222. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  223. package/dist/Stepper/stepper.stylex.js +20 -0
  224. package/dist/Switch/Switch.d.ts.map +1 -1
  225. package/dist/Switch/Switch.js +8 -3
  226. package/dist/TabList/Tab.d.ts +15 -2
  227. package/dist/TabList/Tab.d.ts.map +1 -1
  228. package/dist/TabList/Tab.js +32 -7
  229. package/dist/TabList/TabList.d.ts +46 -7
  230. package/dist/TabList/TabList.d.ts.map +1 -1
  231. package/dist/TabList/TabList.js +353 -23
  232. package/dist/TabList/TabListContext.d.ts +11 -1
  233. package/dist/TabList/TabListContext.d.ts.map +1 -1
  234. package/dist/TabList/TabListContext.js +11 -2
  235. package/dist/TabList/TabMenu.d.ts.map +1 -1
  236. package/dist/TabList/TabMenu.js +3 -2
  237. package/dist/TabList/index.d.ts +2 -2
  238. package/dist/TabList/index.d.ts.map +1 -1
  239. package/dist/Table/BaseTable.d.ts.map +1 -1
  240. package/dist/Table/BaseTable.js +10 -2
  241. package/dist/Table/Table.d.ts.map +1 -1
  242. package/dist/Table/Table.js +3 -6
  243. package/dist/Table/TableRow.d.ts +16 -5
  244. package/dist/Table/TableRow.d.ts.map +1 -1
  245. package/dist/Table/TableRow.js +16 -5
  246. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  247. package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
  248. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  249. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  250. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  251. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  252. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  253. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  254. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  255. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  256. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  257. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  258. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  259. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  260. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  261. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  262. package/dist/Table/types.d.ts +22 -4
  263. package/dist/Table/types.d.ts.map +1 -1
  264. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  265. package/dist/Table/useBaseTablePlugins.js +5 -0
  266. package/dist/Text/Text.d.ts.map +1 -1
  267. package/dist/Text/Text.js +6 -2
  268. package/dist/TextArea/TextArea.d.ts.map +1 -1
  269. package/dist/TextArea/TextArea.js +9 -3
  270. package/dist/TextInput/TextInput.d.ts.map +1 -1
  271. package/dist/TextInput/TextInput.js +3 -2
  272. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  273. package/dist/TimeInput/TimeInput.js +3 -2
  274. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  275. package/dist/Timestamp/Timestamp.js +5 -3
  276. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  277. package/dist/Toast/ToastViewport.js +27 -8
  278. package/dist/Token/TokenLink.d.ts.map +1 -1
  279. package/dist/Token/TokenLink.js +2 -2
  280. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  281. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  282. package/dist/Tokenizer/Tokenizer.js +2 -0
  283. package/dist/Tooltip/Tooltip.d.ts +5 -0
  284. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  285. package/dist/Tooltip/useTooltip.d.ts +5 -0
  286. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  287. package/dist/Tooltip/useTooltip.js +39 -24
  288. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  289. package/dist/TopNav/TopNavHeading.js +5 -4
  290. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  291. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  292. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  293. package/dist/TopNav/TopNavMenu.js +4 -3
  294. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  295. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  296. package/dist/Typeahead/BaseTypeahead.js +68 -29
  297. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  298. package/dist/Typeahead/Typeahead.js +3 -2
  299. package/dist/astryx.css +98 -7
  300. package/dist/hooks/index.d.ts +1 -0
  301. package/dist/hooks/index.d.ts.map +1 -1
  302. package/dist/hooks/index.js +1 -0
  303. package/dist/hooks/useFocusTrap.d.ts +7 -4
  304. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  305. package/dist/hooks/useFocusTrap.js +60 -95
  306. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  307. package/dist/hooks/useHotkeys.js +3 -18
  308. package/dist/hooks/useMergedRefs.d.ts +18 -0
  309. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  310. package/dist/hooks/useMergedRefs.js +25 -0
  311. package/dist/index.d.ts +1 -0
  312. package/dist/index.d.ts.map +1 -1
  313. package/dist/index.js +1 -0
  314. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  315. package/dist/theme/derivedVarRegistry.js +24 -4
  316. package/dist/theme/syntax/tokens.d.ts +1 -1
  317. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  318. package/dist/theme/syntax/tokens.js +5 -2
  319. package/dist/utils/groupItems.d.ts +5 -1
  320. package/dist/utils/groupItems.d.ts.map +1 -1
  321. package/dist/utils/groupItems.js +9 -9
  322. package/dist/utils/index.d.ts +1 -1
  323. package/dist/utils/index.d.ts.map +1 -1
  324. package/dist/utils/isApplePlatform.d.ts +18 -0
  325. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  326. package/dist/utils/isApplePlatform.js +37 -0
  327. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  328. package/dist/utils/sharedResizeObserver.js +10 -1
  329. package/dist/utils/timeParser.d.ts +1 -1
  330. package/dist/utils/timeParser.d.ts.map +1 -1
  331. package/dist/utils/timeParser.js +26 -12
  332. package/locales/en.json +154 -10
  333. package/locales/pseudo.json +108 -0
  334. package/package.json +8 -3
  335. package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
  336. package/src/AlertDialog/AlertDialog.test.tsx +211 -2
  337. package/src/AlertDialog/AlertDialog.tsx +62 -20
  338. package/src/AppShell/AppShell.tsx +3 -2
  339. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  340. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  341. package/src/AspectRatio/AspectRatio.tsx +40 -1
  342. package/src/Avatar/Avatar.doc.mjs +14 -3
  343. package/src/Avatar/Avatar.test.tsx +97 -0
  344. package/src/Avatar/Avatar.tsx +125 -52
  345. package/src/Avatar/index.ts +2 -2
  346. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  347. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  348. package/src/AvatarGroup/AvatarGroup.tsx +15 -6
  349. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  350. package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
  351. package/src/Banner/Banner.doc.mjs +13 -11
  352. package/src/Banner/Banner.test.tsx +102 -15
  353. package/src/Banner/Banner.tsx +72 -25
  354. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  355. package/src/BottomSheet/BottomSheet.tsx +17 -4
  356. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  357. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  358. package/src/Button/Button.doc.mjs +24 -1
  359. package/src/Button/Button.test.tsx +32 -4
  360. package/src/Button/Button.tsx +12 -5
  361. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  362. package/src/ButtonGroup/ButtonGroup.test.tsx +404 -1
  363. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  364. package/src/Calendar/Calendar.doc.mjs +4 -3
  365. package/src/Calendar/Calendar.test.tsx +103 -0
  366. package/src/Calendar/Calendar.tsx +19 -16
  367. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  368. package/src/Calendar/getInitialFocusDate.ts +93 -0
  369. package/src/Calendar/styles.ts +49 -31
  370. package/src/Card/Card.doc.mjs +10 -7
  371. package/src/Card/Card.test.tsx +44 -19
  372. package/src/Card/Card.tsx +12 -32
  373. package/src/Carousel/Carousel.tsx +3 -2
  374. package/src/Chat/ChatComposerInput.tsx +5 -1
  375. package/src/Chat/ChatLayout.tsx +3 -2
  376. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  377. package/src/Chat/ChatMessageList.test.tsx +46 -0
  378. package/src/Chat/ChatMessageList.tsx +28 -4
  379. package/src/Chat/ChatTokenizedText.tsx +17 -10
  380. package/src/Chat/useChatComposerTokens.ts +1 -1
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  383. package/src/CheckboxInput/CheckboxInput.tsx +8 -2
  384. package/src/ClickableCard/ClickableCard.tsx +3 -2
  385. package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
  386. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  387. package/src/CodeBlock/CodeBlock.tsx +20 -4
  388. package/src/CodeBlock/highlightRanges.ts +1 -1
  389. package/src/CodeBlock/highlightStyles.ts +13 -13
  390. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  391. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  392. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  393. package/src/ContextMenu/ContextMenu.tsx +4 -3
  394. package/src/DateInput/DateInput.doc.mjs +16 -0
  395. package/src/DateInput/DateInput.tsx +55 -2
  396. package/src/DateInput/DateInputTouch.test.tsx +146 -32
  397. package/src/DateInput/NativeDateField.test.tsx +635 -0
  398. package/src/DateInput/NativeDateField.tsx +641 -0
  399. package/src/DateInput/TouchDateField.tsx +108 -24
  400. package/src/DateInput/index.ts +1 -0
  401. package/src/DateInput/nativeDateSegments.ts +127 -0
  402. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  403. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  404. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  405. package/src/DateTimeInput/index.ts +1 -0
  406. package/src/Dialog/Dialog.doc.mjs +8 -3
  407. package/src/Dialog/Dialog.test.tsx +429 -2
  408. package/src/Dialog/Dialog.tsx +133 -58
  409. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  410. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  411. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  412. package/src/Field/Field.doc.mjs +13 -2
  413. package/src/Field/FieldLabel.test.tsx +29 -0
  414. package/src/Field/FieldLabel.tsx +7 -0
  415. package/src/FileInput/FileInput.tsx +3 -2
  416. package/src/Heading/Heading.test.tsx +23 -1
  417. package/src/Heading/Heading.tsx +6 -2
  418. package/src/HoverCard/HoverCard.doc.mjs +13 -2
  419. package/src/HoverCard/HoverCard.test.tsx +154 -4
  420. package/src/HoverCard/HoverCard.tsx +143 -3
  421. package/src/HoverCard/useHoverCard.tsx +104 -33
  422. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  423. package/src/Indicator/Indicator.doc.mjs +2 -0
  424. package/src/Item/Item.tsx +5 -4
  425. package/src/Kbd/Kbd.tsx +2 -19
  426. package/src/Layer/LayerDepthContext.tsx +62 -0
  427. package/src/Layer/index.ts +9 -0
  428. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  429. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  430. package/src/Layer/layerStack.ts +362 -0
  431. package/src/Layer/useLayer.test.tsx +62 -14
  432. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  433. package/src/Layer/useLayerDismissal.ts +173 -0
  434. package/src/Layout/Layout.doc.mjs +2 -2
  435. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  436. package/src/Lightbox/Lightbox.tsx +124 -102
  437. package/src/Link/Link.doc.mjs +39 -0
  438. package/src/Markdown/Markdown.doc.mjs +5 -0
  439. package/src/Markdown/Markdown.tsx +9 -4
  440. package/src/Markdown/incremental.test.ts +16 -2
  441. package/src/Markdown/index.ts +1 -0
  442. package/src/Markdown/parser.perf.test.ts +96 -1
  443. package/src/Markdown/parser.test.ts +178 -2
  444. package/src/Markdown/parser.ts +253 -32
  445. package/src/MobileNav/MobileNav.tsx +62 -36
  446. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  447. package/src/MultiSelector/MultiSelector.doc.mjs +49 -1
  448. package/src/MultiSelector/MultiSelector.test.tsx +277 -0
  449. package/src/MultiSelector/MultiSelector.tsx +142 -11
  450. package/src/MultiSelector/index.ts +1 -0
  451. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  452. package/src/NavIcon/NavIcon.test.tsx +9 -0
  453. package/src/NavIcon/NavIcon.tsx +5 -1
  454. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  455. package/src/NumberInput/NumberInput.doc.mjs +31 -7
  456. package/src/NumberInput/NumberInput.test.tsx +436 -8
  457. package/src/NumberInput/NumberInput.tsx +62 -114
  458. package/src/NumberInput/numberInputCommit.test.ts +72 -0
  459. package/src/NumberInput/numberInputCommit.ts +103 -0
  460. package/src/NumberInput/numberParser.docblock.test.ts +234 -0
  461. package/src/NumberInput/numberParser.test.ts +222 -0
  462. package/src/NumberInput/numberParser.ts +403 -0
  463. package/src/Outline/Outline.tsx +3 -2
  464. package/src/OverflowList/OverflowList.tsx +3 -2
  465. package/src/Overlay/Overlay.tsx +3 -2
  466. package/src/Pagination/Pagination.test.tsx +3 -2
  467. package/src/Popover/usePopover.tsx +31 -28
  468. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  469. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  470. package/src/PowerSearch/PowerSearch.tsx +34 -5
  471. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +288 -0
  472. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  473. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +111 -1
  474. package/src/PowerSearch/PowerSearchValueEditor.tsx +124 -10
  475. package/src/PowerSearch/types.ts +2 -0
  476. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  477. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  478. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  479. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  480. package/src/ProgressBar/ProgressBar.tsx +28 -8
  481. package/src/Resizable/ResizeHandle.tsx +3 -2
  482. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  483. package/src/SelectableCard/SelectableCard.tsx +3 -2
  484. package/src/Selector/Selector.doc.mjs +26 -1
  485. package/src/Selector/Selector.test.tsx +311 -1
  486. package/src/Selector/Selector.tsx +139 -16
  487. package/src/SideNav/SideNav.doc.mjs +6 -6
  488. package/src/SideNav/SideNav.tsx +4 -2
  489. package/src/SideNav/SideNavHeading.tsx +12 -12
  490. package/src/SideNav/SideNavItem.tsx +4 -2
  491. package/src/Slider/Slider.test.tsx +106 -1
  492. package/src/Slider/Slider.tsx +55 -3
  493. package/src/Spinner/Spinner.test.tsx +147 -1
  494. package/src/Spinner/Spinner.tsx +120 -101
  495. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  496. package/src/StatusDot/StatusDot.test.tsx +9 -0
  497. package/src/StatusDot/StatusDot.tsx +7 -1
  498. package/src/Stepper/Step.doc.mjs +98 -0
  499. package/src/Stepper/Step.tsx +1638 -0
  500. package/src/Stepper/StepStatus.ts +26 -0
  501. package/src/Stepper/Stepper.doc.mjs +370 -0
  502. package/src/Stepper/Stepper.test.tsx +1024 -0
  503. package/src/Stepper/Stepper.tsx +258 -0
  504. package/src/Stepper/StepperContext.ts +71 -0
  505. package/src/Stepper/index.ts +18 -0
  506. package/src/Stepper/stepper.stylex.ts +19 -0
  507. package/src/Switch/Switch.doc.mjs +2 -0
  508. package/src/Switch/Switch.test.tsx +10 -0
  509. package/src/Switch/Switch.tsx +6 -2
  510. package/src/TabList/Tab.doc.mjs +13 -1
  511. package/src/TabList/Tab.tsx +67 -7
  512. package/src/TabList/TabList.doc.mjs +22 -6
  513. package/src/TabList/TabList.test.tsx +559 -0
  514. package/src/TabList/TabList.tsx +490 -22
  515. package/src/TabList/TabListContext.ts +13 -2
  516. package/src/TabList/TabMenu.tsx +3 -2
  517. package/src/TabList/index.ts +6 -2
  518. package/src/Table/BaseTable.tsx +12 -4
  519. package/src/Table/Table.doc.mjs +9 -2
  520. package/src/Table/Table.test.tsx +132 -14
  521. package/src/Table/Table.tsx +3 -8
  522. package/src/Table/TableBody.doc.mjs +40 -0
  523. package/src/Table/TableFooter.doc.mjs +40 -0
  524. package/src/Table/TableHeader.doc.mjs +40 -0
  525. package/src/Table/TableRow.tsx +16 -5
  526. package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
  527. package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
  528. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  529. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  530. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  531. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  532. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  533. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  534. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  535. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  536. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  537. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  539. package/src/Table/types.ts +22 -4
  540. package/src/Table/useBaseTablePlugins.ts +5 -0
  541. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  542. package/src/Table/useTablePagination.doc.mjs +7 -0
  543. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  544. package/src/Table/useTableSelection.doc.mjs +31 -0
  545. package/src/Text/Text.test.tsx +23 -1
  546. package/src/Text/Text.tsx +6 -2
  547. package/src/TextArea/TextArea.doc.mjs +8 -2
  548. package/src/TextArea/TextArea.test.tsx +11 -0
  549. package/src/TextArea/TextArea.tsx +15 -8
  550. package/src/TextInput/TextInput.doc.mjs +10 -0
  551. package/src/TextInput/TextInput.tsx +3 -2
  552. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  553. package/src/TimeInput/TimeInput.tsx +2 -2
  554. package/src/Timestamp/Timestamp.test.tsx +18 -0
  555. package/src/Timestamp/Timestamp.tsx +4 -2
  556. package/src/Toast/Toast.doc.mjs +6 -0
  557. package/src/Toast/ToastViewport.tsx +17 -1
  558. package/src/Token/TokenLink.tsx +2 -2
  559. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  560. package/src/Tokenizer/Tokenizer.tsx +4 -0
  561. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  562. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  563. package/src/Tooltip/Tooltip.test.tsx +32 -0
  564. package/src/Tooltip/Tooltip.tsx +5 -0
  565. package/src/Tooltip/useTooltip.tsx +47 -24
  566. package/src/TopNav/TopNavHeading.tsx +5 -4
  567. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  568. package/src/TopNav/TopNavMenu.tsx +4 -3
  569. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  570. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  571. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  572. package/src/Typeahead/Typeahead.test.tsx +49 -0
  573. package/src/Typeahead/Typeahead.tsx +3 -2
  574. package/src/hooks/index.ts +2 -0
  575. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  576. package/src/hooks/useFocusTrap.test.tsx +14 -0
  577. package/src/hooks/useFocusTrap.ts +62 -111
  578. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  579. package/src/hooks/useHotkeys.ts +3 -18
  580. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  581. package/src/hooks/useMergedRefs.test.tsx +62 -0
  582. package/src/hooks/useMergedRefs.ts +36 -0
  583. package/src/index.ts +1 -0
  584. package/src/raw-import.d.ts +14 -0
  585. package/src/theme/MediaTheme.doc.mjs +5 -5
  586. package/src/theme/derivedVarRegistry.test.ts +19 -3
  587. package/src/theme/derivedVarRegistry.ts +23 -4
  588. package/src/theme/generateThemeRules.test.ts +37 -0
  589. package/src/theme/syntax/tokens.ts +5 -2
  590. package/src/utils/groupItems.ts +12 -6
  591. package/src/utils/index.ts +1 -1
  592. package/src/utils/isApplePlatform.test.ts +61 -0
  593. package/src/utils/isApplePlatform.ts +37 -0
  594. package/src/utils/sharedResizeObserver.ts +10 -2
  595. package/src/utils/timeParser.test.ts +6 -1
  596. package/src/utils/timeParser.ts +35 -13
  597. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -41,12 +41,13 @@ import {
41
41
  fontWeightVars,
42
42
  typeScaleVars,
43
43
  } from '../theme/tokens.stylex';
44
- import {getKey, mergeProps, mergeRefs} from '../utils';
44
+ import {getKey, groupItems, mergeProps} from '../utils';
45
45
  import type {BaseProps} from '../BaseProps';
46
46
  import type {SearchableItem, SearchSource} from './types';
47
47
  import {themeProps} from '../utils/themeProps';
48
48
  import {useTranslator} from '../i18n';
49
49
 
50
+ import {useMergedRefs} from '../hooks/useMergedRefs';
50
51
  // =============================================================================
51
52
  // Types
52
53
  // =============================================================================
@@ -93,6 +94,9 @@ export interface BaseTypeaheadProps<T extends SearchableItem> extends Omit<
93
94
  */
94
95
  maxMenuItems?: number;
95
96
 
97
+ /** Fixed dropdown width in pixels. Never shrinks below the anchor width. */
98
+ menuWidth?: number;
99
+
96
100
  /**
97
101
  * Text shown when no results found.
98
102
  * @default 'No results found'
@@ -226,6 +230,18 @@ const styles = stylex.create({
226
230
  popover: {
227
231
  minWidth: 'anchor-size(width)',
228
232
  },
233
+ popoverCustomWidth: (width: number) => ({
234
+ width: `${width}px`,
235
+ }),
236
+ groupHeading: {
237
+ paddingInline: spacingVars['--spacing-2'],
238
+ paddingBlockStart: spacingVars['--spacing-2'],
239
+ paddingBlockEnd: spacingVars['--spacing-1'],
240
+ fontSize: typeScaleVars['--text-supporting-size'],
241
+ lineHeight: typeScaleVars['--text-supporting-leading'],
242
+ color: colorVars['--color-text-secondary'],
243
+ userSelect: 'none',
244
+ },
229
245
  item: {
230
246
  boxSizing: 'border-box',
231
247
  display: 'flex',
@@ -316,6 +332,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
316
332
  placeholder: placeholderFromProps,
317
333
  hasEntriesOnFocus = false,
318
334
  maxMenuItems = 10,
335
+ menuWidth,
319
336
  emptySearchResultsText: emptySearchResultsTextFromProps,
320
337
  isDisabled = false,
321
338
  isFocusableDisabled = false,
@@ -691,6 +708,14 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
691
708
  e.preventDefault();
692
709
  popover.hide();
693
710
  break;
711
+ case 'Tab':
712
+ // Dismiss here rather than from the blur this press produces:
713
+ // hiding a top-layer popover during the focusout makes Chrome
714
+ // abandon the in-flight focus move and drop focus to <body>, so the
715
+ // user's Tab appears to do nothing. Selector and MultiSelector
716
+ // already dismiss on this keydown.
717
+ popover.hide();
718
+ break;
694
719
  case 'Home':
695
720
  if (popover.isOpen) {
696
721
  e.preventDefault();
@@ -760,7 +785,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
760
785
  return (
761
786
  <>
762
787
  <input
763
- ref={mergeRefs(ref, inputRef, fallbackAnchorRef)}
788
+ ref={useMergedRefs(ref, inputRef, fallbackAnchorRef)}
764
789
  id={inputId}
765
790
  type="text"
766
791
  role="combobox"
@@ -835,44 +860,71 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
835
860
  {emptySearchResultsText}
836
861
  </div>
837
862
  ) : (
838
- results.map((item, index) => {
839
- const itemKey = getKey(item.id, index);
840
- const isSelected = itemKey === selectedKey;
841
- return (
842
- <div
843
- key={itemKey}
844
- id={getItemId(index)}
845
- role="option"
846
- aria-selected={isSelected}
847
- tabIndex={-1}
848
- onClick={() => handleSelect(item)}
849
- onMouseEnter={() => setHighlightedIndex(index)}
850
- {...stylex.props(
851
- styles.item,
852
- itemSizeStyles[size],
853
- index === highlightedIndex && styles.itemHighlighted,
854
- isSelected && styles.itemSelected,
855
- )}>
856
- <span {...stylex.props(styles.itemContent)}>
857
- {renderItem ? (
858
- renderItem(item)
859
- ) : (
860
- <TypeaheadItem item={item} />
863
+ (() => {
864
+ let flatIndex = 0;
865
+ const renderOption = (item: T) => {
866
+ const index = flatIndex++;
867
+ const itemKey = getKey(item.id, index);
868
+ const isSelected = itemKey === selectedKey;
869
+ return (
870
+ <div
871
+ key={itemKey}
872
+ id={getItemId(index)}
873
+ role="option"
874
+ aria-selected={isSelected}
875
+ tabIndex={-1}
876
+ onClick={() => handleSelect(item)}
877
+ onMouseEnter={() => setHighlightedIndex(index)}
878
+ {...stylex.props(
879
+ styles.item,
880
+ itemSizeStyles[size],
881
+ index === highlightedIndex && styles.itemHighlighted,
882
+ isSelected && styles.itemSelected,
883
+ )}>
884
+ <span {...stylex.props(styles.itemContent)}>
885
+ {renderItem ? (
886
+ renderItem(item)
887
+ ) : (
888
+ <TypeaheadItem item={item} />
889
+ )}
890
+ </span>
891
+ {isSelected && (
892
+ <Icon icon="check" size="sm" color="primary" />
861
893
  )}
862
- </span>
863
- {isSelected && (
864
- <Icon icon="check" size="sm" color="primary" />
865
- )}
866
- </div>
867
- );
868
- })
894
+ </div>
895
+ );
896
+ };
897
+
898
+ return groupItems(results, {ungroupedFirst: true}).map(group => {
899
+ const options = group.items.map(renderOption);
900
+ if (group.heading == null) {
901
+ return options;
902
+ }
903
+ return (
904
+ <div
905
+ key={`group-${group.heading}`}
906
+ role="group"
907
+ aria-label={group.heading}>
908
+ <div
909
+ aria-hidden="true"
910
+ {...stylex.props(styles.groupHeading)}>
911
+ {group.heading}
912
+ </div>
913
+ {options}
914
+ </div>
915
+ );
916
+ });
917
+ })()
869
918
  )}
870
919
  </div>,
871
920
  {
872
921
  placement: 'below',
873
922
  alignment: 'start',
874
923
  offset: spacingVars['--spacing-1'],
875
- xstyle: styles.popover,
924
+ xstyle: [
925
+ styles.popover,
926
+ menuWidth != null && styles.popoverCustomWidth(menuWidth),
927
+ ],
876
928
  },
877
929
  )}
878
930
  </>
@@ -171,6 +171,11 @@ export const docs = {
171
171
  description:
172
172
  'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
173
173
  },
174
+ {
175
+ name: 'startIcon',
176
+ type: 'IconType | ReactNode',
177
+ description: 'SVG icon component displayed at the start of the input.',
178
+ },
174
179
  {
175
180
  name: 'xstyle',
176
181
  type: 'StyleXStyles',
@@ -420,6 +420,55 @@ describe('BaseTypeahead focus-out', () => {
420
420
  });
421
421
  });
422
422
 
423
+ it('closes the list on the Tab keydown, before the blur it produces', async () => {
424
+ render(
425
+ <BaseTypeahead
426
+ searchSource={fruitSource}
427
+ value={null}
428
+ onChange={() => {}}
429
+ debounceMs={0}
430
+ />,
431
+ );
432
+ const input = screen.getByRole('combobox');
433
+ input.focus();
434
+ fireEvent.change(input, {target: {value: 'App'}});
435
+ await waitFor(() => {
436
+ expect(input).toHaveAttribute('aria-expanded', 'true');
437
+ });
438
+
439
+ // No blur is fired here on purpose: dismissing from the blur instead lets
440
+ // the popover close mid-focus-move, which Chrome answers by dropping
441
+ // focus to <body>.
442
+ fireEvent.keyDown(input, {key: 'Tab'});
443
+ await waitFor(() => {
444
+ expect(input).toHaveAttribute('aria-expanded', 'false');
445
+ });
446
+ });
447
+
448
+ it('Tab from the input with the list open moves focus to the next control', async () => {
449
+ const user = userEvent.setup();
450
+ render(
451
+ <>
452
+ <BaseTypeahead
453
+ searchSource={fruitSource}
454
+ value={null}
455
+ onChange={() => {}}
456
+ debounceMs={0}
457
+ />
458
+ <button type="button">Next</button>
459
+ </>,
460
+ );
461
+ const input = screen.getByRole('combobox');
462
+ input.focus();
463
+ fireEvent.change(input, {target: {value: 'App'}});
464
+ await waitFor(() => {
465
+ expect(input).toHaveAttribute('aria-expanded', 'true');
466
+ });
467
+
468
+ await user.keyboard('{Tab}');
469
+ expect(screen.getByRole('button', {name: 'Next'})).toHaveFocus();
470
+ });
471
+
423
472
  it('keeps the dropdown open when focus moves into the anchor wrapper', async () => {
424
473
  const anchor = document.createElement('div');
425
474
  document.body.appendChild(anchor);
@@ -45,13 +45,14 @@ import {VisuallyHidden} from '../VisuallyHidden';
45
45
  import {spacingVars, sizeVars} from '../theme/tokens.stylex';
46
46
  import {groupStyles} from '../InputGroup/groupStyles';
47
47
  import {useInputGroup} from '../InputGroup/InputGroupContext';
48
- import {getInputARIA, isImeKeyEvent, mergeProps, mergeRefs} from '../utils';
48
+ import {getInputARIA, isImeKeyEvent, mergeProps} from '../utils';
49
49
  import type {BaseProps} from '../BaseProps';
50
50
  import type {SizeValue} from '../utils/types';
51
51
  import type {SearchableItem, SearchSource} from './types';
52
52
  import {themeProps} from '../utils/themeProps';
53
53
  import {useTranslator} from '../i18n';
54
54
 
55
+ import {useMergedRefs} from '../hooks/useMergedRefs';
55
56
  export type {
56
57
  InputStatus as TypeaheadStatus,
57
58
  InputStatusType as TypeaheadStatusType,
@@ -418,7 +419,7 @@ export function Typeahead<T extends SearchableItem>({
418
419
  const typeaheadContent = (
419
420
  <>
420
421
  <div
421
- ref={mergeRefs(
422
+ ref={useMergedRefs(
422
423
  wrapperRef,
423
424
  disabledMessageTooltip.ref,
424
425
  inputGroup ? ref : undefined,
@@ -56,6 +56,8 @@ export type {UseTypeaheadOptions, UseTypeaheadReturn} from './useTypeahead';
56
56
 
57
57
  export {useMediaQuery} from './useMediaQuery';
58
58
 
59
+ export {useMergedRefs} from './useMergedRefs';
60
+
59
61
  export {useOverflow} from './useOverflow';
60
62
  export type {UseOverflowOptions, UseOverflowReturn} from './useOverflow';
61
63
 
@@ -24,7 +24,7 @@ export const docs = {
24
24
  {
25
25
  name: 'getContainerProps',
26
26
  type: '(options?: ContainerRevealOptions) => {className?: string; style?: CSSProperties}',
27
- description: 'Spread onto the container whose hover/focus-within drives the reveal. Accepts hoverDelay (ms the pointer must dwell before the reveal starts — a hover-intent gate like Tooltip\'s and HoverCard\'s delay, so a cursor sweeping across a list leaves nothing painted behind it) and forceState ("active" | "inactive") to pin the trigger state when a caller owns it — a motion gate, a scroll, a row whose menu is open. "inactive" still yields to keyboard focus and coarse pointers.',
27
+ description: 'Spread onto the container whose hover/focus-within drives the reveal. Accepts hoverDelay (ms the pointer must dwell before the reveal starts: a hover-intent gate like Tooltip\'s and HoverCard\'s delay, so a cursor sweeping across a list leaves nothing painted behind it) and forceState ("active" | "inactive") to pin the trigger state when a caller owns it: a motion gate, a scroll, or a row whose menu is open. "inactive" still yields to keyboard focus and coarse pointers.',
28
28
  },
29
29
  {
30
30
  name: 'getContentRevealProps',
@@ -61,7 +61,7 @@ export const docsDense = {
61
61
  'options.isEnabled': 'when false hook is inert: no container styles, content getters return no styles, content always shown. Read every render, so it can flip after mount.',
62
62
  },
63
63
  returnDescriptions: {
64
- getContainerProps: 'spread onto container whose hover/focus-within drives reveal. Accepts hoverDelay (ms dwell before reveal starts — hover-intent gate like Tooltip / HoverCard delay) + forceState ("active" | "inactive") to pin trigger state when a caller owns it. "inactive" yields to keyboard focus + coarse pointers.',
64
+ getContainerProps: 'spread onto container whose hover/focus-within drives reveal. Accepts hoverDelay (ms dwell before reveal starts: hover-intent gate like Tooltip / HoverCard delay) + forceState ("active" | "inactive") to pin trigger state when a caller owns it. "inactive" yields to keyboard focus + coarse pointers.',
65
65
  getContentRevealProps: 'spread onto each revealed / concealed child. Accepts isRevealInverted (conceal-on-hover), isLayoutPreserved (reserve layout box while hidden) + forceVisibility ("shown" | "hidden") to pin this element regardless of container. "hidden" yields to focus.',
66
66
  },
67
67
  usage: {
@@ -208,6 +208,20 @@ describe('useFocusTrap tabbable model (infra-8)', () => {
208
208
  expect(target).toHaveFocus();
209
209
  expect(screen.getByTestId('outside')).not.toHaveFocus();
210
210
  });
211
+
212
+ it('leaves Tab alone when focus is outside a trap with no tabbable controls', () => {
213
+ render(
214
+ <Trap>
215
+ <div role="option" tabIndex={-1} data-testid="option">
216
+ Option
217
+ </div>
218
+ </Trap>,
219
+ );
220
+ const outside = screen.getByTestId('outside');
221
+ outside.focus();
222
+
223
+ expect(fireEvent.keyDown(outside, {key: 'Tab'})).toBe(true);
224
+ });
211
225
  });
212
226
 
213
227
  describe('FOCUSABLE_SELECTOR href matching', () => {
@@ -19,80 +19,26 @@
19
19
  import {useCallback, useEffect, useRef} from 'react';
20
20
 
21
21
  import {FOCUSABLE_SELECTOR} from './focusableSelector';
22
- import {isImeKeyEvent} from '../utils/ime';
22
+ import {useLayerDismissal} from '../Layer/useLayerDismissal';
23
23
 
24
- /**
25
- * Module-level stack of active focus-trap Escape handlers.
26
- *
27
- * Every active `useFocusTrap` used to attach its own document-level `keydown`
28
- * listener with no coordination, so a single Escape press closed *every* open
29
- * layer at once (e.g. a popover nested inside a Dialog closed both). Tracking
30
- * traps in a shared stack lets only the top-most trap respond to Escape.
31
- *
32
- * "Top-most" is resolved by DOM containment first, push order second. Push
33
- * order alone is not reliable: React runs child effects before parent
34
- * effects, so when an outer and an inner (DOM-nested) trap mount in the SAME
35
- * commit, the inner trap pushes first and the outer trap would wrongly win a
36
- * pure last-pushed comparison.
37
- */
38
- interface EscapeStackEntry {
39
- handler: () => void;
40
- getContainer: () => HTMLElement | null;
41
- }
42
-
43
- const escapeStack: EscapeStackEntry[] = [];
44
-
45
- function pushEscapeHandler(entry: EscapeStackEntry): void {
46
- escapeStack.push(entry);
47
- }
48
-
49
- function removeEscapeHandler(handler: () => void): void {
50
- for (let i = escapeStack.length - 1; i >= 0; i--) {
51
- if (escapeStack[i].handler === handler) {
52
- escapeStack.splice(i, 1);
53
- return;
54
- }
55
- }
56
- }
24
+ // Escape-dismissible focus traps currently mounted. This is the whole state
25
+ // behind `hasActiveFocusTrapEscape`, which predates the shared stack and must
26
+ // keep answering about focus traps alone — the stack now carries families that
27
+ // never trapped focus (tooltips, hover cards), and counting those would tell
28
+ // callers a trap is above them when none is.
29
+ let activeEscapeTrapCount = 0;
57
30
 
58
31
  /**
59
- * Resolve the top-most trap: walk the stack in push order, keeping the
60
- * deepest container by DOM containment. When a later entry's container
61
- * contains the current candidate's container, the candidate is nested inside
62
- * it and stays on top; otherwise the later push wins (containment for nested
63
- * traps, push order as the tiebreaker for unrelated ones).
64
- */
65
- function isTopEscapeHandler(handler: () => void): boolean {
66
- if (escapeStack.length === 0) {
67
- return false;
68
- }
69
- let top = escapeStack[0];
70
- for (let i = 1; i < escapeStack.length; i++) {
71
- const entry = escapeStack[i];
72
- const topContainer = top.getContainer();
73
- const entryContainer = entry.getContainer();
74
- if (
75
- topContainer != null &&
76
- entryContainer != null &&
77
- entryContainer !== topContainer &&
78
- entryContainer.contains(topContainer)
79
- ) {
80
- // The current top is nested inside this entry — it stays on top.
81
- continue;
82
- }
83
- top = entry;
84
- }
85
- return top.handler === handler;
86
- }
87
-
88
- /**
89
- * Whether any focus-trap Escape handler is currently active (i.e. a popover
90
- * layer is open). Other overlay primitives that manage their own Escape (e.g.
91
- * Dialog) can consult this to defer to a popover layered on top of them,
92
- * giving topmost-only dismissal until a full layer stack exists.
32
+ * Whether an Escape-dismissible focus trap is currently active — a Popover,
33
+ * menu or other trapped layer that would take an Escape press.
34
+ *
35
+ * @deprecated The focus trap no longer owns Escape coordination — every overlay
36
+ * family shares one stack (`useLayerDismissal`), which routes each press to
37
+ * the top-most layer. A layer that wants the same ordering should join the
38
+ * stack rather than ask whether a trap exists.
93
39
  */
94
40
  export function hasActiveFocusTrapEscape(): boolean {
95
- return escapeStack.length > 0;
41
+ return activeEscapeTrapCount > 0;
96
42
  }
97
43
 
98
44
  /**
@@ -243,6 +189,38 @@ export function useFocusTrap<T extends HTMLElement = HTMLElement>(
243
189
  // Track if focus change was triggered by keyboard (Tab key)
244
190
  const isKeyboardNavigationRef = useRef(false);
245
191
 
192
+ // Join the shared layer dismissal stack. The trap no longer listens for
193
+ // Escape itself: the stack owns one listener and routes each press to the
194
+ // top-most layer, so a popover inside a Dialog, a submenu inside a menu, and
195
+ // a modal inside a modal all peel off one at a time. A trap with no
196
+ // `onEscape` is not dismissible and stays off the stack, so a press flows
197
+ // past it to whatever is underneath.
198
+ // One expression drives both the stack registration and the deprecated
199
+ // `hasActiveFocusTrapEscape` count, so the two can never disagree about
200
+ // whether this trap is active.
201
+ const isEscapeTrap = isActive && onEscape != null;
202
+
203
+ useLayerDismissal({
204
+ isActive: isEscapeTrap,
205
+ onDismiss: () => {
206
+ onEscape?.();
207
+ },
208
+ // The trap renders nothing, so it cannot push a depth provider around its
209
+ // content; hand the stack the container instead so two DOM-nested traps
210
+ // still resolve in the right order.
211
+ getContainer: () => containerRef.current,
212
+ });
213
+
214
+ useEffect(() => {
215
+ if (!isEscapeTrap) {
216
+ return;
217
+ }
218
+ activeEscapeTrapCount += 1;
219
+ return () => {
220
+ activeEscapeTrapCount -= 1;
221
+ };
222
+ }, [isEscapeTrap]);
223
+
246
224
  /**
247
225
  * Focus the first focusable element.
248
226
  */
@@ -353,67 +331,43 @@ export function useFocusTrap<T extends HTMLElement = HTMLElement>(
353
331
  }, [isActive]);
354
332
 
355
333
  /**
356
- * Handle Tab key to wrap focus at boundaries, and Escape to close.
357
- * Also tracks that keyboard navigation is occurring.
334
+ * Handle Tab key to wrap focus at boundaries. Also tracks that keyboard
335
+ * navigation is occurring.
336
+ *
337
+ * No Escape here, and no IME guard: the shared stack owns the press, claims
338
+ * a composing Escape so no close request follows, and dismisses the trap
339
+ * through `onEscape` above. The trap renders no element of its own, so it
340
+ * has no `cancel` to answer either.
358
341
  */
359
342
  useEffect(() => {
360
343
  if (!isActive) {
361
344
  return;
362
345
  }
363
346
 
364
- // Register this trap on the shared Escape stack so only the top-most
365
- // active trap responds to Escape. A stable identity per active period is
366
- // enough — we push on activate and remove on cleanup. The container is
367
- // read lazily: the ref may not be attached yet at effect time, and the
368
- // stack resolves top-most by DOM containment at keydown time.
369
- const escapeHandler = () => {
370
- onEscape?.();
371
- };
372
- if (onEscape) {
373
- pushEscapeHandler({
374
- handler: escapeHandler,
375
- getContainer: () => containerRef.current,
376
- });
377
- }
378
-
379
347
  const handleKeyDown = (event: KeyboardEvent) => {
380
348
  const container = containerRef.current;
381
349
  if (!container) {
382
350
  return;
383
351
  }
384
352
 
385
- if (event.key === 'Escape' && onEscape) {
386
- // Ignore Escape that is cancelling an IME composition, already handled
387
- // by a nested handler, or not targeting the top-most trap.
388
- if (
389
- event.defaultPrevented ||
390
- isImeKeyEvent(event) ||
391
- !isTopEscapeHandler(escapeHandler)
392
- ) {
393
- return;
394
- }
395
- // Mark handled and stop propagation so an outer layer (e.g. a Dialog
396
- // hosting this popover) does not also dismiss on the same press.
397
- event.preventDefault();
398
- event.stopPropagation();
399
- onEscape();
400
- return;
401
- }
402
-
403
353
  if (event.key === 'Tab') {
404
354
  // Mark that keyboard navigation is happening
405
355
  isKeyboardNavigationRef.current = true;
406
356
 
407
357
  const focusable = getFocusableElements(container);
408
358
  if (focusable.length === 0) {
359
+ const active = document.activeElement;
360
+ if (!(active instanceof HTMLElement) || !container.contains(active)) {
361
+ // A layer can be open while focus legitimately sits outside it —
362
+ // a listbox popup anchored to its own input. Cancelling there
363
+ // would take Tab away from the whole page.
364
+ return;
365
+ }
409
366
  // There is nowhere to advance to. Keep focus on the current
410
367
  // programmatic target (for example a dialog panel with tabIndex=-1)
411
368
  // rather than allowing the browser to move into background content.
412
369
  event.preventDefault();
413
- const active = document.activeElement;
414
- if (active instanceof HTMLElement && container.contains(active)) {
415
- lastFocusRef.current = active;
416
- }
370
+ lastFocusRef.current = active;
417
371
  isKeyboardNavigationRef.current = false;
418
372
  return;
419
373
  }
@@ -441,9 +395,6 @@ export function useFocusTrap<T extends HTMLElement = HTMLElement>(
441
395
 
442
396
  return () => {
443
397
  document.removeEventListener('keydown', handleKeyDown);
444
- if (onEscape) {
445
- removeEscapeHandler(escapeHandler);
446
- }
447
398
  };
448
399
  }, [isActive, onEscape]);
449
400