@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
@@ -0,0 +1,469 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useLayerDismissal.test.tsx
5
+ * @input Uses @testing-library/react, vitest
6
+ * @output Tests for the shared layer dismissal stack
7
+ * @position Colocated tests for useLayerDismissal + layerStack
8
+ *
9
+ * Every case here is "one Escape press, which layers reacted?" — the single
10
+ * question the stack exists to answer.
11
+ */
12
+
13
+ import {render, fireEvent} from '@testing-library/react';
14
+ import {describe, expect, it, vi, afterEach} from 'vitest';
15
+ import {StrictMode, useRef} from 'react';
16
+
17
+ import {LayerDepthProvider} from './LayerDepthContext';
18
+ import {useLayerDismissal} from './useLayerDismissal';
19
+ import {resetLayerStackForTests} from './layerStack';
20
+ import type {LayerEscapeBehavior} from './layerStack';
21
+
22
+ afterEach(() => {
23
+ resetLayerStackForTests();
24
+ });
25
+
26
+ /** A layer that registers itself and renders its children one level deeper. */
27
+ function Layer({
28
+ onDismiss,
29
+ behavior = 'close',
30
+ isEnabled = true,
31
+ isActive = true,
32
+ isPresent,
33
+ children,
34
+ }: {
35
+ onDismiss: () => void;
36
+ behavior?: LayerEscapeBehavior;
37
+ isEnabled?: boolean;
38
+ isActive?: boolean;
39
+ isPresent?: () => boolean;
40
+ children?: React.ReactNode;
41
+ }) {
42
+ const containerRef = useRef<HTMLDivElement>(null);
43
+ useLayerDismissal({
44
+ isActive,
45
+ onDismiss,
46
+ escapeBehavior: behavior,
47
+ isEnabled,
48
+ isPresent,
49
+ getContainer: () => containerRef.current,
50
+ });
51
+ return (
52
+ <div ref={containerRef}>
53
+ <LayerDepthProvider>{children}</LayerDepthProvider>
54
+ </div>
55
+ );
56
+ }
57
+
58
+ /** A layer that reports no depth — like a bare focus trap, which renders nothing. */
59
+ function FlatLayer({
60
+ onDismiss,
61
+ children,
62
+ }: {
63
+ onDismiss: () => void;
64
+ children?: React.ReactNode;
65
+ }) {
66
+ const containerRef = useRef<HTMLDivElement>(null);
67
+ useLayerDismissal({
68
+ isActive: true,
69
+ onDismiss,
70
+ getContainer: () => containerRef.current,
71
+ });
72
+ return <div ref={containerRef}>{children}</div>;
73
+ }
74
+
75
+ const pressEscape = () => fireEvent.keyDown(document, {key: 'Escape'});
76
+
77
+ describe('useLayerDismissal', () => {
78
+ it('dismisses only the top-most layer when both open in the same commit', () => {
79
+ // The regression: an inner and outer layer that mount together. React runs
80
+ // child effects first, so registration order reports the inner layer as the
81
+ // OLDER one — nesting, not order, has to decide.
82
+ const outer = vi.fn();
83
+ const inner = vi.fn();
84
+
85
+ render(
86
+ <Layer onDismiss={outer}>
87
+ <Layer onDismiss={inner} />
88
+ </Layer>,
89
+ );
90
+
91
+ pressEscape();
92
+ expect(inner).toHaveBeenCalledTimes(1);
93
+ expect(outer).not.toHaveBeenCalled();
94
+ });
95
+
96
+ it('falls through to the outer layer once the inner one closes', () => {
97
+ const outer = vi.fn();
98
+ const inner = vi.fn();
99
+ const {rerender} = render(
100
+ <Layer onDismiss={outer}>
101
+ <Layer onDismiss={inner} />
102
+ </Layer>,
103
+ );
104
+ rerender(<Layer onDismiss={outer} />);
105
+
106
+ pressEscape();
107
+ expect(outer).toHaveBeenCalledTimes(1);
108
+ expect(inner).not.toHaveBeenCalled();
109
+ });
110
+
111
+ it('dismisses the later of two unrelated layers', () => {
112
+ const first = vi.fn();
113
+ const second = vi.fn();
114
+ const {rerender} = render(<Layer onDismiss={first} />);
115
+ rerender(
116
+ <>
117
+ <Layer onDismiss={first} />
118
+ <Layer onDismiss={second} />
119
+ </>,
120
+ );
121
+
122
+ pressEscape();
123
+ expect(second).toHaveBeenCalledTimes(1);
124
+ expect(first).not.toHaveBeenCalled();
125
+ });
126
+
127
+ it('resolves nesting by DOM containment when the tree reports equal depth', () => {
128
+ // A bare focus trap renders nothing, so it cannot push a depth provider.
129
+ // Containment is the only nesting signal those layers have.
130
+ const outer = vi.fn();
131
+ const inner = vi.fn();
132
+ render(
133
+ <FlatLayer onDismiss={outer}>
134
+ <FlatLayer onDismiss={inner} />
135
+ </FlatLayer>,
136
+ );
137
+
138
+ pressEscape();
139
+ expect(inner).toHaveBeenCalledTimes(1);
140
+ expect(outer).not.toHaveBeenCalled();
141
+ });
142
+
143
+ describe('escapeBehavior', () => {
144
+ it("'close' consumes the press, so one Escape affects exactly one layer", () => {
145
+ // A hover tip inside a modal: the tip is on top, so it takes the press
146
+ // and the modal stays open. A second Escape closes the modal.
147
+ const modal = vi.fn();
148
+ const tip = vi.fn();
149
+ render(
150
+ <Layer onDismiss={modal}>
151
+ <Layer onDismiss={tip} />
152
+ </Layer>,
153
+ );
154
+
155
+ pressEscape();
156
+ expect(tip).toHaveBeenCalledTimes(1);
157
+ expect(modal).not.toHaveBeenCalled();
158
+ });
159
+
160
+ it("'block' consumes the press without dismissing anything", () => {
161
+ const host = vi.fn();
162
+ const required = vi.fn();
163
+ render(
164
+ <Layer onDismiss={host}>
165
+ <Layer onDismiss={required} behavior="block" />
166
+ </Layer>,
167
+ );
168
+
169
+ pressEscape();
170
+ expect(required).not.toHaveBeenCalled();
171
+ expect(host).not.toHaveBeenCalled();
172
+ });
173
+ });
174
+
175
+ describe('registration order', () => {
176
+ /** Two unrelated same-depth layers; the older one's behavior is a prop. */
177
+ function Siblings({
178
+ olderBehavior,
179
+ onOlderDismiss,
180
+ onNewerDismiss,
181
+ }: {
182
+ olderBehavior: LayerEscapeBehavior;
183
+ onOlderDismiss: () => void;
184
+ onNewerDismiss: () => void;
185
+ }) {
186
+ return (
187
+ <>
188
+ <Layer onDismiss={onOlderDismiss} behavior={olderBehavior} />
189
+ <Layer onDismiss={onNewerDismiss} />
190
+ </>
191
+ );
192
+ }
193
+
194
+ it('keeps a layer below the ones that opened after it when its escapeBehavior changes', () => {
195
+ // A Dialog whose `purpose` flips from required to info while it is open
196
+ // re-registers with the stack. Re-registration must not reorder it above
197
+ // a layer opened on top of it.
198
+ const older = vi.fn();
199
+ const newer = vi.fn();
200
+ const {rerender} = render(
201
+ <Siblings
202
+ olderBehavior="block"
203
+ onOlderDismiss={older}
204
+ onNewerDismiss={newer}
205
+ />,
206
+ );
207
+ rerender(
208
+ <Siblings
209
+ olderBehavior="close"
210
+ onOlderDismiss={older}
211
+ onNewerDismiss={newer}
212
+ />,
213
+ );
214
+
215
+ pressEscape();
216
+ expect(newer).toHaveBeenCalledTimes(1);
217
+ expect(older).not.toHaveBeenCalled();
218
+ });
219
+
220
+ it('does not let a re-registered layer swallow a press meant for the layer above it', () => {
221
+ // The same flip the other way: info to required. A `block` layer that
222
+ // jumped the queue would consume the press and nothing would close.
223
+ const older = vi.fn();
224
+ const newer = vi.fn();
225
+ const {rerender} = render(
226
+ <Siblings
227
+ olderBehavior="close"
228
+ onOlderDismiss={older}
229
+ onNewerDismiss={newer}
230
+ />,
231
+ );
232
+ rerender(
233
+ <Siblings
234
+ olderBehavior="block"
235
+ onOlderDismiss={older}
236
+ onNewerDismiss={newer}
237
+ />,
238
+ );
239
+
240
+ pressEscape();
241
+ expect(newer).toHaveBeenCalledTimes(1);
242
+ expect(older).not.toHaveBeenCalled();
243
+ });
244
+
245
+ it('keeps ordering under StrictMode, which mounts every effect twice', () => {
246
+ const first = vi.fn();
247
+ const second = vi.fn();
248
+ render(
249
+ <StrictMode>
250
+ <Layer onDismiss={first} />
251
+ <Layer onDismiss={second} />
252
+ </StrictMode>,
253
+ );
254
+
255
+ pressEscape();
256
+ expect(second).toHaveBeenCalledTimes(1);
257
+ expect(first).not.toHaveBeenCalled();
258
+ });
259
+ });
260
+
261
+ describe('presence', () => {
262
+ it('skips a registered layer that is not on screen', () => {
263
+ // Hover layers stay registered for their lifetime because their open
264
+ // state lags the DOM. An absent one must not claim the press — that is
265
+ // exactly the bug where a HoverCard trigger ate Escapes while idle.
266
+ const below = vi.fn();
267
+ const absent = vi.fn();
268
+ render(
269
+ <Layer onDismiss={below}>
270
+ <Layer onDismiss={absent} isPresent={() => false} />
271
+ </Layer>,
272
+ );
273
+
274
+ pressEscape();
275
+ expect(absent).not.toHaveBeenCalled();
276
+ expect(below).toHaveBeenCalledTimes(1);
277
+ });
278
+
279
+ it('lets a present layer claim the press over the one beneath', () => {
280
+ const below = vi.fn();
281
+ const present = vi.fn();
282
+ render(
283
+ <Layer onDismiss={below}>
284
+ <Layer onDismiss={present} isPresent={() => true} />
285
+ </Layer>,
286
+ );
287
+
288
+ pressEscape();
289
+ expect(present).toHaveBeenCalledTimes(1);
290
+ expect(below).not.toHaveBeenCalled();
291
+ });
292
+ });
293
+
294
+ describe('opting out', () => {
295
+ it('skips a disabled layer entirely, so the press reaches the one below', () => {
296
+ const below = vi.fn();
297
+ const optedOut = vi.fn();
298
+ render(
299
+ <Layer onDismiss={below}>
300
+ <Layer onDismiss={optedOut} isEnabled={false} />
301
+ </Layer>,
302
+ );
303
+
304
+ pressEscape();
305
+ expect(optedOut).not.toHaveBeenCalled();
306
+ expect(below).toHaveBeenCalledTimes(1);
307
+ });
308
+
309
+ it('does not register an inactive layer', () => {
310
+ const onDismiss = vi.fn();
311
+ render(<Layer onDismiss={onDismiss} isActive={false} />);
312
+
313
+ pressEscape();
314
+ expect(onDismiss).not.toHaveBeenCalled();
315
+ });
316
+ });
317
+
318
+ describe('deferring to content', () => {
319
+ it('stands down when content already handled the press', () => {
320
+ // preventDefault from inside the layer — an editor claiming Escape for
321
+ // its own find widget, for instance.
322
+ const onDismiss = vi.fn();
323
+ render(
324
+ <Layer onDismiss={onDismiss}>
325
+ <button
326
+ type="button"
327
+ data-testid="editor"
328
+ onKeyDown={e => {
329
+ if (e.key === 'Escape') {
330
+ e.preventDefault();
331
+ }
332
+ }}
333
+ />
334
+ </Layer>,
335
+ );
336
+
337
+ fireEvent.keyDown(document.querySelector('[data-testid="editor"]')!, {
338
+ key: 'Escape',
339
+ });
340
+ expect(onDismiss).not.toHaveBeenCalled();
341
+ });
342
+
343
+ it('ignores Escape that is cancelling an IME composition', () => {
344
+ const onDismiss = vi.fn();
345
+ render(<Layer onDismiss={onDismiss} />);
346
+
347
+ fireEvent.keyDown(document, {key: 'Escape', isComposing: true});
348
+ expect(onDismiss).not.toHaveBeenCalled();
349
+
350
+ fireEvent.keyDown(document, {key: 'Escape', keyCode: 229});
351
+ expect(onDismiss).not.toHaveBeenCalled();
352
+
353
+ pressEscape();
354
+ expect(onDismiss).toHaveBeenCalledTimes(1);
355
+ });
356
+
357
+ it('claims the composing Escape so the browser raises no close request', () => {
358
+ // Not dismissing is only half of it. An unclaimed Escape lets the browser
359
+ // raise its own close request, which reaches the layer's `cancel` handler
360
+ // and dismisses it on the same keypress — so the guard has to swallow the
361
+ // press, not merely skip the dismissal.
362
+ render(<Layer onDismiss={vi.fn()} />);
363
+ const event = new KeyboardEvent('keydown', {
364
+ key: 'Escape',
365
+ bubbles: true,
366
+ cancelable: true,
367
+ });
368
+ Object.defineProperty(event, 'isComposing', {value: true});
369
+
370
+ document.dispatchEvent(event);
371
+ expect(event.defaultPrevented).toBe(true);
372
+ });
373
+
374
+ it('leaves a composing Escape alone when no layer is on screen', () => {
375
+ // With nothing to protect the press is not ours to take: an idle hover
376
+ // layer in the tree must not suppress the page's own close watcher.
377
+ render(<Layer onDismiss={vi.fn()} isPresent={() => false} />);
378
+ const event = new KeyboardEvent('keydown', {
379
+ key: 'Escape',
380
+ bubbles: true,
381
+ cancelable: true,
382
+ });
383
+ Object.defineProperty(event, 'isComposing', {value: true});
384
+
385
+ document.dispatchEvent(event);
386
+ expect(event.defaultPrevented).toBe(false);
387
+ });
388
+ });
389
+
390
+ describe('close requests the browser starts itself', () => {
391
+ // The Android back gesture and the platform close watcher arrive as a
392
+ // `cancel` with no keydown to read, so the keydown guard cannot see them.
393
+ function CloseRequestLayer({onAsk}: {onAsk: (answer: boolean) => void}) {
394
+ const {shouldDismissOnCloseRequest} = useLayerDismissal({
395
+ isActive: true,
396
+ onDismiss: () => {},
397
+ });
398
+ return (
399
+ <>
400
+ <input aria-label="composing field" />
401
+ <button
402
+ type="button"
403
+ onClick={() => onAsk(shouldDismissOnCloseRequest())}>
404
+ ask
405
+ </button>
406
+ </>
407
+ );
408
+ }
409
+
410
+ const ask = (onAsk: ReturnType<typeof vi.fn>): boolean => {
411
+ fireEvent.click(document.querySelector('button')!);
412
+ return Boolean(onAsk.mock.lastCall?.[0]);
413
+ };
414
+
415
+ it('declines while an IME composition is running, and again once it ends', () => {
416
+ const onAsk = vi.fn();
417
+ render(<CloseRequestLayer onAsk={onAsk} />);
418
+ const field = document.querySelector('input')!;
419
+
420
+ expect(ask(onAsk)).toBe(true);
421
+
422
+ fireEvent.compositionStart(field);
423
+ expect(ask(onAsk)).toBe(false);
424
+
425
+ fireEvent.compositionEnd(field);
426
+ expect(ask(onAsk)).toBe(true);
427
+ });
428
+
429
+ it('stops declining when focus leaves a field mid-composition', () => {
430
+ // A compositionend the stack never sees would otherwise leave the flag
431
+ // stuck on, and every later back gesture would be swallowed.
432
+ const onAsk = vi.fn();
433
+ render(<CloseRequestLayer onAsk={onAsk} />);
434
+ const field = document.querySelector('input')!;
435
+
436
+ fireEvent.compositionStart(field);
437
+ expect(ask(onAsk)).toBe(false);
438
+
439
+ fireEvent.blur(field);
440
+ expect(ask(onAsk)).toBe(true);
441
+ });
442
+ });
443
+
444
+ it('leaves the event alone when no layer is open', () => {
445
+ // Nothing registered: the browser keeps its own Escape behavior (exiting
446
+ // fullscreen, closing a native picker).
447
+ render(<div />);
448
+ const event = new KeyboardEvent('keydown', {
449
+ key: 'Escape',
450
+ bubbles: true,
451
+ cancelable: true,
452
+ });
453
+ document.dispatchEvent(event);
454
+ expect(event.defaultPrevented).toBe(false);
455
+ });
456
+
457
+ it('claims the press it handles so the browser does not act too', () => {
458
+ // preventDefault is what stops the native close-watcher dismissing a second
459
+ // layer behind the stack's back.
460
+ render(<Layer onDismiss={vi.fn()} />);
461
+ const event = new KeyboardEvent('keydown', {
462
+ key: 'Escape',
463
+ bubbles: true,
464
+ cancelable: true,
465
+ });
466
+ document.dispatchEvent(event);
467
+ expect(event.defaultPrevented).toBe(true);
468
+ });
469
+ });
@@ -0,0 +1,173 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useLayerDismissal.ts
7
+ * @input Uses React hooks, the layer stack, and LayerDepthContext
8
+ * @output Exports useLayerDismissal + its option/return types
9
+ * @position The API overlays use to join the shared dismissal stack. Wraps
10
+ * layerStack (registration + the single Escape listener) and reads nesting
11
+ * depth from LayerDepthContext.
12
+ *
13
+ * SYNC: When modified, update:
14
+ * - /packages/core/src/Layer/index.ts
15
+ * - /packages/core/src/Layer/useLayerDismissal.test.tsx
16
+ * - /packages/core/src/Layer/layerStack.ts
17
+ */
18
+
19
+ import {useCallback, useEffect, useRef} from 'react';
20
+
21
+ import {useLayerDepth} from './LayerDepthContext';
22
+ import {
23
+ isTextComposing,
24
+ isTopmostLayer,
25
+ registerLayer,
26
+ type LayerEscapeBehavior,
27
+ } from './layerStack';
28
+
29
+ export type {LayerEscapeBehavior};
30
+
31
+ export interface UseLayerDismissalOptions {
32
+ /**
33
+ * Whether this layer is participating right now. Usually the layer's open
34
+ * state. A layer whose open state lags the DOM should pass `true` for its
35
+ * lifetime and answer `isPresent` from the DOM instead.
36
+ */
37
+ isActive: boolean;
38
+ /**
39
+ * Dismiss this layer. Called by the stack when this layer is the one that
40
+ * should respond to an Escape press. Not called for `escapeBehavior: 'block'`.
41
+ */
42
+ onDismiss: () => void;
43
+ /**
44
+ * What this layer does with an Escape press that reaches it.
45
+ * @default 'close'
46
+ */
47
+ escapeBehavior?: LayerEscapeBehavior;
48
+ /**
49
+ * This layer's container element, read lazily. Optional: supply it when the
50
+ * layer cannot wrap its content in `LayerDepthProvider` (a bare focus trap
51
+ * renders nothing), so nesting can still be recovered from the DOM.
52
+ */
53
+ getContainer?: () => HTMLElement | null;
54
+ /**
55
+ * Whether the layer is really on screen, asked at press time. Supply it when
56
+ * `isActive` is `true` for the layer's lifetime because its open state lags
57
+ * the DOM by a frame; the stack skips layers that answer `false`.
58
+ */
59
+ isPresent?: () => boolean;
60
+ /**
61
+ * Whether this layer takes part in the shared stack at all. When `false` the
62
+ * layer is invisible to dismissal: a press flows past it to the layer below,
63
+ * exactly as if it were not open.
64
+ *
65
+ * Deliberately separate from `escapeBehavior: 'block'` — `'block'` is a layer
66
+ * that is present and swallows the press; this is a layer that is not there
67
+ * at all. One case uses it: `Dialog`'s inline rendering mode, where content
68
+ * sits in normal flow with nothing layered over anything.
69
+ *
70
+ * A controlled layer is NOT one of these. It stays registered and takes the
71
+ * press like any other, answering it by calling the consumer's change
72
+ * handler; whether it then closes is the consumer's decision.
73
+ *
74
+ * Layers that are never Escape-dismissible (toasts) should simply not call
75
+ * this hook.
76
+ *
77
+ * @default true
78
+ */
79
+ isEnabled?: boolean;
80
+ }
81
+
82
+ export interface UseLayerDismissalReturn {
83
+ /**
84
+ * Whether a close request the browser started on its own — a `<dialog>`'s
85
+ * `cancel`, the Android back gesture, the platform close watcher — should
86
+ * dismiss this layer. As well as the top-most rule the stack applies to an
87
+ * Escape press, it declines a request that arrives while an IME composition
88
+ * is running, which no `cancel` handler can tell on its own because the
89
+ * event carries no composition state.
90
+ */
91
+ shouldDismissOnCloseRequest: () => boolean;
92
+ }
93
+
94
+ /**
95
+ * Join the shared layer dismissal stack for as long as this layer is active.
96
+ *
97
+ * The layer does NOT attach a key listener — the stack owns one listener and
98
+ * routes each Escape press to the top-most REGISTERED layer, so one press
99
+ * dismisses exactly one of them. Dialog (and what is built on it), Popover and
100
+ * the menus built on it, Tooltip, HoverCard, Lightbox and MobileNav register
101
+ * today; `BottomSheetSwitcher` registers through its focus trap when modal and
102
+ * still handles its own press when not.
103
+ *
104
+ * `BottomSheet`, `CommandPalette`, `ContextMenu`, `DropdownMenuSubMenu`,
105
+ * `PowerSearchEditPopover` and lab's `Drawer` still run their own Escape
106
+ * listener. They stay safe next to the stack only because each claims the press
107
+ * at element level, and the stack stands down on an already-`defaultPrevented`
108
+ * press — but a registered layer opened INSIDE one of them does not get the
109
+ * press: the host takes it and closes instead. Migrating them is the fix.
110
+ *
111
+ * Wrap the layer's own content in `LayerDepthProvider` so anything opened from
112
+ * inside it registers as nested.
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * useLayerDismissal({
117
+ * isActive: isOpen,
118
+ * onDismiss: () => onOpenChange(false),
119
+ * });
120
+ * ```
121
+ */
122
+ export function useLayerDismissal(
123
+ options: UseLayerDismissalOptions,
124
+ ): UseLayerDismissalReturn {
125
+ const {
126
+ isActive,
127
+ onDismiss,
128
+ escapeBehavior = 'close',
129
+ getContainer,
130
+ isPresent,
131
+ isEnabled = true,
132
+ } = options;
133
+
134
+ const depth = useLayerDepth();
135
+
136
+ // Identity for this layer's stack entry, stable across re-registration.
137
+ const tokenRef = useRef<object>({});
138
+
139
+ // The stack calls these during an event, outside React's render, so they must
140
+ // reach the latest closures rather than the ones captured at registration.
141
+ const onDismissRef = useRef(onDismiss);
142
+ const getContainerRef = useRef(getContainer);
143
+ const isPresentRef = useRef(isPresent);
144
+ useEffect(() => {
145
+ onDismissRef.current = onDismiss;
146
+ getContainerRef.current = getContainer;
147
+ isPresentRef.current = isPresent;
148
+ });
149
+
150
+ const isRegistered = isActive && isEnabled;
151
+
152
+ useEffect(() => {
153
+ if (!isRegistered) {
154
+ return;
155
+ }
156
+ return registerLayer({
157
+ token: tokenRef.current,
158
+ depth,
159
+ behavior: escapeBehavior,
160
+ getContainer: () => getContainerRef.current?.() ?? null,
161
+ isPresent: () => isPresentRef.current?.() ?? true,
162
+ dismiss: () => onDismissRef.current(),
163
+ });
164
+ }, [isRegistered, depth, escapeBehavior]);
165
+
166
+ const shouldDismissOnCloseRequest = useCallback(
167
+ () =>
168
+ isRegistered && !isTextComposing() && isTopmostLayer(tokenRef.current),
169
+ [isRegistered],
170
+ );
171
+
172
+ return {shouldDismissOnCloseRequest};
173
+ }
@@ -95,9 +95,9 @@ export const docs = {
95
95
  },
96
96
  {
97
97
  name: 'contentWidth',
98
- type: 'number | string',
98
+ type: 'SizeValue',
99
99
  description:
100
- 'Maximum width of the content within each slot (header, content, footer, panels), centered when narrower than the available space. Dividers stay full-bleed. Numbers are pixels, strings are used as-is (e.g. `60ch`). Common page widths: 640 for forms, settings, and text-focused pages; 960 for content pages and wider layouts.',
100
+ 'Maximum width of the content within each slot (header, content, footer, panels), centered when narrower than the available space. Dividers stay full-bleed. Numbers are treated as pixels, strings are used as-is (e.g. `60ch`). Common page widths: 640 for forms, settings, and text-focused pages; 960 for content pages and wider layouts.',
101
101
  },
102
102
  {
103
103
  name: 'padding',
@@ -47,6 +47,18 @@ export const docs = {
47
47
  description: 'Enable zoom on double-click, or Enter/Space/+/- via keyboard (images only). When zoomed, drag or use arrow keys to pan.',
48
48
  default: 'false',
49
49
  },
50
+ {
51
+ name: 'defaultIndex',
52
+ type: 'number',
53
+ description: 'Initial image index in gallery mode for uncontrolled usage.',
54
+ default: '0',
55
+ },
56
+ {
57
+ name: 'hasAutoPlay',
58
+ type: 'boolean',
59
+ description: 'Automatically start video playback when a video media item is shown.',
60
+ default: 'false',
61
+ },
50
62
  {
51
63
  name: 'xstyle',
52
64
  type: 'StyleXStyles',