@astryxdesign/core 0.4.6 → 0.4.7-canary.019ae5a

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 (486) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +3 -2
  4. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  5. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  6. package/dist/AspectRatio/AspectRatio.js +23 -4
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +15 -10
  9. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  10. package/dist/AvatarGroup/AvatarGroup.js +3 -2
  11. package/dist/Banner/Banner.d.ts +40 -15
  12. package/dist/Banner/Banner.d.ts.map +1 -1
  13. package/dist/Banner/Banner.js +51 -18
  14. package/dist/BottomSheet/BottomSheet.d.ts +1 -0
  15. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  16. package/dist/BottomSheet/BottomSheet.js +54 -15
  17. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +6 -0
  18. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -0
  19. package/dist/BottomSheet/BottomSheetEdgeTint.js +62 -0
  20. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  21. package/dist/BottomSheet/BottomSheetPanel.js +1 -0
  22. package/dist/BottomSheet/BottomSheetSwitcher.d.ts +1 -0
  23. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  24. package/dist/BottomSheet/BottomSheetSwitcher.js +13 -6
  25. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  26. package/dist/BottomSheet/useSheetGestures.js +23 -5
  27. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  28. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  29. package/dist/Button/Button.d.ts.map +1 -1
  30. package/dist/Button/Button.js +5 -4
  31. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  32. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  33. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  34. package/dist/Calendar/Calendar.d.ts +3 -1
  35. package/dist/Calendar/Calendar.d.ts.map +1 -1
  36. package/dist/Calendar/Calendar.js +13 -14
  37. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  38. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  39. package/dist/Calendar/getInitialFocusDate.js +62 -0
  40. package/dist/Carousel/Carousel.d.ts.map +1 -1
  41. package/dist/Carousel/Carousel.js +3 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +2 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayout.js +3 -3
  46. package/dist/Chat/ChatMessageList.d.ts +22 -3
  47. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessageList.js +6 -3
  49. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  50. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  51. package/dist/Chat/ChatTokenizedText.js +11 -6
  52. package/dist/Chat/useChatComposerTokens.js +1 -1
  53. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  54. package/dist/CheckboxInput/CheckboxInput.js +3 -2
  55. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  56. package/dist/ClickableCard/ClickableCard.js +3 -3
  57. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  58. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  59. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  60. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  61. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  62. package/dist/ContextMenu/ContextMenu.js +4 -3
  63. package/dist/DateInput/DateInput.d.ts.map +1 -1
  64. package/dist/DateInput/DateInput.js +3 -2
  65. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  66. package/dist/DateInput/TouchDateField.js +38 -4
  67. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  68. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  69. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  70. package/dist/DateTimeInput/index.d.ts +1 -1
  71. package/dist/DateTimeInput/index.d.ts.map +1 -1
  72. package/dist/Dialog/Dialog.d.ts +7 -1
  73. package/dist/Dialog/Dialog.d.ts.map +1 -1
  74. package/dist/Dialog/Dialog.js +89 -58
  75. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  77. package/dist/FileInput/FileInput.d.ts.map +1 -1
  78. package/dist/FileInput/FileInput.js +3 -2
  79. package/dist/Heading/Heading.d.ts.map +1 -1
  80. package/dist/Heading/Heading.js +6 -2
  81. package/dist/HoverCard/HoverCard.d.ts +6 -0
  82. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  83. package/dist/HoverCard/useHoverCard.d.ts +5 -0
  84. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  85. package/dist/HoverCard/useHoverCard.js +67 -25
  86. package/dist/Item/Item.d.ts.map +1 -1
  87. package/dist/Item/Item.js +4 -2
  88. package/dist/Kbd/Kbd.d.ts.map +1 -1
  89. package/dist/Kbd/Kbd.js +10 -3
  90. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  91. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  92. package/dist/Layer/LayerDepthContext.js +66 -0
  93. package/dist/Layer/index.d.ts +3 -0
  94. package/dist/Layer/index.d.ts.map +1 -1
  95. package/dist/Layer/index.js +4 -0
  96. package/dist/Layer/layerStack.d.ts +98 -0
  97. package/dist/Layer/layerStack.d.ts.map +1 -0
  98. package/dist/Layer/layerStack.js +306 -0
  99. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  100. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  101. package/dist/Layer/useLayerDismissal.js +91 -0
  102. package/dist/Lightbox/Lightbox.d.ts +1 -0
  103. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  104. package/dist/Lightbox/Lightbox.js +103 -81
  105. package/dist/Markdown/index.d.ts +1 -1
  106. package/dist/Markdown/index.d.ts.map +1 -1
  107. package/dist/Markdown/parser.d.ts +38 -1
  108. package/dist/Markdown/parser.d.ts.map +1 -1
  109. package/dist/Markdown/parser.js +177 -29
  110. package/dist/MobileNav/MobileNav.d.ts +6 -1
  111. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  112. package/dist/MobileNav/MobileNav.js +61 -36
  113. package/dist/MultiSelector/MultiSelector.d.ts +15 -1
  114. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  115. package/dist/MultiSelector/MultiSelector.js +17 -6
  116. package/dist/MultiSelector/index.d.ts +1 -1
  117. package/dist/MultiSelector/index.d.ts.map +1 -1
  118. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  119. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  120. package/dist/Outline/Outline.d.ts.map +1 -1
  121. package/dist/Outline/Outline.js +3 -2
  122. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  123. package/dist/OverflowList/OverflowList.js +3 -2
  124. package/dist/Overlay/Overlay.d.ts.map +1 -1
  125. package/dist/Overlay/Overlay.js +3 -2
  126. package/dist/Popover/usePopover.d.ts.map +1 -1
  127. package/dist/Popover/usePopover.js +20 -17
  128. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  129. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  130. package/dist/PowerSearch/PowerSearch.js +19 -6
  131. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  132. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  133. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  134. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  135. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  136. package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
  137. package/dist/PowerSearch/types.d.ts +2 -0
  138. package/dist/PowerSearch/types.d.ts.map +1 -1
  139. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  140. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  141. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  142. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  143. package/dist/Resizable/ResizeHandle.js +3 -2
  144. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  145. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  146. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  147. package/dist/SelectableCard/SelectableCard.js +3 -3
  148. package/dist/Selector/Selector.d.ts.map +1 -1
  149. package/dist/Selector/Selector.js +5 -0
  150. package/dist/SideNav/SideNav.d.ts.map +1 -1
  151. package/dist/SideNav/SideNav.js +4 -3
  152. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  153. package/dist/SideNav/SideNavHeading.js +6 -5
  154. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  155. package/dist/SideNav/SideNavItem.js +4 -3
  156. package/dist/Slider/Slider.d.ts.map +1 -1
  157. package/dist/Slider/Slider.js +3 -3
  158. package/dist/Spinner/Spinner.d.ts +3 -3
  159. package/dist/Spinner/Spinner.d.ts.map +1 -1
  160. package/dist/Spinner/Spinner.js +130 -78
  161. package/dist/Stepper/Step.d.ts +140 -0
  162. package/dist/Stepper/Step.d.ts.map +1 -0
  163. package/dist/Stepper/Step.js +1045 -0
  164. package/dist/Stepper/StepStatus.d.ts +24 -0
  165. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  166. package/dist/Stepper/StepStatus.js +1 -0
  167. package/dist/Stepper/Stepper.d.ts +93 -0
  168. package/dist/Stepper/Stepper.d.ts.map +1 -0
  169. package/dist/Stepper/Stepper.js +184 -0
  170. package/dist/Stepper/StepperContext.d.ts +41 -0
  171. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  172. package/dist/Stepper/StepperContext.js +34 -0
  173. package/dist/Stepper/index.d.ts +8 -0
  174. package/dist/Stepper/index.d.ts.map +1 -0
  175. package/dist/Stepper/index.js +7 -0
  176. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  177. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  178. package/dist/Stepper/stepper.stylex.js +20 -0
  179. package/dist/Switch/Switch.d.ts.map +1 -1
  180. package/dist/Switch/Switch.js +3 -2
  181. package/dist/TabList/Tab.d.ts +15 -2
  182. package/dist/TabList/Tab.d.ts.map +1 -1
  183. package/dist/TabList/Tab.js +32 -3
  184. package/dist/TabList/TabList.d.ts +46 -7
  185. package/dist/TabList/TabList.d.ts.map +1 -1
  186. package/dist/TabList/TabList.js +353 -23
  187. package/dist/TabList/TabListContext.d.ts +11 -1
  188. package/dist/TabList/TabListContext.d.ts.map +1 -1
  189. package/dist/TabList/TabListContext.js +11 -2
  190. package/dist/TabList/TabMenu.d.ts.map +1 -1
  191. package/dist/TabList/TabMenu.js +3 -2
  192. package/dist/TabList/index.d.ts +2 -2
  193. package/dist/TabList/index.d.ts.map +1 -1
  194. package/dist/Table/BaseTable.d.ts.map +1 -1
  195. package/dist/Table/BaseTable.js +4 -1
  196. package/dist/Table/TableRow.d.ts +16 -5
  197. package/dist/Table/TableRow.d.ts.map +1 -1
  198. package/dist/Table/TableRow.js +16 -5
  199. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  200. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  201. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  202. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  203. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  204. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  205. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  206. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  207. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  208. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  209. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  210. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  211. package/dist/Table/types.d.ts +22 -4
  212. package/dist/Table/types.d.ts.map +1 -1
  213. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  214. package/dist/Table/useBaseTablePlugins.js +5 -0
  215. package/dist/Text/Text.d.ts.map +1 -1
  216. package/dist/Text/Text.js +6 -2
  217. package/dist/TextArea/TextArea.d.ts.map +1 -1
  218. package/dist/TextArea/TextArea.js +3 -2
  219. package/dist/TextInput/TextInput.d.ts.map +1 -1
  220. package/dist/TextInput/TextInput.js +3 -2
  221. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  222. package/dist/TimeInput/TimeInput.js +3 -2
  223. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  224. package/dist/Timestamp/Timestamp.js +5 -3
  225. package/dist/Token/TokenLink.d.ts.map +1 -1
  226. package/dist/Token/TokenLink.js +2 -2
  227. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  228. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  229. package/dist/Tokenizer/Tokenizer.js +2 -0
  230. package/dist/Tooltip/Tooltip.d.ts +5 -0
  231. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  232. package/dist/Tooltip/useTooltip.d.ts +5 -0
  233. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  234. package/dist/Tooltip/useTooltip.js +39 -24
  235. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  236. package/dist/TopNav/TopNavHeading.js +3 -2
  237. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  238. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  239. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  240. package/dist/TopNav/TopNavMenu.js +4 -3
  241. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  242. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  243. package/dist/Typeahead/BaseTypeahead.js +68 -29
  244. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  245. package/dist/Typeahead/Typeahead.js +3 -2
  246. package/dist/astryx.css +72 -2
  247. package/dist/hooks/index.d.ts +1 -0
  248. package/dist/hooks/index.d.ts.map +1 -1
  249. package/dist/hooks/index.js +1 -0
  250. package/dist/hooks/useFocusTrap.d.ts +7 -4
  251. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  252. package/dist/hooks/useFocusTrap.js +60 -95
  253. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  254. package/dist/hooks/useHotkeys.js +10 -3
  255. package/dist/hooks/useListFocus.d.ts +5 -2
  256. package/dist/hooks/useListFocus.d.ts.map +1 -1
  257. package/dist/hooks/useListFocus.js +12 -6
  258. package/dist/hooks/useMergedRefs.d.ts +18 -0
  259. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  260. package/dist/hooks/useMergedRefs.js +25 -0
  261. package/dist/index.d.ts +1 -0
  262. package/dist/index.d.ts.map +1 -1
  263. package/dist/index.js +1 -0
  264. package/dist/utils/groupItems.d.ts +5 -1
  265. package/dist/utils/groupItems.d.ts.map +1 -1
  266. package/dist/utils/groupItems.js +9 -9
  267. package/dist/utils/index.d.ts +1 -1
  268. package/dist/utils/index.d.ts.map +1 -1
  269. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  270. package/dist/utils/sharedResizeObserver.js +10 -1
  271. package/dist/utils/timeParser.d.ts +1 -1
  272. package/dist/utils/timeParser.d.ts.map +1 -1
  273. package/dist/utils/timeParser.js +26 -12
  274. package/locales/en.json +31 -7
  275. package/locales/pseudo.json +18 -0
  276. package/package.json +8 -3
  277. package/src/AppShell/AppShell.tsx +3 -2
  278. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  279. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  280. package/src/AspectRatio/AspectRatio.tsx +40 -1
  281. package/src/Avatar/Avatar.doc.mjs +2 -1
  282. package/src/Avatar/Avatar.test.tsx +17 -0
  283. package/src/Avatar/Avatar.tsx +61 -38
  284. package/src/AvatarGroup/AvatarGroup.tsx +3 -2
  285. package/src/Banner/Banner.doc.mjs +13 -11
  286. package/src/Banner/Banner.test.tsx +102 -15
  287. package/src/Banner/Banner.tsx +72 -25
  288. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  289. package/src/BottomSheet/BottomSheet.tsx +36 -4
  290. package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +225 -0
  291. package/src/BottomSheet/BottomSheetEdgeTint.tsx +82 -0
  292. package/src/BottomSheet/BottomSheetPanel.test.tsx +66 -0
  293. package/src/BottomSheet/BottomSheetPanel.tsx +19 -0
  294. package/src/BottomSheet/BottomSheetSwitcher.tsx +16 -2
  295. package/src/BottomSheet/useSheetGestures.test.ts +27 -0
  296. package/src/BottomSheet/useSheetGestures.ts +25 -5
  297. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  298. package/src/Button/Button.doc.mjs +22 -0
  299. package/src/Button/Button.test.tsx +13 -0
  300. package/src/Button/Button.tsx +5 -4
  301. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  302. package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
  303. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  304. package/src/Calendar/Calendar.doc.mjs +4 -3
  305. package/src/Calendar/Calendar.test.tsx +52 -0
  306. package/src/Calendar/Calendar.tsx +18 -15
  307. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  308. package/src/Calendar/getInitialFocusDate.ts +93 -0
  309. package/src/Carousel/Carousel.tsx +3 -2
  310. package/src/Chat/ChatComposerInput.tsx +5 -1
  311. package/src/Chat/ChatLayout.tsx +3 -2
  312. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  313. package/src/Chat/ChatMessageList.test.tsx +46 -0
  314. package/src/Chat/ChatMessageList.tsx +28 -4
  315. package/src/Chat/ChatTokenizedText.tsx +17 -10
  316. package/src/Chat/useChatComposerTokens.ts +1 -1
  317. package/src/CheckboxInput/CheckboxInput.tsx +3 -2
  318. package/src/ClickableCard/ClickableCard.tsx +3 -2
  319. package/src/CodeBlock/CodeBlock.doc.mjs +6 -0
  320. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  321. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  322. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  323. package/src/ContextMenu/ContextMenu.tsx +4 -3
  324. package/src/DateInput/DateInput.tsx +3 -2
  325. package/src/DateInput/DateInputTouch.test.tsx +36 -0
  326. package/src/DateInput/TouchDateField.tsx +38 -4
  327. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  328. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  329. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  330. package/src/DateTimeInput/index.ts +1 -0
  331. package/src/Dialog/Dialog.doc.mjs +8 -3
  332. package/src/Dialog/Dialog.test.tsx +261 -1
  333. package/src/Dialog/Dialog.tsx +109 -58
  334. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  335. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  336. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  337. package/src/Field/Field.doc.mjs +13 -2
  338. package/src/FileInput/FileInput.tsx +3 -2
  339. package/src/Heading/Heading.test.tsx +23 -1
  340. package/src/Heading/Heading.tsx +6 -2
  341. package/src/HoverCard/HoverCard.doc.mjs +5 -0
  342. package/src/HoverCard/HoverCard.tsx +6 -0
  343. package/src/HoverCard/useHoverCard.tsx +81 -30
  344. package/src/Item/Item.tsx +5 -4
  345. package/src/Kbd/Kbd.test.tsx +17 -0
  346. package/src/Kbd/Kbd.tsx +10 -3
  347. package/src/Layer/LayerDepthContext.tsx +62 -0
  348. package/src/Layer/index.ts +9 -0
  349. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  350. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  351. package/src/Layer/layerStack.ts +362 -0
  352. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  353. package/src/Layer/useLayerDismissal.ts +173 -0
  354. package/src/Layout/Layout.doc.mjs +2 -2
  355. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  356. package/src/Lightbox/Lightbox.tsx +124 -102
  357. package/src/Link/Link.doc.mjs +39 -0
  358. package/src/Markdown/Markdown.doc.mjs +5 -0
  359. package/src/Markdown/incremental.test.ts +16 -2
  360. package/src/Markdown/index.ts +1 -0
  361. package/src/Markdown/parser.perf.test.ts +96 -1
  362. package/src/Markdown/parser.test.ts +145 -2
  363. package/src/Markdown/parser.ts +244 -29
  364. package/src/MobileNav/MobileNav.tsx +62 -36
  365. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  366. package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
  367. package/src/MultiSelector/MultiSelector.test.tsx +106 -0
  368. package/src/MultiSelector/MultiSelector.tsx +50 -6
  369. package/src/MultiSelector/index.ts +1 -0
  370. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  371. package/src/NumberInput/NumberInput.doc.mjs +5 -0
  372. package/src/Outline/Outline.tsx +3 -2
  373. package/src/OverflowList/OverflowList.tsx +3 -2
  374. package/src/Overlay/Overlay.tsx +3 -2
  375. package/src/Popover/Popover.test.tsx +27 -1
  376. package/src/Popover/usePopover.tsx +31 -28
  377. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  378. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  379. package/src/PowerSearch/PowerSearch.tsx +34 -5
  380. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
  381. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  382. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
  383. package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
  384. package/src/PowerSearch/types.ts +2 -0
  385. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  386. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  387. package/src/Resizable/ResizeHandle.tsx +3 -2
  388. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  389. package/src/SelectableCard/SelectableCard.tsx +3 -2
  390. package/src/Selector/Selector.doc.mjs +12 -1
  391. package/src/Selector/Selector.test.tsx +45 -1
  392. package/src/Selector/Selector.tsx +11 -1
  393. package/src/SideNav/SideNav.doc.mjs +6 -6
  394. package/src/SideNav/SideNav.tsx +4 -2
  395. package/src/SideNav/SideNavHeading.tsx +10 -10
  396. package/src/SideNav/SideNavItem.tsx +4 -2
  397. package/src/Slider/Slider.tsx +3 -2
  398. package/src/Spinner/Spinner.test.tsx +147 -1
  399. package/src/Spinner/Spinner.tsx +120 -101
  400. package/src/Stepper/Step.doc.mjs +98 -0
  401. package/src/Stepper/Step.tsx +1638 -0
  402. package/src/Stepper/StepStatus.ts +26 -0
  403. package/src/Stepper/Stepper.doc.mjs +370 -0
  404. package/src/Stepper/Stepper.test.tsx +1024 -0
  405. package/src/Stepper/Stepper.tsx +258 -0
  406. package/src/Stepper/StepperContext.ts +71 -0
  407. package/src/Stepper/index.ts +18 -0
  408. package/src/Stepper/stepper.stylex.ts +19 -0
  409. package/src/Switch/Switch.tsx +3 -2
  410. package/src/TabList/Tab.doc.mjs +13 -1
  411. package/src/TabList/Tab.tsx +67 -3
  412. package/src/TabList/TabList.doc.mjs +22 -6
  413. package/src/TabList/TabList.test.tsx +580 -4
  414. package/src/TabList/TabList.tsx +490 -22
  415. package/src/TabList/TabListContext.ts +13 -2
  416. package/src/TabList/TabMenu.tsx +3 -2
  417. package/src/TabList/index.ts +6 -2
  418. package/src/Table/BaseTable.tsx +6 -3
  419. package/src/Table/Table.doc.mjs +6 -1
  420. package/src/Table/Table.test.tsx +132 -14
  421. package/src/Table/TableBody.doc.mjs +40 -0
  422. package/src/Table/TableFooter.doc.mjs +40 -0
  423. package/src/Table/TableHeader.doc.mjs +40 -0
  424. package/src/Table/TableRow.tsx +16 -5
  425. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  426. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  427. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  428. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  429. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  430. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  431. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  432. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  433. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  434. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  435. package/src/Table/types.ts +22 -4
  436. package/src/Table/useBaseTablePlugins.ts +5 -0
  437. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  438. package/src/Table/useTablePagination.doc.mjs +7 -0
  439. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  440. package/src/Table/useTableSelection.doc.mjs +31 -0
  441. package/src/Text/Text.test.tsx +23 -1
  442. package/src/Text/Text.tsx +6 -2
  443. package/src/TextArea/TextArea.tsx +3 -2
  444. package/src/TextInput/TextInput.doc.mjs +10 -0
  445. package/src/TextInput/TextInput.tsx +3 -2
  446. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  447. package/src/TimeInput/TimeInput.tsx +2 -2
  448. package/src/Timestamp/Timestamp.tsx +4 -2
  449. package/src/Toast/Toast.doc.mjs +6 -0
  450. package/src/Token/TokenLink.tsx +2 -2
  451. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  452. package/src/Tokenizer/Tokenizer.tsx +4 -0
  453. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  454. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  455. package/src/Tooltip/Tooltip.test.tsx +32 -0
  456. package/src/Tooltip/Tooltip.tsx +5 -0
  457. package/src/Tooltip/useTooltip.tsx +47 -24
  458. package/src/TopNav/TopNavHeading.tsx +3 -2
  459. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  460. package/src/TopNav/TopNavMenu.tsx +4 -3
  461. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  462. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  463. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  464. package/src/Typeahead/Typeahead.test.tsx +49 -0
  465. package/src/Typeahead/Typeahead.tsx +3 -2
  466. package/src/hooks/index.ts +2 -0
  467. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  468. package/src/hooks/useFocusTrap.test.tsx +14 -0
  469. package/src/hooks/useFocusTrap.ts +62 -111
  470. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  471. package/src/hooks/useHotkeys.test.ts +18 -0
  472. package/src/hooks/useHotkeys.ts +10 -3
  473. package/src/hooks/useListFocus.doc.mjs +2 -2
  474. package/src/hooks/useListFocus.test.tsx +65 -3
  475. package/src/hooks/useListFocus.ts +15 -7
  476. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  477. package/src/hooks/useMergedRefs.test.tsx +62 -0
  478. package/src/hooks/useMergedRefs.ts +36 -0
  479. package/src/index.ts +1 -0
  480. package/src/theme/MediaTheme.doc.mjs +5 -5
  481. package/src/utils/groupItems.ts +12 -6
  482. package/src/utils/index.ts +1 -1
  483. package/src/utils/sharedResizeObserver.ts +10 -2
  484. package/src/utils/timeParser.test.ts +6 -1
  485. package/src/utils/timeParser.ts +35 -13
  486. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @file LayerDepthContext.tsx
3
+ * @input React context
4
+ * @output Exports LayerDepthContext, useLayerDepth, LayerDepthProvider
5
+ * @position Layer system; how the dismissal stack learns which layer is nested
6
+ * inside which.
7
+ *
8
+ * SYNC: When modified, update:
9
+ * - /packages/core/src/Layer/index.ts
10
+ * - /packages/core/src/Layer/useLayerDismissal.ts
11
+ *
12
+ * Nesting is read from the React tree rather than the DOM. Two reasons:
13
+ *
14
+ * - **Portals.** A nested overlay routinely renders into `document.body` or the
15
+ * native top layer, so DOM containment reports it as a sibling of the layer
16
+ * it is logically inside. React context flows through `createPortal`, so the
17
+ * tree keeps the relationship the DOM loses.
18
+ * - **Same-commit mounts.** Depth is fixed during render. Effect order is not
19
+ * available then and is misleading anyway: React runs child effects before
20
+ * parent effects, so an inner layer registers first and looks "older" than
21
+ * the outer layer that contains it.
22
+ *
23
+ * There is nothing to mount at the app root: the context defaults to 0, and
24
+ * each layer provides depth for its OWN content, so nesting composes on its own.
25
+ */
26
+ import { type ReactNode } from 'react';
27
+ /**
28
+ * How many layers deep the current subtree is. 0 at the app root; each layer
29
+ * increments it for its own content.
30
+ */
31
+ export declare const LayerDepthContext: import("react").Context<number>;
32
+ /**
33
+ * Read the current nesting depth. A layer calls this to learn its OWN depth —
34
+ * the depth of the subtree it is rendered into, before it pushes its content
35
+ * one level deeper.
36
+ */
37
+ export declare function useLayerDepth(): number;
38
+ /**
39
+ * Wrap a layer's content so any layer opened from inside it registers as
40
+ * nested. Takes no depth prop on purpose: it reads the ambient depth and adds
41
+ * one, so nesting composes without anyone tracking absolute numbers.
42
+ *
43
+ * Renders no DOM element — it is a context boundary only, which is why it takes
44
+ * no `xstyle`/`ref` and exposes no props interface.
45
+ */
46
+ export declare function LayerDepthProvider({ children }: {
47
+ children: ReactNode;
48
+ }): import("react").JSX.Element;
49
+ export declare namespace LayerDepthProvider {
50
+ var displayName: string;
51
+ }
52
+ //# sourceMappingURL=LayerDepthContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayerDepthContext.d.ts","sourceRoot":"","sources":["../../src/Layer/LayerDepthContext.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,iCAAmB,CAAC;AAGlD;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EAAC,QAAQ,EAAC,EAAE;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAC,+BAGnE;yBAHe,kBAAkB"}
@@ -0,0 +1,66 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file LayerDepthContext.tsx
7
+ * @input React context
8
+ * @output Exports LayerDepthContext, useLayerDepth, LayerDepthProvider
9
+ * @position Layer system; how the dismissal stack learns which layer is nested
10
+ * inside which.
11
+ *
12
+ * SYNC: When modified, update:
13
+ * - /packages/core/src/Layer/index.ts
14
+ * - /packages/core/src/Layer/useLayerDismissal.ts
15
+ *
16
+ * Nesting is read from the React tree rather than the DOM. Two reasons:
17
+ *
18
+ * - **Portals.** A nested overlay routinely renders into `document.body` or the
19
+ * native top layer, so DOM containment reports it as a sibling of the layer
20
+ * it is logically inside. React context flows through `createPortal`, so the
21
+ * tree keeps the relationship the DOM loses.
22
+ * - **Same-commit mounts.** Depth is fixed during render. Effect order is not
23
+ * available then and is misleading anyway: React runs child effects before
24
+ * parent effects, so an inner layer registers first and looks "older" than
25
+ * the outer layer that contains it.
26
+ *
27
+ * There is nothing to mount at the app root: the context defaults to 0, and
28
+ * each layer provides depth for its OWN content, so nesting composes on its own.
29
+ */
30
+ import { createContext, use } from 'react';
31
+
32
+ /**
33
+ * How many layers deep the current subtree is. 0 at the app root; each layer
34
+ * increments it for its own content.
35
+ */
36
+ import { jsx as _jsx } from "react/jsx-runtime";
37
+ export const LayerDepthContext = /*#__PURE__*/createContext(0);
38
+ LayerDepthContext.displayName = 'LayerDepthContext';
39
+
40
+ /**
41
+ * Read the current nesting depth. A layer calls this to learn its OWN depth —
42
+ * the depth of the subtree it is rendered into, before it pushes its content
43
+ * one level deeper.
44
+ */
45
+ export function useLayerDepth() {
46
+ return use(LayerDepthContext);
47
+ }
48
+
49
+ /**
50
+ * Wrap a layer's content so any layer opened from inside it registers as
51
+ * nested. Takes no depth prop on purpose: it reads the ambient depth and adds
52
+ * one, so nesting composes without anyone tracking absolute numbers.
53
+ *
54
+ * Renders no DOM element — it is a context boundary only, which is why it takes
55
+ * no `xstyle`/`ref` and exposes no props interface.
56
+ */
57
+ export function LayerDepthProvider({
58
+ children
59
+ }) {
60
+ const depth = use(LayerDepthContext);
61
+ return /*#__PURE__*/_jsx(LayerDepthContext, {
62
+ value: depth + 1,
63
+ children: children
64
+ });
65
+ }
66
+ LayerDepthProvider.displayName = 'LayerDepthProvider';
@@ -13,6 +13,9 @@
13
13
  */
14
14
  export { useLayer } from './useLayer';
15
15
  export type { LayerAlignment, LayerPlacement, ContextRenderProps, FixedRenderProps, ContextLayerOptions, FixedLayerOptions, ContextLayerReturn, FixedLayerReturn, } from './useLayer';
16
+ export { useLayerDismissal } from './useLayerDismissal';
17
+ export type { LayerEscapeBehavior, UseLayerDismissalOptions, UseLayerDismissalReturn, } from './useLayerDismissal';
18
+ export { LayerDepthProvider } from './LayerDepthContext';
16
19
  export { useTouchTrigger, isActionTrigger } from './useTouchTrigger';
17
20
  export type { LayerTouchTrigger, UseTouchTriggerOptions, UseTouchTriggerReturn, } from './useTouchTrigger';
18
21
  export { LayerProvider } from './LayerProvider';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Layer/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,YAAY,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,mBAAmB,CAAC;AACnE,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EAAC,kBAAkB,EAAC,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAC,YAAY,EAAE,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAC7D,YAAY,EAAC,iBAAiB,EAAE,gBAAgB,EAAC,MAAM,gBAAgB,CAAC;AAGxE,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAGzD,OAAO,EAAC,UAAU,EAAE,OAAO,EAAC,MAAM,YAAY,CAAC;AAC/C,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAC,YAAY,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AACrD,YAAY,EACV,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAC,UAAU,EAAE,OAAO,EAAC,MAAM,YAAY,CAAC;AAC/C,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Layer/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,YAAY,EACV,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AACtD,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAC,kBAAkB,EAAC,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,mBAAmB,CAAC;AACnE,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EAAC,kBAAkB,EAAC,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAC,YAAY,EAAE,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAC7D,YAAY,EAAC,iBAAiB,EAAE,gBAAgB,EAAC,MAAM,gBAAgB,CAAC;AAGxE,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAGzD,OAAO,EAAC,UAAU,EAAE,OAAO,EAAC,MAAM,YAAY,CAAC;AAC/C,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAC,YAAY,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AACrD,YAAY,EACV,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAC,UAAU,EAAE,OAAO,EAAC,MAAM,YAAY,CAAC;AAC/C,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC"}
@@ -18,6 +18,10 @@
18
18
 
19
19
  // Core layer hook (remains in Layer)
20
20
  export { useLayer } from "./useLayer.js";
21
+ // Layer dismissal stack — one Escape owner for every overlay family
22
+ export { useLayerDismissal } from "./useLayerDismissal.js";
23
+ export { LayerDepthProvider } from "./LayerDepthContext.js";
24
+
21
25
  // Shared touch behavior for the hover layers
22
26
  export { useTouchTrigger, isActionTrigger } from "./useTouchTrigger.js";
23
27
  // Layer provider
@@ -0,0 +1,98 @@
1
+ /**
2
+ * What a layer does with an Escape press that reaches it.
3
+ *
4
+ * - `'close'` — dismiss this layer and consume the press. One Escape closes
5
+ * exactly this layer. The default, and correct for every dismissible layer:
6
+ * modals, popovers, menus, comboboxes, and hover layers alike.
7
+ * - `'block'` — consume the press WITHOUT dismissing. For a layer that requires
8
+ * an explicit choice (`Dialog purpose="required"`): Escape must not dismiss
9
+ * it, and must not fall through and dismiss something behind it either.
10
+ *
11
+ * There is deliberately no "dismiss but let the press continue" variant. Escape
12
+ * affects exactly one layer, always — a rule with no per-component exceptions
13
+ * is one users can predict. Hover layers were the tempting exception (the user
14
+ * never opened the tip, so why should it eat their press?), but guessing wrong
15
+ * that way is destructive: someone dismissing a stray tooltip over a form would
16
+ * lose the whole dialog. Guessing wrong the other way costs one keystroke.
17
+ */
18
+ export type LayerEscapeBehavior = 'close' | 'block';
19
+ export interface LayerStackEntry {
20
+ /**
21
+ * Identity for removal; also the equality check for top-most, and the key
22
+ * `seq` is assigned against so a layer keeps its place across re-registration.
23
+ */
24
+ token: object;
25
+ /**
26
+ * Nesting depth from the React tree (see LayerDepthContext), NOT the DOM.
27
+ * Context flows through portals and is known during render, so it is correct
28
+ * for layers that portal out and for an inner + outer layer that open in the
29
+ * same commit — the case where effect order lies, because React runs child
30
+ * effects before parent effects.
31
+ */
32
+ depth: number;
33
+ /**
34
+ * Monotonic registration order; breaks ties between unrelated layers. For a
35
+ * layer that registers when it opens this is open order; a hover layer
36
+ * registers at mount and answers `isPresent` instead, so for two of those it
37
+ * is mount order rather than which one appeared first.
38
+ *
39
+ * Keyed to `token`, not to the registration, because a layer re-registers
40
+ * whenever its behavior or depth changes and must not overtake the layers
41
+ * above it.
42
+ */
43
+ seq: number;
44
+ behavior: LayerEscapeBehavior;
45
+ /**
46
+ * The layer's container element, read lazily at press time. Used only to
47
+ * break ties between layers the React tree reports at the same depth — a raw
48
+ * `useFocusTrap` cannot wrap its content in a depth provider (it renders
49
+ * nothing), so DOM containment is the only nesting signal those have.
50
+ */
51
+ getContainer?: () => HTMLElement | null;
52
+ /**
53
+ * Whether the layer is really on screen right now, asked at press time.
54
+ *
55
+ * Registration alone is not proof: a layer whose open state lives in React
56
+ * state can lag a frame behind the DOM, so gating registration on that state
57
+ * drops presses that arrive in the gap. Such layers register for their whole
58
+ * lifetime and answer this from the DOM instead. Omit it to mean "registered
59
+ * means present", which is true for layers that register on open.
60
+ */
61
+ isPresent?: () => boolean;
62
+ dismiss: () => void;
63
+ }
64
+ /**
65
+ * Whether the user is part-way through composing text with an IME.
66
+ *
67
+ * The stack reads this to decide what an Escape means, and layers read it
68
+ * through `useLayerDismissal`'s `shouldDismissOnCloseRequest` for the close
69
+ * requests that arrive without a keydown to inspect — the Android back
70
+ * gesture, the platform close watcher, or a composing Escape whose keydown
71
+ * never reached this listener because content stopped it. Mid-composition,
72
+ * those are far more likely to be the user backing out of a half-formed
73
+ * character than a request to dismiss a layer, and guessing wrong that way
74
+ * costs them everything they have typed into it.
75
+ *
76
+ * Scope, deliberately page-wide: a composition in a field behind a non-modal
77
+ * layer also suppresses that layer's close requests. It is one keypress to
78
+ * recover from, and the alternative — matching the composing element against
79
+ * a layer's container — cannot work for the layers that do not have one.
80
+ *
81
+ * A composition already running when the first layer registers is invisible
82
+ * here: the listeners go on with the stack's own.
83
+ */
84
+ export declare function isTextComposing(): boolean;
85
+ /**
86
+ * Whether `token` identifies the top-most layer. Overlays that still own a
87
+ * dismissal channel the stack does not model yet (outside-press, swipe) can
88
+ * gate on this so every channel agrees on who is on top.
89
+ */
90
+ export declare function isTopmostLayer(token: object): boolean;
91
+ /**
92
+ * Add a layer to the stack for as long as it is active. Returns the unregister
93
+ * function; callers own calling it.
94
+ */
95
+ export declare function registerLayer(entry: Omit<LayerStackEntry, 'seq'>): () => void;
96
+ /** Test-only: drop all entries and detach the listener. */
97
+ export declare function resetLayerStackForTests(): void;
98
+ //# sourceMappingURL=layerStack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layerStack.d.ts","sourceRoot":"","sources":["../../src/Layer/layerStack.ts"],"names":[],"mappings":"AAoEA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,OAAO,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACxC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AA2ED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AA2BD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAwED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAc7E;AAED,2DAA2D;AAC3D,wBAAgB,uBAAuB,IAAI,IAAI,CAM9C"}
@@ -0,0 +1,306 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file layerStack.ts
7
+ * @input Uses the isImeKeyEvent predicate from utils/ime
8
+ * @output Exports the shared layer dismissal stack: registration, top-most
9
+ * ordering, and the single document-level Escape listener
10
+ * @position Internal to the Layer system; consumed by useLayerDismissal, which
11
+ * is what overlays actually call. Not exported from the package root.
12
+ *
13
+ * SYNC: When modified, update:
14
+ * - /packages/core/src/Layer/useLayerDismissal.ts
15
+ * - /packages/core/src/Layer/useLayerDismissal.test.tsx
16
+ *
17
+ * ## Why one stack
18
+ *
19
+ * Every overlay used to own its own Escape listener, so one press dismissed
20
+ * every open layer (a popover inside a Dialog closed both; a Modal opened from
21
+ * inside another Modal closed both). Patching this per primitive produced
22
+ * parallel registries — the focus-trap Escape stack, Drawer's LIFO registry,
23
+ * useScrollLock's counter. This module is the single registry those collapse
24
+ * into. One press dismisses exactly one layer.
25
+ *
26
+ * ## Only the top layer acts
27
+ *
28
+ * The stack owns ONE `keydown` listener on `document` and routes the press to
29
+ * the top-most layer itself. Overlays do not listen; they register and say what
30
+ * they want done. The alternative — every layer listens and filters itself out
31
+ * — is what forces `stopPropagation()` choreography between element-level and
32
+ * document-level listeners, and it silently breaks whenever a layer forgets.
33
+ *
34
+ * ## Bubble phase, not capture
35
+ *
36
+ * The listener is on the BUBBLE phase so content inside a layer can claim the
37
+ * press first, either by `stopPropagation()` (the press never reaches us) or by
38
+ * `preventDefault()` (we see it and stand down). Editors are the motivating
39
+ * case: Monaco and the rich-text editor use Escape to close their own find
40
+ * widget or autocomplete, and that must win over dismissing the Dialog they sit
41
+ * in. A capture-phase listener would take the press away from them.
42
+ *
43
+ * ## The stack, not the browser, decides
44
+ *
45
+ * When the stack handles a press it calls `preventDefault()`, which suppresses
46
+ * the browser's own close-watcher behavior — both `<dialog>`'s `cancel` event
47
+ * and `popover="auto"` light-dismiss (verified in Chromium; the default action
48
+ * runs after propagation, so a bubble-phase `preventDefault` still beats it).
49
+ * That is deliberate: the native top layer only covers `showModal()` and
50
+ * `popover="auto"`, so relying on it would leave non-modal `show()` drawers,
51
+ * `popover="manual"` layers, and older browsers on a second, differently-behaved
52
+ * code path. One code path, one ordering, everywhere.
53
+ *
54
+ * ## An Escape that cancels an IME composition
55
+ *
56
+ * That press is not a dismissal, so no layer acts on it — but the stack still
57
+ * claims it, because an unclaimed Escape is exactly what makes the browser
58
+ * raise a close request, and that request dismisses the layer through its
59
+ * `cancel` handler on the same keypress. Claiming is the whole fix; the guard
60
+ * that only returns measures identically to no guard at all.
61
+ *
62
+ * Close requests that arrive with no keydown to read — the Android back
63
+ * gesture, the platform close watcher — are answered by the hosts, which ask
64
+ * `isTextComposing()` through `useLayerDismissal`.
65
+ */
66
+ import { isImeKeyEvent } from "../utils/ime.js";
67
+
68
+ /**
69
+ * What a layer does with an Escape press that reaches it.
70
+ *
71
+ * - `'close'` — dismiss this layer and consume the press. One Escape closes
72
+ * exactly this layer. The default, and correct for every dismissible layer:
73
+ * modals, popovers, menus, comboboxes, and hover layers alike.
74
+ * - `'block'` — consume the press WITHOUT dismissing. For a layer that requires
75
+ * an explicit choice (`Dialog purpose="required"`): Escape must not dismiss
76
+ * it, and must not fall through and dismiss something behind it either.
77
+ *
78
+ * There is deliberately no "dismiss but let the press continue" variant. Escape
79
+ * affects exactly one layer, always — a rule with no per-component exceptions
80
+ * is one users can predict. Hover layers were the tempting exception (the user
81
+ * never opened the tip, so why should it eat their press?), but guessing wrong
82
+ * that way is destructive: someone dismissing a stray tooltip over a form would
83
+ * lose the whole dialog. Guessing wrong the other way costs one keystroke.
84
+ */
85
+
86
+ const entries = [];
87
+ // Registration order per layer identity. A layer re-registers whenever its
88
+ // depth or behavior changes — a Dialog whose `purpose` flips while open, a focus trap
89
+ // that moves — and StrictMode remounts every effect once more on top of that.
90
+ // Counting registrations instead would hand the layer a fresh, higher seq each
91
+ // time and promote it above the layers opened over it.
92
+ let seqByToken = new WeakMap();
93
+ let nextSeq = 0;
94
+ let isListening = false;
95
+ function seqFor(token) {
96
+ const existing = seqByToken.get(token);
97
+ if (existing !== undefined) {
98
+ return existing;
99
+ }
100
+ const seq = nextSeq++;
101
+ seqByToken.set(token, seq);
102
+ return seq;
103
+ }
104
+
105
+ /**
106
+ * Order two entries: positive when `a` is above `b`, negative when below.
107
+ * Three keys, each earning its place:
108
+ *
109
+ * 1. **Depth.** The layer nested inside the other is on top. This is what makes
110
+ * an inner-and-outer pair that mount in the SAME commit come out right;
111
+ * registration order alone gets it backwards, because React runs child
112
+ * effects before parent effects.
113
+ * 2. **DOM containment.** Depth only moves when a layer wraps its content in a
114
+ * `LayerDepthProvider`, which a bare `useFocusTrap` cannot do — it renders
115
+ * nothing. For those, containment recovers the nesting the tree did not
116
+ * report.
117
+ * 3. **Registration order.** For unrelated layers, the one that registered
118
+ * later is on top, matching how the browser's own top layer stacks. A layer
119
+ * keeps its first place for as long as its identity lives, so one that
120
+ * closes and reopens does not move to the top — see the known edge in the
121
+ * pull request.
122
+ *
123
+ * Modality is deliberately NOT a key. Ranking modals above everything sounds
124
+ * right and is not: a hover tip shown inside a modal renders on top of it, so
125
+ * a modal-first rule would hand the press to the dialog and strand the tip.
126
+ * Nesting already covers the case modality was meant to catch — a layer opened
127
+ * from inside another is deeper, modal or not.
128
+ *
129
+ * Note this is a PARTIAL order: containment relates only nested pairs. Callers
130
+ * must resolve the top with a pairwise max scan, never `Array.sort`, whose
131
+ * result is undefined for a non-transitive comparator.
132
+ */
133
+ function compareEntries(a, b) {
134
+ if (a.depth !== b.depth) {
135
+ return a.depth - b.depth;
136
+ }
137
+ const aEl = a.getContainer?.() ?? null;
138
+ const bEl = b.getContainer?.() ?? null;
139
+ if (aEl != null && bEl != null && aEl !== bEl) {
140
+ if (bEl.contains(aEl)) {
141
+ return 1;
142
+ }
143
+ if (aEl.contains(bEl)) {
144
+ return -1;
145
+ }
146
+ }
147
+ return a.seq - b.seq;
148
+ }
149
+ function isPresentEntry(entry) {
150
+ return entry.isPresent?.() ?? true;
151
+ }
152
+
153
+ // Whether a text composition is running anywhere on the page. Tracked from
154
+ // composition events because the DOM offers no way to ask.
155
+ let isComposing = false;
156
+
157
+ /**
158
+ * Whether the user is part-way through composing text with an IME.
159
+ *
160
+ * The stack reads this to decide what an Escape means, and layers read it
161
+ * through `useLayerDismissal`'s `shouldDismissOnCloseRequest` for the close
162
+ * requests that arrive without a keydown to inspect — the Android back
163
+ * gesture, the platform close watcher, or a composing Escape whose keydown
164
+ * never reached this listener because content stopped it. Mid-composition,
165
+ * those are far more likely to be the user backing out of a half-formed
166
+ * character than a request to dismiss a layer, and guessing wrong that way
167
+ * costs them everything they have typed into it.
168
+ *
169
+ * Scope, deliberately page-wide: a composition in a field behind a non-modal
170
+ * layer also suppresses that layer's close requests. It is one keypress to
171
+ * recover from, and the alternative — matching the composing element against
172
+ * a layer's container — cannot work for the layers that do not have one.
173
+ *
174
+ * A composition already running when the first layer registers is invisible
175
+ * here: the listeners go on with the stack's own.
176
+ */
177
+ export function isTextComposing() {
178
+ return isComposing;
179
+ }
180
+ function handleCompositionStart() {
181
+ isComposing = true;
182
+ }
183
+
184
+ // Focus leaving the field ends the composition too. Clearing on blur as well
185
+ // means a compositionend this listener never sees cannot leave the flag stuck
186
+ // on, swallowing every later close request.
187
+ function handleCompositionEnd() {
188
+ isComposing = false;
189
+ }
190
+
191
+ /** The top-most present layer, or null when nothing is on screen. */
192
+ function topPresentEntry() {
193
+ let top = null;
194
+ for (const entry of entries) {
195
+ if (!isPresentEntry(entry)) {
196
+ continue;
197
+ }
198
+ if (top == null || compareEntries(entry, top) > 0) {
199
+ top = entry;
200
+ }
201
+ }
202
+ return top;
203
+ }
204
+
205
+ /**
206
+ * Whether `token` identifies the top-most layer. Overlays that still own a
207
+ * dismissal channel the stack does not model yet (outside-press, swipe) can
208
+ * gate on this so every channel agrees on who is on top.
209
+ */
210
+ export function isTopmostLayer(token) {
211
+ return topPresentEntry()?.token === token;
212
+ }
213
+
214
+ /** Resolve an Escape press against the stack. Returns whether it was handled. */
215
+ function dispatchEscape() {
216
+ const top = topPresentEntry();
217
+ if (top == null) {
218
+ return false;
219
+ }
220
+ if (top.behavior === 'block') {
221
+ return true;
222
+ }
223
+ top.dismiss();
224
+ return true;
225
+ }
226
+ function handleKeyDown(event) {
227
+ if (event.key !== 'Escape') {
228
+ return;
229
+ }
230
+ // An IME user pressing Escape is cancelling a composition, not dismissing a
231
+ // layer. Claim the press anyway, then stand down: left unclaimed, the browser
232
+ // raises its own close request, which arrives at a layer's `cancel` handler
233
+ // and dismisses it on this very keypress — measured in Chromium, where a
234
+ // guard that only returns behaves exactly like no guard at all. Claim it only
235
+ // while a layer is on screen; with nothing to protect, the press is not ours.
236
+ if (isImeKeyEvent(event)) {
237
+ if (topPresentEntry() != null) {
238
+ event.preventDefault();
239
+ }
240
+ return;
241
+ }
242
+ // Content inside a layer already claimed this press (see the bubble-phase
243
+ // note in the file header). Honor it and leave the default action alone.
244
+ if (event.defaultPrevented) {
245
+ return;
246
+ }
247
+ if (dispatchEscape()) {
248
+ // Suppress the browser's own close-watcher so it cannot dismiss a second
249
+ // layer behind our back. Propagation is left alone: consumers may
250
+ // legitimately listen for Escape on window, and the stack's decision is
251
+ // already recorded in defaultPrevented.
252
+ event.preventDefault();
253
+ }
254
+ }
255
+ function startListening() {
256
+ if (isListening || typeof document === 'undefined') {
257
+ return;
258
+ }
259
+ document.addEventListener('keydown', handleKeyDown);
260
+ document.addEventListener('compositionstart', handleCompositionStart, true);
261
+ document.addEventListener('compositionend', handleCompositionEnd, true);
262
+ document.addEventListener('blur', handleCompositionEnd, true);
263
+ isListening = true;
264
+ }
265
+ function stopListening() {
266
+ if (!isListening || typeof document === 'undefined') {
267
+ return;
268
+ }
269
+ document.removeEventListener('keydown', handleKeyDown);
270
+ document.removeEventListener('compositionstart', handleCompositionStart, true);
271
+ document.removeEventListener('compositionend', handleCompositionEnd, true);
272
+ document.removeEventListener('blur', handleCompositionEnd, true);
273
+ isComposing = false;
274
+ isListening = false;
275
+ }
276
+
277
+ /**
278
+ * Add a layer to the stack for as long as it is active. Returns the unregister
279
+ * function; callers own calling it.
280
+ */
281
+ export function registerLayer(entry) {
282
+ const full = {
283
+ ...entry,
284
+ seq: seqFor(entry.token)
285
+ };
286
+ entries.push(full);
287
+ startListening();
288
+ return () => {
289
+ const index = entries.indexOf(full);
290
+ if (index !== -1) {
291
+ entries.splice(index, 1);
292
+ }
293
+ if (entries.length === 0) {
294
+ stopListening();
295
+ }
296
+ };
297
+ }
298
+
299
+ /** Test-only: drop all entries and detach the listener. */
300
+ export function resetLayerStackForTests() {
301
+ entries.length = 0;
302
+ seqByToken = new WeakMap();
303
+ nextSeq = 0;
304
+ isComposing = false;
305
+ stopListening();
306
+ }
@@ -0,0 +1,93 @@
1
+ import { type LayerEscapeBehavior } from './layerStack';
2
+ export type { LayerEscapeBehavior };
3
+ export interface UseLayerDismissalOptions {
4
+ /**
5
+ * Whether this layer is participating right now. Usually the layer's open
6
+ * state. A layer whose open state lags the DOM should pass `true` for its
7
+ * lifetime and answer `isPresent` from the DOM instead.
8
+ */
9
+ isActive: boolean;
10
+ /**
11
+ * Dismiss this layer. Called by the stack when this layer is the one that
12
+ * should respond to an Escape press. Not called for `escapeBehavior: 'block'`.
13
+ */
14
+ onDismiss: () => void;
15
+ /**
16
+ * What this layer does with an Escape press that reaches it.
17
+ * @default 'close'
18
+ */
19
+ escapeBehavior?: LayerEscapeBehavior;
20
+ /**
21
+ * This layer's container element, read lazily. Optional: supply it when the
22
+ * layer cannot wrap its content in `LayerDepthProvider` (a bare focus trap
23
+ * renders nothing), so nesting can still be recovered from the DOM.
24
+ */
25
+ getContainer?: () => HTMLElement | null;
26
+ /**
27
+ * Whether the layer is really on screen, asked at press time. Supply it when
28
+ * `isActive` is `true` for the layer's lifetime because its open state lags
29
+ * the DOM by a frame; the stack skips layers that answer `false`.
30
+ */
31
+ isPresent?: () => boolean;
32
+ /**
33
+ * Whether this layer takes part in the shared stack at all. When `false` the
34
+ * layer is invisible to dismissal: a press flows past it to the layer below,
35
+ * exactly as if it were not open.
36
+ *
37
+ * Deliberately separate from `escapeBehavior: 'block'` — `'block'` is a layer
38
+ * that is present and swallows the press; this is a layer that is not there
39
+ * at all. One case uses it: `Dialog`'s inline rendering mode, where content
40
+ * sits in normal flow with nothing layered over anything.
41
+ *
42
+ * A controlled layer is NOT one of these. It stays registered and takes the
43
+ * press like any other, answering it by calling the consumer's change
44
+ * handler; whether it then closes is the consumer's decision.
45
+ *
46
+ * Layers that are never Escape-dismissible (toasts) should simply not call
47
+ * this hook.
48
+ *
49
+ * @default true
50
+ */
51
+ isEnabled?: boolean;
52
+ }
53
+ export interface UseLayerDismissalReturn {
54
+ /**
55
+ * Whether a close request the browser started on its own — a `<dialog>`'s
56
+ * `cancel`, the Android back gesture, the platform close watcher — should
57
+ * dismiss this layer. As well as the top-most rule the stack applies to an
58
+ * Escape press, it declines a request that arrives while an IME composition
59
+ * is running, which no `cancel` handler can tell on its own because the
60
+ * event carries no composition state.
61
+ */
62
+ shouldDismissOnCloseRequest: () => boolean;
63
+ }
64
+ /**
65
+ * Join the shared layer dismissal stack for as long as this layer is active.
66
+ *
67
+ * The layer does NOT attach a key listener — the stack owns one listener and
68
+ * routes each Escape press to the top-most REGISTERED layer, so one press
69
+ * dismisses exactly one of them. Dialog (and what is built on it), Popover and
70
+ * the menus built on it, Tooltip, HoverCard, Lightbox and MobileNav register
71
+ * today; `BottomSheetSwitcher` registers through its focus trap when modal and
72
+ * still handles its own press when not.
73
+ *
74
+ * `BottomSheet`, `CommandPalette`, `ContextMenu`, `DropdownMenuSubMenu`,
75
+ * `PowerSearchEditPopover` and lab's `Drawer` still run their own Escape
76
+ * listener. They stay safe next to the stack only because each claims the press
77
+ * at element level, and the stack stands down on an already-`defaultPrevented`
78
+ * press — but a registered layer opened INSIDE one of them does not get the
79
+ * press: the host takes it and closes instead. Migrating them is the fix.
80
+ *
81
+ * Wrap the layer's own content in `LayerDepthProvider` so anything opened from
82
+ * inside it registers as nested.
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * useLayerDismissal({
87
+ * isActive: isOpen,
88
+ * onDismiss: () => onOpenChange(false),
89
+ * });
90
+ * ```
91
+ */
92
+ export declare function useLayerDismissal(options: UseLayerDismissalOptions): UseLayerDismissalReturn;
93
+ //# sourceMappingURL=useLayerDismissal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLayerDismissal.d.ts","sourceRoot":"","sources":["../../src/Layer/useLayerDismissal.ts"],"names":[],"mappings":"AAqBA,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAC,mBAAmB,EAAC,CAAC;AAElC,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACxC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;;OAOG;IACH,2BAA2B,EAAE,MAAM,OAAO,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAChC,uBAAuB,CAiDzB"}