@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,362 @@
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
+
67
+ import {isImeKeyEvent} from '../utils/ime';
68
+
69
+ /**
70
+ * What a layer does with an Escape press that reaches it.
71
+ *
72
+ * - `'close'` — dismiss this layer and consume the press. One Escape closes
73
+ * exactly this layer. The default, and correct for every dismissible layer:
74
+ * modals, popovers, menus, comboboxes, and hover layers alike.
75
+ * - `'block'` — consume the press WITHOUT dismissing. For a layer that requires
76
+ * an explicit choice (`Dialog purpose="required"`): Escape must not dismiss
77
+ * it, and must not fall through and dismiss something behind it either.
78
+ *
79
+ * There is deliberately no "dismiss but let the press continue" variant. Escape
80
+ * affects exactly one layer, always — a rule with no per-component exceptions
81
+ * is one users can predict. Hover layers were the tempting exception (the user
82
+ * never opened the tip, so why should it eat their press?), but guessing wrong
83
+ * that way is destructive: someone dismissing a stray tooltip over a form would
84
+ * lose the whole dialog. Guessing wrong the other way costs one keystroke.
85
+ */
86
+ export type LayerEscapeBehavior = 'close' | 'block';
87
+
88
+ export interface LayerStackEntry {
89
+ /**
90
+ * Identity for removal; also the equality check for top-most, and the key
91
+ * `seq` is assigned against so a layer keeps its place across re-registration.
92
+ */
93
+ token: object;
94
+ /**
95
+ * Nesting depth from the React tree (see LayerDepthContext), NOT the DOM.
96
+ * Context flows through portals and is known during render, so it is correct
97
+ * for layers that portal out and for an inner + outer layer that open in the
98
+ * same commit — the case where effect order lies, because React runs child
99
+ * effects before parent effects.
100
+ */
101
+ depth: number;
102
+ /**
103
+ * Monotonic registration order; breaks ties between unrelated layers. For a
104
+ * layer that registers when it opens this is open order; a hover layer
105
+ * registers at mount and answers `isPresent` instead, so for two of those it
106
+ * is mount order rather than which one appeared first.
107
+ *
108
+ * Keyed to `token`, not to the registration, because a layer re-registers
109
+ * whenever its behavior or depth changes and must not overtake the layers
110
+ * above it.
111
+ */
112
+ seq: number;
113
+ behavior: LayerEscapeBehavior;
114
+ /**
115
+ * The layer's container element, read lazily at press time. Used only to
116
+ * break ties between layers the React tree reports at the same depth — a raw
117
+ * `useFocusTrap` cannot wrap its content in a depth provider (it renders
118
+ * nothing), so DOM containment is the only nesting signal those have.
119
+ */
120
+ getContainer?: () => HTMLElement | null;
121
+ /**
122
+ * Whether the layer is really on screen right now, asked at press time.
123
+ *
124
+ * Registration alone is not proof: a layer whose open state lives in React
125
+ * state can lag a frame behind the DOM, so gating registration on that state
126
+ * drops presses that arrive in the gap. Such layers register for their whole
127
+ * lifetime and answer this from the DOM instead. Omit it to mean "registered
128
+ * means present", which is true for layers that register on open.
129
+ */
130
+ isPresent?: () => boolean;
131
+ dismiss: () => void;
132
+ }
133
+
134
+ const entries: LayerStackEntry[] = [];
135
+ // Registration order per layer identity. A layer re-registers whenever its
136
+ // depth or behavior changes — a Dialog whose `purpose` flips while open, a focus trap
137
+ // that moves — and StrictMode remounts every effect once more on top of that.
138
+ // Counting registrations instead would hand the layer a fresh, higher seq each
139
+ // time and promote it above the layers opened over it.
140
+ let seqByToken = new WeakMap<object, number>();
141
+ let nextSeq = 0;
142
+ let isListening = false;
143
+
144
+ function seqFor(token: object): number {
145
+ const existing = seqByToken.get(token);
146
+ if (existing !== undefined) {
147
+ return existing;
148
+ }
149
+ const seq = nextSeq++;
150
+ seqByToken.set(token, seq);
151
+ return seq;
152
+ }
153
+
154
+ /**
155
+ * Order two entries: positive when `a` is above `b`, negative when below.
156
+ * Three keys, each earning its place:
157
+ *
158
+ * 1. **Depth.** The layer nested inside the other is on top. This is what makes
159
+ * an inner-and-outer pair that mount in the SAME commit come out right;
160
+ * registration order alone gets it backwards, because React runs child
161
+ * effects before parent effects.
162
+ * 2. **DOM containment.** Depth only moves when a layer wraps its content in a
163
+ * `LayerDepthProvider`, which a bare `useFocusTrap` cannot do — it renders
164
+ * nothing. For those, containment recovers the nesting the tree did not
165
+ * report.
166
+ * 3. **Registration order.** For unrelated layers, the one that registered
167
+ * later is on top, matching how the browser's own top layer stacks. A layer
168
+ * keeps its first place for as long as its identity lives, so one that
169
+ * closes and reopens does not move to the top — see the known edge in the
170
+ * pull request.
171
+ *
172
+ * Modality is deliberately NOT a key. Ranking modals above everything sounds
173
+ * right and is not: a hover tip shown inside a modal renders on top of it, so
174
+ * a modal-first rule would hand the press to the dialog and strand the tip.
175
+ * Nesting already covers the case modality was meant to catch — a layer opened
176
+ * from inside another is deeper, modal or not.
177
+ *
178
+ * Note this is a PARTIAL order: containment relates only nested pairs. Callers
179
+ * must resolve the top with a pairwise max scan, never `Array.sort`, whose
180
+ * result is undefined for a non-transitive comparator.
181
+ */
182
+ function compareEntries(a: LayerStackEntry, b: LayerStackEntry): number {
183
+ if (a.depth !== b.depth) {
184
+ return a.depth - b.depth;
185
+ }
186
+ const aEl = a.getContainer?.() ?? null;
187
+ const bEl = b.getContainer?.() ?? null;
188
+ if (aEl != null && bEl != null && aEl !== bEl) {
189
+ if (bEl.contains(aEl)) {
190
+ return 1;
191
+ }
192
+ if (aEl.contains(bEl)) {
193
+ return -1;
194
+ }
195
+ }
196
+ return a.seq - b.seq;
197
+ }
198
+
199
+ function isPresentEntry(entry: LayerStackEntry): boolean {
200
+ return entry.isPresent?.() ?? true;
201
+ }
202
+
203
+ // Whether a text composition is running anywhere on the page. Tracked from
204
+ // composition events because the DOM offers no way to ask.
205
+ let isComposing = false;
206
+
207
+ /**
208
+ * Whether the user is part-way through composing text with an IME.
209
+ *
210
+ * The stack reads this to decide what an Escape means, and layers read it
211
+ * through `useLayerDismissal`'s `shouldDismissOnCloseRequest` for the close
212
+ * requests that arrive without a keydown to inspect — the Android back
213
+ * gesture, the platform close watcher, or a composing Escape whose keydown
214
+ * never reached this listener because content stopped it. Mid-composition,
215
+ * those are far more likely to be the user backing out of a half-formed
216
+ * character than a request to dismiss a layer, and guessing wrong that way
217
+ * costs them everything they have typed into it.
218
+ *
219
+ * Scope, deliberately page-wide: a composition in a field behind a non-modal
220
+ * layer also suppresses that layer's close requests. It is one keypress to
221
+ * recover from, and the alternative — matching the composing element against
222
+ * a layer's container — cannot work for the layers that do not have one.
223
+ *
224
+ * A composition already running when the first layer registers is invisible
225
+ * here: the listeners go on with the stack's own.
226
+ */
227
+ export function isTextComposing(): boolean {
228
+ return isComposing;
229
+ }
230
+
231
+ function handleCompositionStart(): void {
232
+ isComposing = true;
233
+ }
234
+
235
+ // Focus leaving the field ends the composition too. Clearing on blur as well
236
+ // means a compositionend this listener never sees cannot leave the flag stuck
237
+ // on, swallowing every later close request.
238
+ function handleCompositionEnd(): void {
239
+ isComposing = false;
240
+ }
241
+
242
+ /** The top-most present layer, or null when nothing is on screen. */
243
+ function topPresentEntry(): LayerStackEntry | null {
244
+ let top: LayerStackEntry | null = null;
245
+ for (const entry of entries) {
246
+ if (!isPresentEntry(entry)) {
247
+ continue;
248
+ }
249
+ if (top == null || compareEntries(entry, top) > 0) {
250
+ top = entry;
251
+ }
252
+ }
253
+ return top;
254
+ }
255
+
256
+ /**
257
+ * Whether `token` identifies the top-most layer. Overlays that still own a
258
+ * dismissal channel the stack does not model yet (outside-press, swipe) can
259
+ * gate on this so every channel agrees on who is on top.
260
+ */
261
+ export function isTopmostLayer(token: object): boolean {
262
+ return topPresentEntry()?.token === token;
263
+ }
264
+
265
+ /** Resolve an Escape press against the stack. Returns whether it was handled. */
266
+ function dispatchEscape(): boolean {
267
+ const top = topPresentEntry();
268
+ if (top == null) {
269
+ return false;
270
+ }
271
+ if (top.behavior === 'block') {
272
+ return true;
273
+ }
274
+ top.dismiss();
275
+ return true;
276
+ }
277
+
278
+ function handleKeyDown(event: KeyboardEvent): void {
279
+ if (event.key !== 'Escape') {
280
+ return;
281
+ }
282
+ // An IME user pressing Escape is cancelling a composition, not dismissing a
283
+ // layer. Claim the press anyway, then stand down: left unclaimed, the browser
284
+ // raises its own close request, which arrives at a layer's `cancel` handler
285
+ // and dismisses it on this very keypress — measured in Chromium, where a
286
+ // guard that only returns behaves exactly like no guard at all. Claim it only
287
+ // while a layer is on screen; with nothing to protect, the press is not ours.
288
+ if (isImeKeyEvent(event)) {
289
+ if (topPresentEntry() != null) {
290
+ event.preventDefault();
291
+ }
292
+ return;
293
+ }
294
+ // Content inside a layer already claimed this press (see the bubble-phase
295
+ // note in the file header). Honor it and leave the default action alone.
296
+ if (event.defaultPrevented) {
297
+ return;
298
+ }
299
+ if (dispatchEscape()) {
300
+ // Suppress the browser's own close-watcher so it cannot dismiss a second
301
+ // layer behind our back. Propagation is left alone: consumers may
302
+ // legitimately listen for Escape on window, and the stack's decision is
303
+ // already recorded in defaultPrevented.
304
+ event.preventDefault();
305
+ }
306
+ }
307
+
308
+ function startListening(): void {
309
+ if (isListening || typeof document === 'undefined') {
310
+ return;
311
+ }
312
+ document.addEventListener('keydown', handleKeyDown);
313
+ document.addEventListener('compositionstart', handleCompositionStart, true);
314
+ document.addEventListener('compositionend', handleCompositionEnd, true);
315
+ document.addEventListener('blur', handleCompositionEnd, true);
316
+ isListening = true;
317
+ }
318
+
319
+ function stopListening(): void {
320
+ if (!isListening || typeof document === 'undefined') {
321
+ return;
322
+ }
323
+ document.removeEventListener('keydown', handleKeyDown);
324
+ document.removeEventListener(
325
+ 'compositionstart',
326
+ handleCompositionStart,
327
+ true,
328
+ );
329
+ document.removeEventListener('compositionend', handleCompositionEnd, true);
330
+ document.removeEventListener('blur', handleCompositionEnd, true);
331
+ isComposing = false;
332
+ isListening = false;
333
+ }
334
+
335
+ /**
336
+ * Add a layer to the stack for as long as it is active. Returns the unregister
337
+ * function; callers own calling it.
338
+ */
339
+ export function registerLayer(entry: Omit<LayerStackEntry, 'seq'>): () => void {
340
+ const full: LayerStackEntry = {...entry, seq: seqFor(entry.token)};
341
+ entries.push(full);
342
+ startListening();
343
+
344
+ return () => {
345
+ const index = entries.indexOf(full);
346
+ if (index !== -1) {
347
+ entries.splice(index, 1);
348
+ }
349
+ if (entries.length === 0) {
350
+ stopListening();
351
+ }
352
+ };
353
+ }
354
+
355
+ /** Test-only: drop all entries and detach the listener. */
356
+ export function resetLayerStackForTests(): void {
357
+ entries.length = 0;
358
+ seqByToken = new WeakMap();
359
+ nextSeq = 0;
360
+ isComposing = false;
361
+ stopListening();
362
+ }