@astryxdesign/core 0.7.5 → 0.7.6-canary.bb18c81

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 (353) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +5 -5
  4. package/dist/Avatar/Avatar.d.ts.map +1 -1
  5. package/dist/Avatar/Avatar.js +12 -3
  6. package/dist/BottomSheet/BottomSheet.js +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  9. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  10. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  11. package/dist/BottomSheet/useSheetGestures.js +30 -3
  12. package/dist/Button/Button.js +4 -4
  13. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  14. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  15. package/dist/DateInput/DateInput.d.ts.map +1 -1
  16. package/dist/DateInput/DateInput.js +4 -1
  17. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  18. package/dist/DateInput/MonthScroller.js +101 -15
  19. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  20. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  21. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  22. package/dist/DateTimeInput/DateTimeInput.js +4 -1
  23. package/dist/Dialog/Dialog.d.ts.map +1 -1
  24. package/dist/Dialog/Dialog.js +34 -9
  25. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  26. package/dist/Dialog/DialogHeader.js +5 -4
  27. package/dist/Divider/Divider.d.ts.map +1 -1
  28. package/dist/Divider/Divider.js +2 -1
  29. package/dist/Drawer/Drawer.d.ts.map +1 -1
  30. package/dist/Drawer/Drawer.js +5 -0
  31. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  32. package/dist/DropdownMenu/DropdownMenu.js +4 -1
  33. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  34. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  35. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +10 -1
  36. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  37. package/dist/DropdownMenu/lazyMenuBottomSheet.js +16 -2
  38. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  39. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  40. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  41. package/dist/EmptyState/EmptyState.js +2 -1
  42. package/dist/Field/FieldLabel.d.ts +1 -1
  43. package/dist/Field/FieldLabel.d.ts.map +1 -1
  44. package/dist/Field/FieldLabel.js +4 -4
  45. package/dist/Field/InputClearButton.js +3 -3
  46. package/dist/Field/PanelSearchInput.js +2 -2
  47. package/dist/FieldStatus/FieldStatus.js +2 -2
  48. package/dist/FileInput/FileInput.d.ts.map +1 -1
  49. package/dist/FileInput/FileInput.js +7 -1
  50. package/dist/Heading/Heading.js +1 -1
  51. package/dist/Icon/Icon.js +1 -1
  52. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  53. package/dist/Indicator/CheckIndicator.js +2 -1
  54. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  55. package/dist/Indicator/CheckboxIndicator.js +3 -1
  56. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  57. package/dist/Indicator/RadioIndicator.js +3 -1
  58. package/dist/Item/Item.d.ts.map +1 -1
  59. package/dist/Item/Item.js +2 -1
  60. package/dist/Item/ItemSwipeLayer.js +1 -1
  61. package/dist/Layout/Layout.js +1 -1
  62. package/dist/Layout/LayoutContent.js +1 -1
  63. package/dist/Layout/LayoutFooter.js +1 -1
  64. package/dist/Layout/LayoutHeader.js +1 -1
  65. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  66. package/dist/Lightbox/Lightbox.js +5 -0
  67. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  68. package/dist/List/List.js +1 -1
  69. package/dist/List/ListItem.js +3 -3
  70. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  71. package/dist/MobileNav/MobileNav.js +7 -2
  72. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  73. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  74. package/dist/MultiSelector/MultiSelector.js +5 -2
  75. package/dist/NumberInput/NumberInput.d.ts +3 -1
  76. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  77. package/dist/NumberInput/NumberInput.js +3 -3
  78. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  79. package/dist/ProgressBar/ProgressBar.js +16 -12
  80. package/dist/Resizable/useResizable.d.ts.map +1 -1
  81. package/dist/Resizable/useResizable.js +28 -2
  82. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  83. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  84. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  85. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  86. package/dist/Selector/useSelectorPresentation.js +20 -4
  87. package/dist/Slider/Slider.d.ts.map +1 -1
  88. package/dist/Slider/Slider.js +7 -1
  89. package/dist/Spinner/Spinner.js +2 -2
  90. package/dist/Stack/Stack.js +1 -1
  91. package/dist/Stack/StackItem.js +1 -1
  92. package/dist/TabList/TabList.d.ts.map +1 -1
  93. package/dist/TabList/TabList.js +16 -1
  94. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  95. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  96. package/dist/Text/Text.js +1 -1
  97. package/dist/Text/useTruncation.d.ts.map +1 -1
  98. package/dist/Text/useTruncation.js +23 -5
  99. package/dist/TextArea/TextArea.d.ts +1 -1
  100. package/dist/TextArea/TextArea.d.ts.map +1 -1
  101. package/dist/TextArea/TextArea.js +13 -2
  102. package/dist/TextInput/TextInput.d.ts.map +1 -1
  103. package/dist/TextInput/TextInput.js +3 -3
  104. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  105. package/dist/Timestamp/Timestamp.js +2 -2
  106. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  107. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  108. package/dist/Toast/ToastViewport.js +82 -51
  109. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  110. package/dist/Tokenizer/Tokenizer.js +13 -2
  111. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  112. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  113. package/dist/Toolbar/Toolbar.js +2 -1
  114. package/dist/Typeahead/Typeahead.d.ts +1 -1
  115. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  116. package/dist/Typeahead/Typeahead.js +9 -1
  117. package/dist/astryx.css +14 -1
  118. package/dist/gestures/PinchGestureRecognizer.d.ts +30 -0
  119. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  120. package/dist/gestures/PinchGestureRecognizer.js +125 -0
  121. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +70 -0
  122. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  123. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +220 -0
  124. package/dist/gestures/SwipeGestureRecognizer.d.ts +36 -0
  125. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  126. package/dist/gestures/SwipeGestureRecognizer.js +145 -0
  127. package/dist/gestures/VelocityTracker.d.ts +6 -2
  128. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  129. package/dist/gestures/VelocityTracker.js +7 -3
  130. package/dist/gestures/types.d.ts +11 -7
  131. package/dist/gestures/types.d.ts.map +1 -1
  132. package/dist/gestures/types.js +22 -2
  133. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  134. package/dist/hooks/useAdaptivePresentation.js +4 -1
  135. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  136. package/dist/hooks/useMenuHover.js +12 -1
  137. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  138. package/dist/i18n/generated-locales/en.generated.js +3 -0
  139. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  140. package/dist/i18n/generated-locales/pseudo.generated.js +3 -0
  141. package/dist/motion/animator.d.ts +7 -1
  142. package/dist/motion/animator.d.ts.map +1 -1
  143. package/dist/motion/animator.js +119 -27
  144. package/dist/theme/defineTheme.js +1 -1
  145. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  146. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  147. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  148. package/dist/theme/domainTokens/index.d.ts +5 -4
  149. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  150. package/dist/theme/domainTokens/index.js +5 -9
  151. package/dist/utils/focusReturn.d.ts +17 -1
  152. package/dist/utils/focusReturn.d.ts.map +1 -1
  153. package/dist/utils/focusReturn.js +11 -4
  154. package/dist/utils/getNodeText.d.ts +8 -0
  155. package/dist/utils/getNodeText.d.ts.map +1 -0
  156. package/dist/utils/getNodeText.js +37 -0
  157. package/dist/utils/interactionModality.d.ts +11 -2
  158. package/dist/utils/interactionModality.d.ts.map +1 -1
  159. package/dist/utils/interactionModality.js +58 -5
  160. package/dist/utils/presentation.d.ts +50 -0
  161. package/dist/utils/presentation.d.ts.map +1 -0
  162. package/dist/utils/presentation.js +125 -0
  163. package/dist/utils/touchPresentation.d.ts.map +1 -1
  164. package/dist/utils/touchPresentation.js +5 -3
  165. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  166. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  167. package/dist/utils/touchPresentation.stylex.js +33 -0
  168. package/locales/en.json +13 -1
  169. package/locales/pseudo.json +9 -0
  170. package/package.json +5 -4
  171. package/src/AlertDialog/AlertDialog.tsx +6 -5
  172. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  173. package/src/Avatar/Avatar.spec.md +22 -22
  174. package/src/Avatar/Avatar.tsx +18 -1
  175. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  176. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  177. package/src/BottomSheet/BottomSheet.tsx +1 -1
  178. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  179. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  180. package/src/BottomSheet/useSheetGestures.ts +37 -6
  181. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  182. package/src/Button/Button.tsx +4 -4
  183. package/src/Calendar/Calendar.spec.md +87 -48
  184. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  185. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  186. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  187. package/src/Collapsible/Collapsible.spec.md +2 -0
  188. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  189. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  190. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  191. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  192. package/src/DateInput/DateInput.test.tsx +53 -2
  193. package/src/DateInput/DateInput.tsx +15 -11
  194. package/src/DateInput/MonthScroller.tsx +106 -20
  195. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  196. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  197. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  198. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  199. package/src/DateTimeInput/DateTimeInput.spec.md +71 -48
  200. package/src/DateTimeInput/DateTimeInput.test.tsx +61 -0
  201. package/src/DateTimeInput/DateTimeInput.tsx +15 -11
  202. package/src/Dialog/Dialog.test.tsx +216 -2
  203. package/src/Dialog/Dialog.tsx +42 -9
  204. package/src/Dialog/DialogHeader.tsx +5 -4
  205. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  206. package/src/Divider/Divider.tsx +2 -1
  207. package/src/Drawer/Drawer.spec.md +111 -80
  208. package/src/Drawer/Drawer.test.tsx +22 -0
  209. package/src/Drawer/Drawer.tsx +5 -0
  210. package/src/Drawer/DrawerHeader.spec.md +181 -0
  211. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  212. package/src/DropdownMenu/DropdownMenu.tsx +7 -1
  213. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  214. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  215. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  216. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  217. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +53 -0
  218. package/src/DropdownMenu/lazyMenuBottomSheet.ts +23 -6
  219. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  220. package/src/EmptyState/EmptyState.tsx +2 -1
  221. package/src/Field/FieldLabel.tsx +5 -4
  222. package/src/Field/InputClearButton.tsx +3 -3
  223. package/src/Field/PanelSearchInput.tsx +2 -2
  224. package/src/FieldStatus/FieldStatus.tsx +2 -2
  225. package/src/FileInput/FileInput.test.tsx +21 -0
  226. package/src/FileInput/FileInput.tsx +4 -1
  227. package/src/FormLayout/FormLayout.spec.md +7 -8
  228. package/src/Heading/Heading.tsx +1 -1
  229. package/src/Icon/Icon.tsx +1 -1
  230. package/src/Indicator/CheckIndicator.spec.md +47 -42
  231. package/src/Indicator/CheckIndicator.tsx +2 -1
  232. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  233. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  234. package/src/Indicator/Indicator.spec.md +188 -0
  235. package/src/Indicator/RadioIndicator.tsx +3 -1
  236. package/src/Item/Item.tsx +2 -1
  237. package/src/Item/ItemSwipeLayer.tsx +1 -1
  238. package/src/Layout/Layout.spec.md +127 -131
  239. package/src/Layout/Layout.tsx +1 -1
  240. package/src/Layout/LayoutContent.spec.md +205 -0
  241. package/src/Layout/LayoutContent.tsx +1 -1
  242. package/src/Layout/LayoutFooter.spec.md +190 -0
  243. package/src/Layout/LayoutFooter.tsx +1 -1
  244. package/src/Layout/LayoutHeader.spec.md +193 -0
  245. package/src/Layout/LayoutHeader.tsx +1 -1
  246. package/src/Layout/LayoutPanel.spec.md +194 -0
  247. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  248. package/src/Lightbox/Lightbox.test.tsx +18 -0
  249. package/src/Lightbox/Lightbox.tsx +5 -0
  250. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  251. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  252. package/src/List/List.tsx +1 -1
  253. package/src/List/ListItem.tsx +3 -3
  254. package/src/MobileNav/MobileNav.test.tsx +15 -0
  255. package/src/MobileNav/MobileNav.tsx +9 -1
  256. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  257. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  258. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  259. package/src/MultiSelector/MultiSelector.test.tsx +118 -1
  260. package/src/MultiSelector/MultiSelector.tsx +6 -2
  261. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  262. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  263. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  264. package/src/NumberInput/NumberInput.spec.md +59 -37
  265. package/src/NumberInput/NumberInput.tsx +13 -3
  266. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  267. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  268. package/src/ProgressBar/ProgressBar.tsx +58 -10
  269. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  270. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  271. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  272. package/src/Resizable/useResizable.test.ts +43 -0
  273. package/src/Resizable/useResizable.ts +32 -9
  274. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  275. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  276. package/src/Selector/Selector.test.tsx +90 -1
  277. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  278. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  279. package/src/Selector/useSelectorPresentation.ts +31 -3
  280. package/src/SideNav/SideNav.test.tsx +27 -0
  281. package/src/Slider/Slider.test.tsx +12 -0
  282. package/src/Slider/Slider.tsx +4 -1
  283. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  284. package/src/Spinner/Spinner.tsx +2 -2
  285. package/src/Stack/Stack.tsx +1 -1
  286. package/src/Stack/StackItem.tsx +1 -1
  287. package/src/TabList/TabList.test.tsx +52 -1
  288. package/src/TabList/TabList.tsx +16 -1
  289. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  290. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  291. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  292. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  293. package/src/Text/Text.tsx +1 -1
  294. package/src/Text/useTruncation.test.tsx +128 -1
  295. package/src/Text/useTruncation.ts +23 -5
  296. package/src/TextArea/TextArea.spec.md +10 -13
  297. package/src/TextArea/TextArea.test.tsx +60 -0
  298. package/src/TextArea/TextArea.tsx +15 -1
  299. package/src/TextInput/TextInput.test.tsx +21 -0
  300. package/src/TextInput/TextInput.tsx +3 -1
  301. package/src/Timestamp/Timestamp.spec.md +6 -12
  302. package/src/Timestamp/Timestamp.test.tsx +30 -5
  303. package/src/Timestamp/Timestamp.tsx +6 -2
  304. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  305. package/src/Toast/Toast.spec.md +7 -10
  306. package/src/Toast/ToastViewport.test.tsx +575 -1
  307. package/src/Toast/ToastViewport.tsx +100 -50
  308. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  309. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  310. package/src/Tokenizer/Tokenizer.tsx +14 -2
  311. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  312. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  313. package/src/Toolbar/Toolbar.spec.md +5 -6
  314. package/src/Toolbar/Toolbar.test.tsx +30 -0
  315. package/src/Toolbar/Toolbar.tsx +2 -1
  316. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  317. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  318. package/src/TopNav/TopNav.test.tsx +27 -0
  319. package/src/Typeahead/Typeahead.test.tsx +40 -0
  320. package/src/Typeahead/Typeahead.tsx +12 -1
  321. package/src/gestures/PinchGestureRecognizer.ts +144 -0
  322. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +252 -0
  323. package/src/gestures/SwipeGestureRecognizer.ts +193 -0
  324. package/src/gestures/VelocityTracker.ts +10 -3
  325. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  326. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  327. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  328. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  329. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +188 -0
  330. package/src/gestures/__tests__/harness.ts +21 -2
  331. package/src/gestures/types.ts +23 -9
  332. package/src/hooks/useAdaptivePresentation.ts +4 -1
  333. package/src/hooks/useMenuHover.ts +15 -1
  334. package/src/i18n/generated-locales/en.generated.ts +4 -0
  335. package/src/i18n/generated-locales/pseudo.generated.ts +4 -0
  336. package/src/motion/README.md +11 -1
  337. package/src/motion/animator.test.ts +151 -3
  338. package/src/motion/animator.ts +149 -33
  339. package/src/theme/defineTheme.ts +1 -1
  340. package/src/theme/derivedVarRegistry.test.ts +4 -0
  341. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  342. package/src/theme/domainTokens/index.ts +6 -10
  343. package/src/utils/focusReturn.ts +17 -6
  344. package/src/utils/getNodeText.test.ts +24 -0
  345. package/src/utils/getNodeText.ts +37 -0
  346. package/src/utils/interactionModality.test.ts +45 -1
  347. package/src/utils/interactionModality.ts +64 -5
  348. package/src/utils/presentation.test.ts +380 -0
  349. package/src/utils/presentation.ts +180 -0
  350. package/src/utils/pressPaintPriority.test.ts +80 -0
  351. package/src/utils/touchPresentation.stylex.ts +43 -0
  352. package/src/utils/touchPresentation.test.ts +140 -1
  353. package/src/utils/touchPresentation.ts +5 -3
@@ -0,0 +1,181 @@
1
+ ---
2
+ schema_version: 3
3
+ template_version: 7
4
+ kind: component
5
+ id: component:DrawerHeader
6
+ authority: current
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: cixzhang
10
+ approved_at: 2026-10-30
11
+ owners: [cixzhang, imdreamrunner]
12
+ review_triggers: [public-api, behavior, layout, theming, accessibility]
13
+ verified_by:
14
+ [
15
+ packages/core/src/Drawer/DrawerHeader.test.tsx,
16
+ apps/storybook/stories/DrawerHeader.stories.tsx,
17
+ scripts/check-knowledge.mjs,
18
+ ]
19
+ modules: []
20
+ families: [family:layout-regions]
21
+ design_specs: []
22
+ architecture:
23
+ [architecture:component-theming-surface, architecture:container-padding]
24
+ contributing: []
25
+ system_specs: []
26
+ ---
27
+
28
+ # DrawerHeader component contract
29
+
30
+ ## Contract at a glance
31
+
32
+ | Area | Contract |
33
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | Public contract | `DrawerHeader` with `title`, `subtitle`, `onOpenChange`, `startContent`, `endContent`, `endContentEdgeCompensation` (`inline`, `block`, `all`), and `hasDivider`; five `drawer-header` targets. |
35
+ | Behavior | A Layout header row for a Drawer: optional start content, an h2 title with optional subtitle, optional end content, and a close button only when `onOpenChange` is given. |
36
+ | End-user impact | Users see the drawer's title and, when the caller provides one, a named close button; focus is not moved when the header mounts. |
37
+ | Builder impact | None. |
38
+ | Compatibility/readiness | Records shipped behavior; no public change. |
39
+ | Review checks | Reject a close button rendered without `onOpenChange`, a header that names the Drawer or moves focus on mount, or a title that is not an h2. |
40
+ | Governing rules | `component:Drawer` (the panel and naming); `family:layout-regions`; `architecture:component-theming-surface`. |
41
+
42
+ This table is a review projection; the body below is authoritative.
43
+
44
+ ## Intent
45
+
46
+ DrawerHeader is the optional title row a caller composes in a Drawer's Layout
47
+ header slot. It owns the row's parts, its close button, and its targets.
48
+ Consumer usage lives in `DrawerHeader.doc.mjs`.
49
+
50
+ ## Compatibility and migration
51
+
52
+ - Released default preserved: `yes`
53
+ - Compatibility class: records shipped behavior; no public change
54
+ - Controlled/uncontrolled behavior: the close button calls the caller's
55
+ `onOpenChange(false)` and owns no state
56
+ - Migration decision: none
57
+
58
+ Consumer migration instructions belong in consumer docs and release notes.
59
+
60
+ ## Ownership boundary
61
+
62
+ **Owns**
63
+
64
+ - The header row, its slots, title semantics, close button, end-content edge
65
+ compensation, and five targets (FR1–FR5).
66
+
67
+ **Does not own / non-goals**
68
+
69
+ - The Drawer's name, focus entry, and open state — owned by `component:Drawer`.
70
+ - The header region's padding, divider default, and place in the Layout — owned
71
+ by `family:layout-regions` through `LayoutHeader`.
72
+ - The close Button — owned by `component:Button`.
73
+
74
+ ## Public concepts
75
+
76
+ | Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
77
+ | ------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------- | ------------------------ | --------- | -------------------------------- |
78
+ | End-content compensation | `inline`, `block`, `all`, omitted | Which edges of the end slot pull outward to align a trailing control with the container edge. | End slot. | Both axes when `onOpenChange` is set, none otherwise | `component:DrawerHeader` | Stable | TypeScript rejects other values. |
79
+
80
+ ## Behavioral and layout contract
81
+
82
+ | ID | Invariant | Basis |
83
+ | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
84
+ | FR1 | DrawerHeader renders inside a LayoutHeader that receives `ref`, `hasDivider`, `xstyle`, `className`, `style`, and other DOM props. Its row carries `drawer-header` and shows, in order, start content, the title block, and the end slot. | Current source and tests |
85
+ | FR2 | The title renders as an h2; a renderable `subtitle` (including `0`) renders below it as secondary small body text, and an empty one renders nothing. Start and end slots render when their content is renderable, including `0`. | Current source and tests |
86
+ | FR3 | With `onOpenChange`, the end slot ends with an icon-only ghost Button named and tooltipped by `@astryx.dialog.close` that calls `onOpenChange(false)`; without it there is no close button. | Current source and tests |
87
+ | FR4 | The end slot pulls outward by `--spacing-2` on the block axis, the inline-end edge, or both, per `endContentEdgeCompensation`; when omitted, both apply only if there is a close button. | Current source |
88
+ | FR5 | The start slot, title block, end slot, and close glyph carry `drawer-header-start-content`, `drawer-header-title-block`, `drawer-header-end-content`, and `drawer-header-close-icon`. | Current source and tests |
89
+ | FR6 | Mounting the header does not move focus, and the header does not name the Drawer. | Current source and tests |
90
+
91
+ ### Allowed variation
92
+
93
+ - **AV1 — Slots.** Start, end, and subtitle content vary or are absent.
94
+
95
+ ### Representative states
96
+
97
+ | State | Required invariant | Allowed variation |
98
+ | ------------------- | ------------------------------------------------- | ----------------- |
99
+ | Title only | h2 title; no close button. | — |
100
+ | With `onOpenChange` | Named close button at the end; edges compensated. | End content. |
101
+ | Start and end slots | Order: start, title block, end. | Content. |
102
+
103
+ ### Transformation and precedence order
104
+
105
+ - **ORD1 — Compensation.** `endContentEdgeCompensation`, then the close-button
106
+ default (FR4).
107
+
108
+ ### Performance and resources
109
+
110
+ - No resource rule.
111
+
112
+ ## Accessibility contract
113
+
114
+ - **AR1 — Heading.** The title is an h2.
115
+ - **AR2 — Close name.** FR3's translated name and tooltip.
116
+ - **AR3 — Focus.** FR6.
117
+
118
+ ## Design relationships
119
+
120
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
121
+ | ---------------- | --------------------------------------------- | ------------------------------ | -------------- | ------------------ |
122
+ | Header row | Start, title block, end in one row. | Current source and public docs | Supporting | FR1 |
123
+ | Start content | Leading caller content. | Current source and public docs | Supporting | FR2, FR5 |
124
+ | Title block | Title and optional subtitle. | Current source and public docs | Prominent | FR2, FR5 |
125
+ | End content | Trailing caller content and the close button. | Current source and public docs | Supporting | FR3–FR5 |
126
+ | Close icon | Close glyph inside the button. | Current source and public docs | Supporting | FR3, FR5 |
127
+
128
+ ### Theming anatomy
129
+
130
+ <!-- anatomy-theming:v1 -->
131
+
132
+ ```json
133
+ {
134
+ "Header row": {"target": "drawer-header"},
135
+ "Start content": {"target": "drawer-header-start-content"},
136
+ "Title block": {"target": "drawer-header-title-block"},
137
+ "End content": {"target": "drawer-header-end-content"},
138
+ "Close icon": {"target": "drawer-header-close-icon"}
139
+ }
140
+ ```
141
+
142
+ ## Family and system relationships
143
+
144
+ - `component:Drawer` hosts the header in its Layout and owns naming and focus.
145
+ - `family:layout-regions` owns the header region it renders into.
146
+
147
+ ## Verification map
148
+
149
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
150
+ | ------------------- | -------------------------------------------- | ---------------------------------------- | -------------------------------------------------- | ---------------------------------- |
151
+ | FR1, FR2, AR1 | `DrawerHeader.test.tsx` title and slot tests | Title, subtitle, `0` values, empty slots | Wrong heading level or dropped content fails. | `audit:DrawerHeader/anatomy` |
152
+ | FR3, AR2 | `DrawerHeader.test.tsx` close-button tests | With and without `onOpenChange` | A button without a callback, or no name, fails. | `audit:DrawerHeader/accessibility` |
153
+ | FR5 | `DrawerHeader.test.tsx` theming target test | All slots | A missing target fails. | `audit:DrawerHeader/theming` |
154
+ | FR6, AR3 | `DrawerHeader.test.tsx` focus test | Mount | Focus moved on mount fails. | `audit:DrawerHeader/accessibility` |
155
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Consumer anatomy and current targets | Missing, extra, or stale mappings fail validation. | `audit:DrawerHeader/theming` |
156
+
157
+ FR4 is read from source; no focused test checks it yet.
158
+
159
+ Read from source, with no focused test yet:
160
+
161
+ - FR1: `ref`, `hasDivider`, `xstyle`, `className`, `style`, and other DOM
162
+ props reaching the LayoutHeader.
163
+ - FR3: the close button's tooltip.
164
+ - FR4: the end slot's edge compensation.
165
+ - FR6: the header not naming the Drawer.
166
+
167
+ ## Decision log
168
+
169
+ No component-local decision is recorded.
170
+
171
+ ## Open questions
172
+
173
+ - How do `endContentEdgeCompensation`'s `inline`, `block`, and `all` axis
174
+ values map onto `endContentEdge` treatment values
175
+ (`architecture:container-padding`), and when does the old prop enter its
176
+ compatibility window? Today the prop stays supported as released.
177
+
178
+ ## Content boundary
179
+
180
+ This file does not duplicate consumer prop tables, examples, or Drawer
181
+ behavior. It links to their owners.
@@ -127,6 +127,47 @@ describe('DropdownMenu', () => {
127
127
  ).not.toBeInTheDocument();
128
128
  });
129
129
 
130
+ it("forwards the button's ref object to the popover trigger", () => {
131
+ const ref = createRef<HTMLButtonElement>();
132
+ render(
133
+ <DropdownMenu
134
+ button={{label: 'Project actions', ref}}
135
+ items={[{label: 'Edit project', onClick: vi.fn()}]}
136
+ />,
137
+ );
138
+ expect(ref.current).toBe(
139
+ screen.getByRole('button', {name: /Project actions/}),
140
+ );
141
+ });
142
+
143
+ it("forwards the button's ref object to the bottom-sheet trigger", () => {
144
+ const ref = createRef<HTMLButtonElement>();
145
+ render(
146
+ <DropdownMenu
147
+ button={{label: 'Project actions', ref}}
148
+ presentation="bottom-sheet"
149
+ items={[{label: 'Edit project', onClick: vi.fn()}]}
150
+ />,
151
+ );
152
+ expect(ref.current).toBe(
153
+ screen.getByRole('button', {name: /Project actions/}),
154
+ );
155
+ });
156
+
157
+ it("forwards the button's callback ref to the bottom-sheet trigger", () => {
158
+ const ref = vi.fn();
159
+ render(
160
+ <DropdownMenu
161
+ button={{label: 'Project actions', ref}}
162
+ presentation="bottom-sheet"
163
+ items={[{label: 'Edit project', onClick: vi.fn()}]}
164
+ />,
165
+ );
166
+ expect(ref).toHaveBeenCalledWith(
167
+ screen.getByRole('button', {name: /Project actions/}),
168
+ );
169
+ });
170
+
130
171
  it('renders data-driven actions in a bottom sheet when requested', async () => {
131
172
  const user = userEvent.setup();
132
173
  const onEdit = vi.fn();
@@ -67,6 +67,7 @@ import {
67
67
  type DropdownMenuSurfaceValue,
68
68
  } from './DropdownMenuContext';
69
69
  import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
70
+ import {useMergedRefs} from '../hooks/useMergedRefs';
70
71
  import {useListFocus} from '../hooks/useListFocus';
71
72
  import {useMenuPress} from '../hooks/useMenuPress';
72
73
  import type {MenuPressPointerType} from '../hooks/menuPressGesture';
@@ -621,6 +622,11 @@ function DropdownMenuBottomSheet({
621
622
  const button = buttonFromProps ?? {label: t(DEFAULT_BUTTON_I18N_KEY)};
622
623
  const backLabel = t('@astryx.dropdownMenu.back');
623
624
  const buttonRef = useRef<HTMLElement>(null);
625
+ // The caller's `button.ref` rides along with the sheet's own trigger ref.
626
+ const triggerRef = useMergedRefs<HTMLButtonElement>(
627
+ buttonRef as React.Ref<HTMLButtonElement>,
628
+ button.ref,
629
+ );
624
630
  const triggerId = useId();
625
631
  const sheetId = useId();
626
632
  const actionListRef = useRef<HTMLDivElement>(null);
@@ -806,7 +812,7 @@ function DropdownMenuBottomSheet({
806
812
  ) : (
807
813
  <Button
808
814
  {...button}
809
- ref={buttonRef as React.Ref<HTMLButtonElement>}
815
+ ref={triggerRef}
810
816
  xstyle={[
811
817
  isOpen && styles.triggerOpen,
812
818
  button.xstyle,
@@ -647,6 +647,40 @@ describe('DropdownMenu data-driven submenus', () => {
647
647
  expect(onMove).toHaveBeenCalledWith('b');
648
648
  });
649
649
 
650
+ it('renders a submenu for an item with nested items inside a section', async () => {
651
+ const user = userEvent.setup();
652
+ const onWork = vi.fn();
653
+ render(
654
+ <DropdownMenu
655
+ button={{label: 'Share'}}
656
+ items={[
657
+ {
658
+ type: 'section',
659
+ title: 'Send',
660
+ items: [
661
+ {label: 'Copy link', onClick: () => {}},
662
+ {
663
+ label: 'Email',
664
+ items: [
665
+ {label: 'Work', onClick: onWork},
666
+ {label: 'Personal', onClick: () => {}},
667
+ ],
668
+ },
669
+ ],
670
+ },
671
+ ]}
672
+ />,
673
+ );
674
+ await user.click(screen.getByRole('button', {name: /Share/}));
675
+ const trigger = screen.getByRole('menuitem', {name: /Email/, hidden: true});
676
+ expect(trigger).toHaveAttribute('aria-haspopup', 'menu');
677
+ await user.click(trigger);
678
+ await user.click(
679
+ await screen.findByRole('menuitem', {name: 'Work', hidden: true}),
680
+ );
681
+ expect(onWork).toHaveBeenCalled();
682
+ });
683
+
650
684
  it('keyboard-reaches an item positioned after a submenu row', async () => {
651
685
  // Regression: the submenu flyout renders inline inside the root menu, so
652
686
  // the root's item query swept in the (hidden) flyout items. Arrow nav then
@@ -11,13 +11,14 @@
11
11
 
12
12
  import React, {useRef, type ReactElement} from 'react';
13
13
  import * as stylex from '@stylexjs/stylex';
14
- import {Divider} from '../Divider';
15
- import {Heading} from '../Heading';
16
- import {Icon, renderIconSlot} from '../Icon';
17
- import {List, ListItem} from '../List';
14
+ import {Divider} from '../Divider/Divider';
15
+ import {Heading} from '../Heading/Heading';
16
+ import {Icon, renderIconSlot} from '../Icon/Icon';
17
+ import {List} from '../List/List';
18
+ import {ListItem} from '../List/ListItem';
18
19
  import {useMenuPress} from '../hooks/useMenuPress';
19
20
  import {colorVars, spacingVars} from '../theme/tokens.stylex';
20
- import {rtlStyles} from '../utils';
21
+ import {rtlStyles} from '../utils/rtlStyles';
21
22
  import {getInteractionModality} from '../utils/interactionModality';
22
23
  import type {DropdownMenuItemData, DropdownMenuOption} from './DropdownMenu';
23
24
  import {isModifiedClick} from './menuItemRoles';
@@ -120,16 +120,28 @@ async function runState(
120
120
  });
121
121
  }
122
122
 
123
- test('every menu expectation is exercised by a DropdownMenu state', async ({
124
- page,
125
- }) => {
126
- const cdp = await page.context().newCDPSession(page);
123
+ // Checking coverage runs every state again, which on a loaded runner nears
124
+ // the per-test timeout as one test. It runs as one serial part per state,
125
+ // sharing their results, then one check over all of them.
126
+ test.describe('every menu expectation is exercised by a DropdownMenu state', () => {
127
+ test.describe.configure({mode: 'serial'});
127
128
  const results: BindingResult[] = [];
129
+
128
130
  for (const state of MENU_BINDING_STATES) {
129
- results.push(await runState(page, cdp, state));
131
+ test(`runs ${state.id}`, async ({page}) => {
132
+ const cdp = await page.context().newCDPSession(page);
133
+ results.push(await runState(page, cdp, state));
134
+ });
130
135
  }
131
- expect(neverExercised(results)).toEqual([]);
132
- expect(unmatchedKnownFailures(MENU_KNOWN_FAILURES, results)).toEqual([]);
136
+
137
+ test('no expectation is left unexercised', () => {
138
+ // Every part ran in this worker; this test alone proves nothing.
139
+ expect(results.map(result => result.state).sort()).toEqual(
140
+ MENU_BINDING_STATES.map(state => state.id).sort(),
141
+ );
142
+ expect(neverExercised(results)).toEqual([]);
143
+ expect(unmatchedKnownFailures(MENU_KNOWN_FAILURES, results)).toEqual([]);
144
+ });
133
145
  });
134
146
 
135
147
  for (const state of MENU_BINDING_STATES) {
@@ -107,18 +107,30 @@ async function runState(
107
107
  });
108
108
  }
109
109
 
110
- test('every menu-button expectation is exercised by a DropdownMenu or MoreMenu state', async ({
111
- page,
112
- }) => {
113
- const cdp = await page.context().newCDPSession(page);
110
+ // Checking coverage runs every state again, which on a loaded runner nears
111
+ // the per-test timeout as one test. It runs as one serial part per state,
112
+ // sharing their results, then one check over all of them.
113
+ test.describe('every menu-button expectation is exercised by a DropdownMenu or MoreMenu state', () => {
114
+ test.describe.configure({mode: 'serial'});
114
115
  const results: BindingResult[] = [];
116
+
115
117
  for (const state of MENU_BUTTON_BINDING_STATES) {
116
- results.push(await runState(page, cdp, state));
118
+ test(`runs ${state.id}`, async ({page}) => {
119
+ const cdp = await page.context().newCDPSession(page);
120
+ results.push(await runState(page, cdp, state));
121
+ });
117
122
  }
118
- expect(neverExercised(results)).toEqual([]);
119
- expect(unmatchedKnownFailures(MENU_BUTTON_KNOWN_FAILURES, results)).toEqual(
120
- [],
121
- );
123
+
124
+ test('no expectation is left unexercised', () => {
125
+ // Every part ran in this worker; this test alone proves nothing.
126
+ expect(results.map(result => result.state).sort()).toEqual(
127
+ MENU_BUTTON_BINDING_STATES.map(state => state.id).sort(),
128
+ );
129
+ expect(neverExercised(results)).toEqual([]);
130
+ expect(unmatchedKnownFailures(MENU_BUTTON_KNOWN_FAILURES, results)).toEqual(
131
+ [],
132
+ );
133
+ });
122
134
  });
123
135
 
124
136
  for (const state of MENU_BUTTON_BINDING_STATES) {
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file lazyMenuBottomSheet.test.ts
5
+ * @input lazyMenuBottomSheet's action-list preload, with the list's module
6
+ * failing to load once
7
+ * @output A failed preload is no unhandled rejection, and is not kept: the
8
+ * next load asks the module loader again
9
+ * @position Unit test of ./lazyMenuBottomSheet; the hydration case is in
10
+ * DropdownMenu.ssr.test.tsx
11
+ */
12
+
13
+ import {afterEach, describe, expect, it, vi} from 'vitest';
14
+
15
+ afterEach(() => {
16
+ vi.doUnmock('./MenuBottomSheetActionList');
17
+ vi.resetModules();
18
+ });
19
+
20
+ describe('preloadMenuBottomSheetActionList', () => {
21
+ it('drops a failed preload without an unhandled rejection, and asks the module loader again on the next call', async () => {
22
+ vi.resetModules();
23
+ let attempts = 0;
24
+ vi.doMock('./MenuBottomSheetActionList', () => {
25
+ attempts += 1;
26
+ if (attempts === 1) {
27
+ throw new Error('the connection dropped');
28
+ }
29
+ return {MenuBottomSheetActionList: () => null};
30
+ });
31
+ const unhandled: unknown[] = [];
32
+ const onUnhandled = (reason: unknown) => unhandled.push(reason);
33
+ process.on('unhandledRejection', onUnhandled);
34
+ try {
35
+ const {preloadMenuBottomSheetActionList} =
36
+ await import('./lazyMenuBottomSheet');
37
+ preloadMenuBottomSheetActionList();
38
+ await vi.waitFor(() => expect(attempts).toBe(1));
39
+ // An unhandled rejection is reported after the rejecting task's
40
+ // microtasks: give it two macrotasks.
41
+ await new Promise(resolve => setTimeout(resolve, 0));
42
+ await new Promise(resolve => setTimeout(resolve, 0));
43
+ expect(unhandled).toEqual([]);
44
+
45
+ preloadMenuBottomSheetActionList();
46
+ await vi.waitFor(() => expect(attempts).toBe(2));
47
+ const loaded = await import('./MenuBottomSheetActionList');
48
+ expect(typeof loaded.MenuBottomSheetActionList).toBe('function');
49
+ } finally {
50
+ process.off('unhandledRejection', onUnhandled);
51
+ }
52
+ });
53
+ });
@@ -15,6 +15,11 @@
15
15
  * of pages whose dropdowns only open as anchored popovers. Each wrapper keeps
16
16
  * the wrapped component's props and renders nothing until its module loads.
17
17
  *
18
+ * The action list preloads at rest; a failed load is not kept, so the next
19
+ * preload or open asks the module loader again (a loader that retries then
20
+ * loads it; a browser does not fetch a native module again once its import
21
+ * failed).
22
+ *
18
23
  * Neither wrapper renders on the server or in the render that hydrates server
19
24
  * HTML (spec:AST-013 FR9): a lazy component that has not loaded yet would
20
25
  * leave server markup incomplete and abort hydration. The closed sheet has no
@@ -47,16 +52,28 @@ const LazyMenuBottomSheet = lazy(async () =>
47
52
  let actionListModule: typeof ActionListModule | null = null;
48
53
  let actionListLoad: Promise<typeof ActionListModule> | null = null;
49
54
  async function loadActionList(): Promise<typeof ActionListModule> {
50
- actionListLoad ??= import('./MenuBottomSheetActionList').then(module => {
51
- actionListModule = module;
52
- return module;
53
- });
55
+ actionListLoad ??= import('./MenuBottomSheetActionList').then(
56
+ module => {
57
+ actionListModule = module;
58
+ return module;
59
+ },
60
+ (error: unknown) => {
61
+ // A failed load (a dropped connection) is not kept: the next caller
62
+ // asks the module loader again.
63
+ actionListLoad = null;
64
+ throw error;
65
+ },
66
+ );
54
67
  return actionListLoad;
55
68
  }
56
69
 
57
- /** Starts loading the action list; a sheet menu calls it at rest. */
70
+ /**
71
+ * Starts loading the action list; a sheet menu calls it at rest. A preload
72
+ * that fails is dropped, never an unhandled rejection: the first open asks the
73
+ * module loader again.
74
+ */
58
75
  export function preloadMenuBottomSheetActionList(): void {
59
- void loadActionList();
76
+ loadActionList().catch(() => {});
60
77
  }
61
78
 
62
79
  const LazyMenuBottomSheetActionList = lazy(async () =>
@@ -68,6 +68,28 @@ function renderLeafItem(
68
68
  return <DropdownMenuItem key={getItemKey(item, index)} {...itemProps} />;
69
69
  }
70
70
 
71
+ /**
72
+ * A data item that declares nested `items` becomes a submenu (data-mode
73
+ * parity with the compound DropdownMenuSubMenu API), at the top level and
74
+ * inside a section alike; otherwise it's a leaf. renderDropdownItems owns the
75
+ * recursion and hands the rendered children to DropdownMenuSubMenu, so
76
+ * DropdownMenuSubMenu never imports this module back (no import cycle).
77
+ */
78
+ function renderItem(item: DropdownMenuItemData, index: number): ReactElement {
79
+ if (item.items && item.items.length > 0) {
80
+ return (
81
+ <DropdownMenuSubMenu
82
+ key={getItemKey(item, index)}
83
+ icon={item.icon}
84
+ label={item.label}
85
+ isDisabled={item.isDisabled}>
86
+ {renderDropdownItems(item.items)}
87
+ </DropdownMenuSubMenu>
88
+ );
89
+ }
90
+ return renderLeafItem(item, index);
91
+ }
92
+
71
93
  /**
72
94
  * Converts data-driven items into DropdownMenuItem components,
73
95
  * so both modes share the same rendering and keyboard navigation path.
@@ -96,28 +118,11 @@ export function renderDropdownItems(items: DropdownMenuOption[]): ReactNode {
96
118
  {option.title}
97
119
  </div>
98
120
  )}
99
- {option.items.map(renderLeafItem)}
121
+ {option.items.map(renderItem)}
100
122
  </div>,
101
123
  );
102
124
  } else if (!('type' in option)) {
103
- // A plain item that declares nested `items` becomes a submenu (data-mode
104
- // parity with the compound DropdownMenuSubMenu API); otherwise it's a
105
- // leaf. renderDropdownItems owns the recursion and hands the rendered
106
- // children to DropdownMenuSubMenu — so DropdownMenuSubMenu never imports
107
- // this module back (no import cycle).
108
- if (option.items && option.items.length > 0) {
109
- elements.push(
110
- <DropdownMenuSubMenu
111
- key={getItemKey(option, i)}
112
- icon={option.icon}
113
- label={option.label}
114
- isDisabled={option.isDisabled}>
115
- {renderDropdownItems(option.items)}
116
- </DropdownMenuSubMenu>,
117
- );
118
- } else {
119
- elements.push(renderLeafItem(option, i));
120
- }
125
+ elements.push(renderItem(option, i));
121
126
  }
122
127
  }
123
128
 
@@ -24,7 +24,8 @@ import {
24
24
  } from '../theme/tokens.stylex';
25
25
  import type {BaseProps} from '../BaseProps';
26
26
  import {AnnounceRenderedText} from '../hooks/AnnounceRenderedText';
27
- import {isRenderable, mergeProps} from '../utils';
27
+ import {isRenderable} from '../utils/isRenderable';
28
+ import {mergeProps} from '../utils/mergeProps';
28
29
  import {themeProps} from '../utils/themeProps';
29
30
 
30
31
  const styles = stylex.create({
@@ -19,7 +19,7 @@
19
19
  import {use, useMemo, useRef, type ReactNode, type RefObject} from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
  import type {BaseProps} from '../BaseProps';
22
- import {mergeProps} from '../utils';
22
+ import {mergeProps} from '../utils/mergeProps';
23
23
 
24
24
  import {
25
25
  colorVars,
@@ -28,9 +28,10 @@ import {
28
28
  typographyVars,
29
29
  typeScaleVars,
30
30
  } from '../theme/tokens.stylex';
31
- import {Icon, renderIconSlot, type IconType} from '../Icon';
32
- import {Tooltip} from '../Tooltip';
33
- import {useTranslator} from '../i18n';
31
+ import {Icon, renderIconSlot} from '../Icon/Icon';
32
+ import type {IconType} from '../Icon';
33
+ import {Tooltip} from '../Tooltip/Tooltip';
34
+ import {useTranslator} from '../i18n/useTranslator';
34
35
  import {themeProps} from '../utils/themeProps';
35
36
  import {useInputContainer} from '../hooks/useInputContainer';
36
37
  import {FormLayoutContext} from '../FormLayout/FormLayoutContext';
@@ -18,9 +18,9 @@
18
18
 
19
19
  import type {ReactNode} from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
- import {Button} from '../Button';
22
- import {Icon} from '../Icon';
23
- import {useTranslator} from '../i18n';
21
+ import {Button} from '../Button/Button';
22
+ import {Icon} from '../Icon/Icon';
23
+ import {useTranslator} from '../i18n/useTranslator';
24
24
  import {themeProps} from '../utils/themeProps';
25
25
 
26
26
  const styles = stylex.create({
@@ -42,9 +42,9 @@
42
42
  import {useCallback, useState, type Ref} from 'react';
43
43
  import * as stylex from '@stylexjs/stylex';
44
44
  import type {StyleXStyles} from '@stylexjs/stylex';
45
- import {Icon} from '../Icon';
45
+ import {Icon} from '../Icon/Icon';
46
46
  import {InputClearButton} from './InputClearButton';
47
- import {mergeProps} from '../utils';
47
+ import {mergeProps} from '../utils/mergeProps';
48
48
  import {
49
49
  getInteractionModality,
50
50
  useInteractionModalityTracking,
@@ -20,7 +20,7 @@
20
20
  import React from 'react';
21
21
  import * as stylex from '@stylexjs/stylex';
22
22
  import type {BaseProps} from '../BaseProps';
23
- import {mergeProps} from '../utils';
23
+ import {mergeProps} from '../utils/mergeProps';
24
24
  import {
25
25
  colorVars,
26
26
  radiusVars,
@@ -31,7 +31,7 @@ import {
31
31
  import type {InputStatusType} from '../Field/types';
32
32
  import {useEntryAnimation} from '../hooks/useEntryAnimation';
33
33
  import {themeProps} from '../utils/themeProps';
34
- import {Icon} from '../Icon';
34
+ import {Icon} from '../Icon/Icon';
35
35
  import type {IconName} from '../Icon';
36
36
  import type {FieldStatusVariantMap} from './index';
37
37
  import {useFieldStatusAnnouncement} from './useFieldStatusAnnouncement';