@astryxdesign/core 0.5.1 → 0.5.2-canary.00477a5

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 (270) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/BottomSheet/BottomSheet.d.ts +3 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheet.js +4 -3
  5. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
  6. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
  8. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  9. package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
  10. package/dist/Button/Button.d.ts +2 -1
  11. package/dist/Button/Button.d.ts.map +1 -1
  12. package/dist/Button/Button.js +11 -4
  13. package/dist/Carousel/Carousel.d.ts.map +1 -1
  14. package/dist/Carousel/Carousel.js +2 -2
  15. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  16. package/dist/Chat/ChatToolCalls.js +34 -17
  17. package/dist/ContextMenu/ContextMenu.d.ts +14 -3
  18. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  19. package/dist/ContextMenu/ContextMenu.js +149 -26
  20. package/dist/ContextMenu/index.d.ts +1 -0
  21. package/dist/ContextMenu/index.d.ts.map +1 -1
  22. package/dist/DropdownMenu/DropdownMenu.d.ts +24 -5
  23. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  24. package/dist/DropdownMenu/DropdownMenu.js +340 -26
  25. package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -1
  26. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  27. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  28. package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
  29. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -2
  30. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  31. package/dist/DropdownMenu/DropdownMenuSubMenu.js +43 -14
  32. package/dist/DropdownMenu/MenuBottomSheet.d.ts +20 -0
  33. package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -0
  34. package/dist/DropdownMenu/MenuBottomSheet.js +36 -0
  35. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts +10 -0
  36. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -0
  37. package/dist/DropdownMenu/MenuBottomSheetActionList.js +111 -0
  38. package/dist/DropdownMenu/index.d.ts +2 -1
  39. package/dist/DropdownMenu/index.d.ts.map +1 -1
  40. package/dist/DropdownMenu/menuWidth.d.ts +14 -0
  41. package/dist/DropdownMenu/menuWidth.d.ts.map +1 -0
  42. package/dist/DropdownMenu/menuWidth.js +35 -0
  43. package/dist/DropdownMenu/useMenuOverflow.d.ts +10 -0
  44. package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -0
  45. package/dist/DropdownMenu/useMenuOverflow.js +60 -0
  46. package/dist/MoreMenu/MoreMenu.d.ts +8 -2
  47. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  48. package/dist/MoreMenu/MoreMenu.js +2 -0
  49. package/dist/MultiSelector/MultiSelector.d.ts +11 -1
  50. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  51. package/dist/MultiSelector/MultiSelector.js +97 -68
  52. package/dist/MultiSelector/index.d.ts +1 -1
  53. package/dist/MultiSelector/index.d.ts.map +1 -1
  54. package/dist/Popover/Popover.d.ts +10 -6
  55. package/dist/Popover/Popover.d.ts.map +1 -1
  56. package/dist/Popover/Popover.js +141 -16
  57. package/dist/Popover/usePopover.d.ts +4 -0
  58. package/dist/Popover/usePopover.d.ts.map +1 -1
  59. package/dist/Popover/usePopover.js +52 -5
  60. package/dist/Selector/Selector.d.ts +10 -0
  61. package/dist/Selector/Selector.d.ts.map +1 -1
  62. package/dist/Selector/Selector.js +106 -80
  63. package/dist/Selector/SelectorBottomSheet.d.ts +21 -0
  64. package/dist/Selector/SelectorBottomSheet.d.ts.map +1 -0
  65. package/dist/Selector/SelectorBottomSheet.js +83 -0
  66. package/dist/Selector/index.d.ts +1 -1
  67. package/dist/Selector/index.d.ts.map +1 -1
  68. package/dist/Selector/selectorPresentation.stylex.d.ts +16 -0
  69. package/dist/Selector/selectorPresentation.stylex.d.ts.map +1 -0
  70. package/dist/Selector/selectorPresentation.stylex.js +21 -0
  71. package/dist/Selector/useSelectorPresentation.d.ts +32 -0
  72. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -0
  73. package/dist/Selector/useSelectorPresentation.js +90 -0
  74. package/dist/Toast/useToastGesture.d.ts.map +1 -1
  75. package/dist/Toast/useToastGesture.js +46 -9
  76. package/dist/astryx.css +19 -1
  77. package/dist/hooks/useAdaptivePresentation.d.ts +5 -0
  78. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -0
  79. package/dist/hooks/useAdaptivePresentation.js +19 -0
  80. package/dist/hooks/useFocusReturnVisibility.d.ts +7 -0
  81. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -0
  82. package/dist/hooks/useFocusReturnVisibility.js +35 -0
  83. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  84. package/dist/hooks/useFocusTrap.js +12 -0
  85. package/dist/i18n/InternationalizationContext.d.ts +2 -0
  86. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  87. package/dist/i18n/InternationalizationContext.js +3 -1
  88. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  89. package/dist/i18n/InternationalizationProvider.js +3 -1
  90. package/dist/i18n/resolve.d.ts +1 -1
  91. package/dist/i18n/resolve.d.ts.map +1 -1
  92. package/dist/i18n/resolve.js +37 -38
  93. package/dist/i18n/useTranslator.d.ts.map +1 -1
  94. package/dist/i18n/useTranslator.js +2 -3
  95. package/locales/af-ZA.json +0 -4
  96. package/locales/ar-SA.json +0 -4
  97. package/locales/ca-ES.json +0 -4
  98. package/locales/cs-CZ.json +0 -4
  99. package/locales/da-DK.json +0 -4
  100. package/locales/de-DE.json +0 -4
  101. package/locales/el-GR.json +0 -4
  102. package/locales/en.json +20 -0
  103. package/locales/es-ES.json +0 -4
  104. package/locales/fi-FI.json +0 -4
  105. package/locales/fr-FR.json +0 -4
  106. package/locales/he-IL.json +0 -4
  107. package/locales/hu-HU.json +0 -4
  108. package/locales/it-IT.json +0 -4
  109. package/locales/ja-JP.json +0 -4
  110. package/locales/ko-KR.json +0 -4
  111. package/locales/nl-NL.json +0 -4
  112. package/locales/no-NO.json +0 -4
  113. package/locales/pl-PL.json +0 -4
  114. package/locales/pseudo.json +15 -0
  115. package/locales/pt-BR.json +0 -4
  116. package/locales/pt-PT.json +0 -4
  117. package/locales/ro-RO.json +0 -4
  118. package/locales/ru-RU.json +0 -4
  119. package/locales/sr-SP.json +0 -4
  120. package/locales/sv-SE.json +0 -4
  121. package/locales/tr-TR.json +0 -4
  122. package/locales/uk-UA.json +0 -4
  123. package/locales/vi-VN.json +0 -4
  124. package/locales/zh-CN.json +0 -4
  125. package/locales/zh-TW.json +0 -4
  126. package/package.json +3 -3
  127. package/src/BottomSheet/BottomSheet.doc.mjs +37 -0
  128. package/src/BottomSheet/BottomSheet.spec.md +177 -0
  129. package/src/BottomSheet/BottomSheet.test.tsx +29 -0
  130. package/src/BottomSheet/BottomSheet.tsx +7 -4
  131. package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
  132. package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
  133. package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
  134. package/src/Button/Button.doc.mjs +56 -0
  135. package/src/Button/Button.test.tsx +10 -0
  136. package/src/Button/Button.tsx +13 -5
  137. package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
  138. package/src/Calendar/Calendar.doc.mjs +1 -1
  139. package/src/Carousel/Carousel.test.tsx +83 -0
  140. package/src/Carousel/Carousel.tsx +8 -2
  141. package/src/Chat/ChatToolCalls.test.tsx +44 -0
  142. package/src/Chat/ChatToolCalls.tsx +36 -15
  143. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  144. package/src/CheckboxList/CheckboxList.doc.mjs +173 -32
  145. package/src/CheckboxList/CheckboxList.spec.md +214 -0
  146. package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
  147. package/src/CommandPalette/CommandPalette.spec.md +223 -0
  148. package/src/ComplexSelector/ComplexSelector.doc.mjs +44 -2
  149. package/src/ComplexSelector/ComplexSelector.spec.md +189 -0
  150. package/src/ContextMenu/ContextMenu.doc.mjs +13 -1
  151. package/src/ContextMenu/ContextMenu.test.tsx +131 -1
  152. package/src/ContextMenu/ContextMenu.tsx +199 -32
  153. package/src/ContextMenu/index.ts +1 -0
  154. package/src/DateInput/DateInput.doc.mjs +1 -1
  155. package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
  156. package/src/DateTimeInput/DateTimeInput.doc.mjs +1 -1
  157. package/src/Divider/Divider.doc.mjs +24 -0
  158. package/src/Divider/Divider.spec.md +150 -0
  159. package/src/DropdownMenu/DropdownMenu.doc.mjs +201 -40
  160. package/src/DropdownMenu/DropdownMenu.test.tsx +518 -7
  161. package/src/DropdownMenu/DropdownMenu.tsx +459 -28
  162. package/src/DropdownMenu/DropdownMenuContext.tsx +1 -1
  163. package/src/DropdownMenu/DropdownMenuItem.tsx +2 -0
  164. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +4 -2
  165. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +124 -0
  166. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +72 -17
  167. package/src/DropdownMenu/MenuBottomSheet.tsx +46 -0
  168. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +141 -0
  169. package/src/DropdownMenu/index.ts +2 -0
  170. package/src/DropdownMenu/menuWidth.ts +51 -0
  171. package/src/DropdownMenu/useMenuOverflow.ts +77 -0
  172. package/src/Field/Field.doc.mjs +1 -1
  173. package/src/FieldStatus/FieldStatus.doc.mjs +22 -1
  174. package/src/FieldStatus/FieldStatus.spec.md +212 -0
  175. package/src/FileInput/FileInput.doc.mjs +1 -1
  176. package/src/Grid/Grid.doc.mjs +19 -0
  177. package/src/Grid/Grid.spec.md +161 -0
  178. package/src/Icon/Icon.doc.mjs +196 -48
  179. package/src/Icon/Icon.spec.md +191 -0
  180. package/src/IconButton/IconButton.doc.mjs +38 -0
  181. package/src/Indicator/Indicator.doc.mjs +1 -1
  182. package/src/InputGroup/InputGroup.doc.mjs +1 -1
  183. package/src/Kbd/Kbd.doc.mjs +18 -0
  184. package/src/Kbd/Kbd.spec.md +141 -0
  185. package/src/Layout/Layout.doc.mjs +32 -0
  186. package/src/Layout/Layout.spec.md +199 -0
  187. package/src/Lightbox/Lightbox.doc.mjs +149 -33
  188. package/src/Lightbox/Lightbox.spec.md +197 -0
  189. package/src/Markdown/Markdown.doc.mjs +60 -0
  190. package/src/Markdown/Markdown.spec.md +200 -0
  191. package/src/MobileNav/MobileNav.doc.mjs +140 -32
  192. package/src/MobileNav/MobileNav.spec.md +208 -0
  193. package/src/MoreMenu/MoreMenu.doc.mjs +17 -0
  194. package/src/MoreMenu/MoreMenu.test.tsx +47 -0
  195. package/src/MoreMenu/MoreMenu.tsx +13 -1
  196. package/src/MultiSelector/MultiSelector.doc.mjs +129 -1
  197. package/src/MultiSelector/MultiSelector.spec.md +266 -0
  198. package/src/MultiSelector/MultiSelector.test.tsx +127 -0
  199. package/src/MultiSelector/MultiSelector.tsx +121 -73
  200. package/src/MultiSelector/index.ts +1 -0
  201. package/src/NavIcon/NavIcon.doc.mjs +17 -0
  202. package/src/NavIcon/NavIcon.spec.md +133 -0
  203. package/src/NavMenu/NavHeadingMenu.spec.md +205 -0
  204. package/src/NavMenu/NavMenu.doc.mjs +37 -0
  205. package/src/NumberInput/NumberInput.doc.mjs +1 -1
  206. package/src/Outline/Outline.doc.mjs +35 -0
  207. package/src/Outline/Outline.spec.md +161 -0
  208. package/src/OverflowList/OverflowList.doc.mjs +24 -0
  209. package/src/OverflowList/OverflowList.spec.md +156 -0
  210. package/src/Pagination/Pagination.doc.mjs +74 -0
  211. package/src/Pagination/Pagination.spec.md +225 -0
  212. package/src/Popover/Popover.doc.mjs +11 -11
  213. package/src/Popover/Popover.test.tsx +397 -2
  214. package/src/Popover/Popover.tsx +228 -21
  215. package/src/Popover/usePopover.tsx +92 -15
  216. package/src/PowerSearch/PowerSearch.doc.mjs +1 -1
  217. package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
  218. package/src/ProgressBar/ProgressBar.spec.md +260 -0
  219. package/src/RadioList/RadioList.doc.mjs +1 -1
  220. package/src/Resizable/Resizable.doc.mjs +59 -16
  221. package/src/Resizable/Resizable.spec.md +194 -0
  222. package/src/Section/Section.doc.mjs +17 -0
  223. package/src/Section/Section.spec.md +138 -0
  224. package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
  225. package/src/SegmentedControl/SegmentedControl.spec.md +151 -0
  226. package/src/Selector/Selector.doc.mjs +13 -1
  227. package/src/Selector/Selector.test.tsx +135 -0
  228. package/src/Selector/Selector.tsx +141 -82
  229. package/src/Selector/SelectorBottomSheet.tsx +97 -0
  230. package/src/Selector/index.ts +1 -0
  231. package/src/Selector/selectorPresentation.stylex.ts +20 -0
  232. package/src/Selector/useSelectorPresentation.ts +147 -0
  233. package/src/Skeleton/Skeleton.doc.mjs +12 -0
  234. package/src/Skeleton/Skeleton.spec.md +126 -0
  235. package/src/Slider/Slider.doc.mjs +186 -32
  236. package/src/Slider/Slider.spec.md +223 -0
  237. package/src/Stack/Stack.doc.mjs +22 -0
  238. package/src/Stack/Stack.spec.md +154 -0
  239. package/src/StatusDot/StatusDot.doc.mjs +17 -0
  240. package/src/StatusDot/StatusDot.spec.md +137 -0
  241. package/src/Switch/Switch.doc.mjs +41 -1
  242. package/src/Switch/Switch.spec.md +174 -0
  243. package/src/Text/Text.doc.mjs +190 -43
  244. package/src/Text/Text.spec.md +205 -0
  245. package/src/TextArea/TextArea.doc.mjs +256 -46
  246. package/src/TextArea/TextArea.spec.md +204 -0
  247. package/src/TextInput/TextInput.doc.mjs +1 -1
  248. package/src/TimeInput/TimeInput.doc.mjs +1 -1
  249. package/src/Toast/ToastViewport.test.tsx +118 -4
  250. package/src/Toast/useToastGesture.ts +61 -8
  251. package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
  252. package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
  253. package/src/Toolbar/Toolbar.doc.mjs +19 -0
  254. package/src/Toolbar/Toolbar.spec.md +173 -0
  255. package/src/Tooltip/Tooltip.doc.mjs +17 -0
  256. package/src/Tooltip/Tooltip.spec.md +141 -0
  257. package/src/TreeList/TreeList.doc.mjs +58 -0
  258. package/src/TreeList/TreeList.spec.md +220 -0
  259. package/src/Typeahead/Typeahead.doc.mjs +84 -2
  260. package/src/Typeahead/Typeahead.spec.md +250 -0
  261. package/src/hooks/useAdaptivePresentation.ts +35 -0
  262. package/src/hooks/useFocusReturnVisibility.ts +45 -0
  263. package/src/hooks/useFocusTrap.test.tsx +40 -0
  264. package/src/hooks/useFocusTrap.ts +13 -0
  265. package/src/i18n/InternationalizationContext.ts +3 -0
  266. package/src/i18n/InternationalizationProvider.tsx +8 -1
  267. package/src/i18n/__tests__/resolve.test.ts +23 -1
  268. package/src/i18n/resolve.ts +46 -49
  269. package/src/i18n/useTranslator.ts +2 -7
  270. package/src/theme/themingTargets.test.ts +91 -25
@@ -1,5 +1,43 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ /** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
4
+ const anatomy = [
5
+ {
6
+ name: 'Navigation overlay',
7
+ required: true,
8
+ description:
9
+ 'Full-viewport dialog overlay that hosts the mobile navigation drawer.',
10
+ },
11
+ {
12
+ name: 'Drawer',
13
+ required: true,
14
+ description:
15
+ 'Painted panel that slides in from the resolved viewport edge.',
16
+ },
17
+ {
18
+ name: 'Header',
19
+ required: true,
20
+ description:
21
+ 'Fixed row containing optional header content and the close button.',
22
+ },
23
+ {
24
+ name: 'Content',
25
+ required: true,
26
+ description: 'Scrollable region containing the navigation content.',
27
+ },
28
+ {
29
+ name: 'Close button',
30
+ required: true,
31
+ description: 'Button that closes the navigation drawer.',
32
+ },
33
+ {
34
+ name: 'Toggle button',
35
+ required: false,
36
+ description:
37
+ 'AppShell-aware Button that opens or closes the drawer on mobile viewports.',
38
+ },
39
+ ];
40
+
3
41
  /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
4
42
 
5
43
  export const docs = {
@@ -8,12 +46,24 @@ export const docs = {
8
46
  group: 'Navigation',
9
47
  category: 'Navigation',
10
48
  isHiddenFromOverview: true,
11
- keywords: ["mobilenav","drawer","sidebar","navigation","hamburger","menu","offcanvas","slideout","navdrawer","toggle"],
49
+ keywords: [
50
+ 'mobilenav',
51
+ 'drawer',
52
+ 'sidebar',
53
+ 'navigation',
54
+ 'hamburger',
55
+ 'menu',
56
+ 'offcanvas',
57
+ 'slideout',
58
+ 'navdrawer',
59
+ 'toggle',
60
+ ],
12
61
  components: [
13
62
  {
14
63
  name: 'MobileNav',
15
64
  displayName: 'Mobile Nav',
16
- description: 'A slide-out drawer for mobile navigation. Accepts SideNav children.',
65
+ description:
66
+ 'A slide-out drawer for mobile navigation. Accepts SideNav children.',
17
67
  // The drawer opens via showModal() and renders nothing while closed —
18
68
  // overlay mode gives the Properties preview an open trigger instead of
19
69
  // an empty stage (#2706). Declared on this entry (not the directory
@@ -27,7 +77,10 @@ export const docs = {
27
77
  __element: 'SideNavSection',
28
78
  props: {title: 'Main'},
29
79
  children: [
30
- {__element: 'SideNavItem', props: {label: 'Dashboard', isSelected: true}},
80
+ {
81
+ __element: 'SideNavItem',
82
+ props: {label: 'Dashboard', isSelected: true},
83
+ },
31
84
  {__element: 'SideNavItem', props: {label: 'Projects'}},
32
85
  {__element: 'SideNavItem', props: {label: 'Settings'}},
33
86
  ],
@@ -38,7 +91,8 @@ export const docs = {
38
91
  {
39
92
  name: 'isOpen',
40
93
  type: 'boolean',
41
- description: 'Whether the drawer is open. Inside AppShell, this is managed automatically via context. Outside AppShell, provide this prop to control the drawer yourself.',
94
+ description:
95
+ 'Whether the drawer is open. Inside AppShell, this is managed automatically via context. Outside AppShell, provide this prop to control the drawer yourself.',
42
96
  },
43
97
  {
44
98
  name: 'onOpenChange',
@@ -56,7 +110,8 @@ export const docs = {
56
110
  {
57
111
  name: 'header',
58
112
  type: 'ReactNode',
59
- description: 'Header content for the drawer. Rendered next to the close button. Pass a string for a simple text heading, or a ReactNode for custom content (logo, search bar, etc.).',
113
+ description:
114
+ 'Header content for the drawer. Rendered next to the close button. Pass a string for a simple text heading, or a ReactNode for custom content (logo, search bar, etc.).',
60
115
  },
61
116
  {
62
117
  name: 'width',
@@ -77,7 +132,8 @@ export const docs = {
77
132
  {
78
133
  name: 'MobileNavToggle',
79
134
  displayName: 'Mobile Nav Toggle',
80
- description: 'Hamburger button that opens/closes the mobile nav drawer. Reads open state from AppShell context automatically: does NOT accept isOpen or onOpenChange props. Renders nothing above the mobile breakpoint.',
135
+ description:
136
+ 'Hamburger button that opens/closes the mobile nav drawer. Reads open state from AppShell context automatically: does NOT accept isOpen or onOpenChange props. Renders nothing above the mobile breakpoint.',
81
137
  // The toggle renders null unless AppShell mobile context reports an
82
138
  // enabled mobile viewport — the default context outside AppShell never
83
139
  // does, so the Properties preview was an empty stage. appShellMobile
@@ -89,7 +145,8 @@ export const docs = {
89
145
  {
90
146
  name: 'children',
91
147
  type: 'ReactNode',
92
- description: 'Custom content to render instead of the default hamburger icon.',
148
+ description:
149
+ 'Custom content to render instead of the default hamburger icon.',
93
150
  },
94
151
  {
95
152
  name: 'label',
@@ -101,18 +158,33 @@ export const docs = {
101
158
  },
102
159
  ],
103
160
  theming: {
104
- targets: [
105
- {className: 'astryx-mobile-nav', visualProps: ['side']},
106
- ],
161
+ targets: [{className: 'astryx-mobile-nav', visualProps: ['side']}],
107
162
  },
108
163
  usage: {
164
+ anatomy,
109
165
  description:
110
166
  'A slide-out drawer for mobile navigation. MobileNav is the mobile counterpart to SideNav and accepts the same children. Use it on narrow viewports where a persistent sidebar is not practical. Inside AppShell, use MobileNavToggle as the trigger; it reads state from context automatically.',
111
167
  bestPractices: [
112
- { guidance: true, description: 'Share the same nav items between MobileNav and SideNav by extracting them into a variable.' },
113
- { guidance: true, description: 'Provide a header when the drawer\'s purpose is not obvious from its content.' },
114
- { guidance: true, description: 'Inside AppShell, use MobileNavToggle to open the drawer; it reads state from context. Do not pass isOpen/onOpenChange to the toggle.' },
115
- { guidance: false, description: 'Use MobileNav on desktop: use a persistent SideNav instead.' },
168
+ {
169
+ guidance: true,
170
+ description:
171
+ 'Share the same nav items between MobileNav and SideNav by extracting them into a variable.',
172
+ },
173
+ {
174
+ guidance: true,
175
+ description:
176
+ "Provide a header when the drawer's purpose is not obvious from its content.",
177
+ },
178
+ {
179
+ guidance: true,
180
+ description:
181
+ 'Inside AppShell, use MobileNavToggle to open the drawer; it reads state from context. Do not pass isOpen/onOpenChange to the toggle.',
182
+ },
183
+ {
184
+ guidance: false,
185
+ description:
186
+ 'Use MobileNav on desktop: use a persistent SideNav instead.',
187
+ },
116
188
  ],
117
189
  },
118
190
  };
@@ -144,13 +216,13 @@ export const docsZh = {
144
216
  {
145
217
  name: 'header',
146
218
  type: 'ReactNode',
147
- description: '抽屉的头部内容。渲染在关闭按钮旁边。传入字符串作为简单文本标题,或传入 ReactNode 作为自定义内容。',
219
+ description:
220
+ '抽屉的头部内容。渲染在关闭按钮旁边。传入字符串作为简单文本标题,或传入 ReactNode 作为自定义内容。',
148
221
  },
149
222
  {
150
223
  name: 'width',
151
224
  type: 'number',
152
- description:
153
- '抽屉宽度(像素)。上限为 85vw 以防止在小屏幕上溢出。',
225
+ description: '抽屉宽度(像素)。上限为 85vw 以防止在小屏幕上溢出。',
154
226
  default: '320',
155
227
  },
156
228
  {
@@ -162,18 +234,33 @@ export const docsZh = {
162
234
  },
163
235
  ],
164
236
  theming: {
165
- targets: [
166
- {className: 'astryx-mobile-nav', visualProps: ['side']},
167
- ],
237
+ targets: [{className: 'astryx-mobile-nav', visualProps: ['side']}],
168
238
  },
169
239
  usage: {
240
+ anatomy,
170
241
  description:
171
242
  'A slide-out drawer for mobile navigation. MobileNav is the mobile counterpart to SideNav and accepts the same children. Use it on narrow viewports where a persistent sidebar is not practical.',
172
243
  bestPractices: [
173
- { guidance: true, description: 'Share the same nav items between MobileNav and SideNav by extracting them into a variable.' },
174
- { guidance: true, description: 'Provide a header when the drawer\'s purpose is not obvious from its content.' },
175
- { guidance: true, description: 'Inside AppShell, use MobileNavToggle to open the drawer; it reads state from context. Do not pass isOpen/onOpenChange to the toggle.' },
176
- { guidance: false, description: 'Use MobileNav on desktop: use a persistent SideNav instead.' },
244
+ {
245
+ guidance: true,
246
+ description:
247
+ 'Share the same nav items between MobileNav and SideNav by extracting them into a variable.',
248
+ },
249
+ {
250
+ guidance: true,
251
+ description:
252
+ "Provide a header when the drawer's purpose is not obvious from its content.",
253
+ },
254
+ {
255
+ guidance: true,
256
+ description:
257
+ 'Inside AppShell, use MobileNavToggle to open the drawer; it reads state from context. Do not pass isOpen/onOpenChange to the toggle.',
258
+ },
259
+ {
260
+ guidance: false,
261
+ description:
262
+ 'Use MobileNav on desktop: use a persistent SideNav instead.',
263
+ },
177
264
  ],
178
265
  },
179
266
  };
@@ -183,23 +270,42 @@ export const docsDense = {
183
270
  description:
184
271
  'Slide-out drawer overlay for mobile navigation. Mobile counterpart to SideNav; accepts same children (SideNavSection, SideNavItem, or any ReactNode).',
185
272
  usage: {
273
+ anatomy,
186
274
  description:
187
275
  'A slide-out drawer for mobile navigation. MobileNav is the mobile counterpart to SideNav and accepts the same children. Use it on narrow viewports where a persistent sidebar is not practical.',
188
276
  bestPractices: [
189
- { guidance: true, description: 'Share nav items between MobileNav and SideNav by extracting into a variable.' },
190
- { guidance: true, description: 'Provide a header when the drawer purpose is not obvious from content.' },
191
- { guidance: true, description: 'Inside AppShell, use MobileNavToggle to open the drawer; it reads state from context. Do not pass isOpen/onOpenChange to the toggle.' },
192
- { guidance: false, description: 'Use MobileNav on desktop; use a persistent SideNav instead.' },
277
+ {
278
+ guidance: true,
279
+ description:
280
+ 'Share nav items between MobileNav and SideNav by extracting into a variable.',
281
+ },
282
+ {
283
+ guidance: true,
284
+ description:
285
+ 'Provide a header when the drawer purpose is not obvious from content.',
286
+ },
287
+ {
288
+ guidance: true,
289
+ description:
290
+ 'Inside AppShell, use MobileNavToggle to open the drawer; it reads state from context. Do not pass isOpen/onOpenChange to the toggle.',
291
+ },
292
+ {
293
+ guidance: false,
294
+ description:
295
+ 'Use MobileNav on desktop; use a persistent SideNav instead.',
296
+ },
193
297
  ],
194
298
  },
195
299
  components: [
196
300
  {
197
301
  name: 'MobileNav',
198
- description: 'Slide-out drawer for mobile navigation. Accepts SideNav children.',
302
+ description:
303
+ 'Slide-out drawer for mobile navigation. Accepts SideNav children.',
199
304
  },
200
305
  {
201
306
  name: 'MobileNavToggle',
202
- description: 'Hamburger button that opens/closes mobile nav drawer. Reads open state from AppShell context automatically. Renders nothing above mobile breakpoint.',
307
+ description:
308
+ 'Hamburger button that opens/closes mobile nav drawer. Reads open state from AppShell context automatically. Renders nothing above mobile breakpoint.',
203
309
  },
204
310
  ],
205
311
  propDescriptions: {
@@ -208,8 +314,10 @@ export const docsDense = {
208
314
  'called when drawer visibility changes (backdrop click, Escape, close button)',
209
315
  children:
210
316
  'drawer content; typically SideNavSection/SideNavItem or any ReactNode',
211
- header: 'header content (string or ReactNode), rendered next to close button',
212
- width: 'drawer width px; capped at 85vw to prevent overflow on small screens',
317
+ header:
318
+ 'header content (string or ReactNode), rendered next to close button',
319
+ width:
320
+ 'drawer width px; capped at 85vw to prevent overflow on small screens',
213
321
  side: 'slide direction; start=left LTR, right RTL',
214
322
  },
215
323
  };
@@ -0,0 +1,208 @@
1
+ ---
2
+ schema_version: 1
3
+ template_version: 3
4
+ kind: component
5
+ id: component:MobileNav
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers: [behavior, theming, accessibility]
13
+ verified_by:
14
+ [
15
+ packages/core/src/MobileNav/MobileNav.test.tsx,
16
+ packages/core/src/MobileNav/MobileNavToggle.test.tsx,
17
+ packages/core/src/MobileNav/MobileNavCloseEdgeCases.test.tsx,
18
+ packages/core/src/theme/themingTargets.test.ts,
19
+ scripts/check-knowledge.mjs,
20
+ ]
21
+ families: [family:overlay-dismissal]
22
+ design_specs: []
23
+ architecture:
24
+ [
25
+ architecture:component-theming-surface,
26
+ architecture:layer-runtime,
27
+ architecture:public-component-api,
28
+ ]
29
+ contributing: []
30
+ system_specs: []
31
+ ---
32
+
33
+ # MobileNav component contract
34
+
35
+ ## Intent
36
+
37
+ MobileNav presents mobile navigation in a modal drawer and exports an AppShell-
38
+ aware Toggle button for opening and closing it. This draft records the aggregate
39
+ consumer anatomy and theming ownership of both exports without changing runtime
40
+ behavior, styling, targets, or public API.
41
+
42
+ ## Compatibility and migration
43
+
44
+ - Released default preserved: `yes`
45
+ - Compatibility class: additive documentation only; runtime, DOM, styling,
46
+ targets, aliases, and public API remain unchanged
47
+ - Controlled/uncontrolled behavior: unchanged
48
+ - Migration decision: none
49
+
50
+ Consumer migration instructions belong in consumer docs and release notes.
51
+
52
+ ## Ownership boundary
53
+
54
+ **Owns**
55
+
56
+ - The Navigation overlay and its current `mobile-nav` target.
57
+ - Drawer placement and motion, the stable Header and Content regions, and the
58
+ connection between AppShell state, MobileNav, and MobileNavToggle.
59
+ - Canonical anatomy for both exported components. `component:MobileNav` is the
60
+ parent owner; MobileNavToggle does not have a separate component contract.
61
+
62
+ **Does not own / non-goals**
63
+
64
+ - The Close and Toggle button surfaces — delegated to `component:Button`.
65
+ - Header or navigation content supplied by the caller.
66
+ - A public target for the painted Drawer, Header, or Content region; none exists
67
+ on current `main`.
68
+ - AppShell responsive policy or shared layer and dismissal policy.
69
+
70
+ ## Public concepts
71
+
72
+ No new public concept is introduced. Consumer props, exports, and usage remain
73
+ documented in `MobileNav.doc.mjs`.
74
+
75
+ ## Behavioral and layout contract
76
+
77
+ | ID | Candidate invariant | Basis | Draft review state |
78
+ | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------- |
79
+ | FR1 | MobileNav renders one Navigation overlay containing a Drawer with a stable Header, Close button, and scrollable Content region. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
80
+ | FR2 | Navigation overlay carries the current `mobile-nav` target. Drawer, Header, and Content carry no MobileNav public target. | Current source and public docs | Verified current reachability; no target change |
81
+ | FR3 | Close button and the separately exported Toggle button use Button's `button` target; Toggle renders only when AppShell mobile context enables it and references the drawer by ID. | Current source, docs, and tests | Verified current delegation; no ownership change |
82
+
83
+ ### Allowed variation
84
+
85
+ - **AV1 — Drawer edge.** The Drawer may resolve to logical start or end through
86
+ the current explicit or trigger-derived side behavior.
87
+ - **AV2 — Header content.** Header may contain a string rendered through Heading,
88
+ caller-provided content, or only the Close button.
89
+ - **AV3 — Navigation content.** Caller-provided navigation content may vary
90
+ without becoming MobileNav-owned anatomy.
91
+ - **AV4 — Toggle presence.** Toggle button is absent outside an enabled AppShell
92
+ mobile context.
93
+
94
+ ### Representative states
95
+
96
+ | State | Required invariant | Allowed variation |
97
+ | ------------------------ | ---------------------------------------------------------------------- | -------------------------------------------------- |
98
+ | Standalone drawer | Navigation overlay contains Drawer, Header, Close button, and Content. | Caller controls open state and may choose an edge. |
99
+ | AppShell drawer | The same drawer anatomy consumes AppShell mobile state. | Header and navigation content remain caller-owned. |
100
+ | AppShell mobile toggle | Toggle button controls and references the current drawer. | Custom button content may replace the menu glyph. |
101
+ | Non-mobile/disabled host | Toggle button does not render. | MobileNav remains owned by the parent contract. |
102
+
103
+ ### Transformation and precedence order
104
+
105
+ - No new side resolution, open-state, close timing, or styling precedence rule is
106
+ introduced.
107
+
108
+ ### Performance and resources
109
+
110
+ - No new performance or resource rule is introduced.
111
+
112
+ ## Accessibility contract
113
+
114
+ This draft does not change or extend MobileNav's existing dialog naming, focus,
115
+ Toggle relationship, Button labels, modal behavior, or dismissal behavior.
116
+
117
+ ## Design relationships
118
+
119
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
120
+ | ------------------ | --------------------------------------------------------------------- | ------------------------------ | -------------- | ------------------ |
121
+ | Navigation overlay | Hosts the modal navigation layer and its backdrop. | Current source and public docs | Supporting | FR1, FR2 |
122
+ | Drawer | Paints the sliding mobile navigation surface. | Current source and public docs | Prominent | FR1, FR2 |
123
+ | Header | Arranges optional header content with the persistent Close button. | Current source and public docs | Supporting | FR1, FR2 |
124
+ | Content | Provides the scrolling region for caller-supplied navigation content. | Current source and public docs | Prominent | FR1, FR2 |
125
+ | Close button | Dismisses the current drawer. | `component:Button` | Supporting | FR1, FR3 |
126
+ | Toggle button | Opens or closes the AppShell-owned mobile drawer state. | `component:Button` | Supporting | FR3 |
127
+
128
+ Drawer is the primary painted surface, but the only current MobileNav target is
129
+ on the full-viewport dialog overlay. Its `none` disposition is a factual
130
+ reachability gap, not a decision that the Drawer must remain unthemeable. Header
131
+ and Content likewise have no current local target. Close and Toggle retain Button
132
+ ownership.
133
+
134
+ ### Theming anatomy
135
+
136
+ <!-- anatomy-theming:v1 -->
137
+
138
+ ```json
139
+ {
140
+ "Navigation overlay": {"target": "mobile-nav"},
141
+ "Drawer": {
142
+ "none": {
143
+ "reason": "reachability-gap: No current public target is applied to the painted drawer panel"
144
+ }
145
+ },
146
+ "Header": {
147
+ "none": {
148
+ "reason": "reachability-gap: No current public target is applied to the stable header row"
149
+ }
150
+ },
151
+ "Content": {
152
+ "none": {
153
+ "reason": "reachability-gap: No current public target is applied to the scrollable content region"
154
+ }
155
+ },
156
+ "Close button": {
157
+ "delegatesTo": {"owner": "component:Button", "target": "button"}
158
+ },
159
+ "Toggle button": {
160
+ "delegatesTo": {"owner": "component:Button", "target": "button"}
161
+ }
162
+ }
163
+ ```
164
+
165
+ ## Family and system relationships
166
+
167
+ - `architecture:component-theming-surface` owns anatomy qualification, target
168
+ mapping, delegation, and factual `none` dispositions.
169
+ - `architecture:layer-runtime` owns the current native-dialog host and shared
170
+ layer plumbing; MobileNav retains its current drawer motion and backdrop path.
171
+ - `family:overlay-dismissal` owns shared Escape and platform-close ordering and
172
+ records MobileNav as a current adopter.
173
+ - `architecture:public-component-api` owns the shared API boundary; this draft
174
+ changes neither MobileNav nor MobileNavToggle API.
175
+ - AppShell remains the higher-level responsive owner. This parent component
176
+ record owns the two MobileNav exports' shared anatomy only.
177
+
178
+ ## Verification map
179
+
180
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
181
+ | ------------------- | ----------------------------------------------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------- |
182
+ | FR1 | `MobileNav.test.tsx` content/header/close assertions plus source inspection | Standalone open and closed drawer | Removing dialog content or the Close action fails tests; distinct Drawer/Header/Content wrappers rely on review. | `audit:MobileNav/anatomy` |
183
+ | FR2 | Source inspection and `themingTargets.test.ts` | Overlay, Drawer, Header, and Content | Removing the root target or documenting an unshipped child target fails evidence or inventory. | `audit:MobileNav/theming` |
184
+ | FR3 | `MobileNavToggle.test.tsx`, `MobileNavReopen.test.tsx`, and source inspection | Closed/open AppShell drawer and toggle | Breaking Button delegation or the ID/state relationship fails focused toggle coverage. | `audit:MobileNav/anatomy` |
185
+ | Close lifecycle | `MobileNavCloseEdgeCases.test.tsx` and related close/visibility suites | Escape, backdrop, close, reopen, unmount | Leaving the modal open or disconnecting state fails existing lifecycle assertions. | `audit:MobileNav/behavior` |
186
+ | Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical parent anatomy and current target | Missing, extra, prefixed, stale, or multiply assigned mappings fail repository validation. | `audit:MobileNav/theming` |
187
+
188
+ Current tests find header and navigation content by text and the Close action by
189
+ role, but do not identify the distinct Drawer, Header, or Content elements. Their
190
+ existence and nesting are source-inspected; merging or reordering those wrappers
191
+ currently lacks focused regression evidence.
192
+
193
+ ## Decision log
194
+
195
+ None. This draft records current facts and introduces no component-local design,
196
+ API, theming, responsive, or layer-system decision.
197
+
198
+ ## Open questions
199
+
200
+ - **OQ1 — Should Drawer gain a stable public theming target?** (`human-api`) The
201
+ current target reaches the Navigation overlay rather than the primary painted
202
+ drawer surface; this is an audit gap, not settled intent.
203
+
204
+ ## Content boundary
205
+
206
+ This file does not duplicate consumer prop tables/examples, AppShell responsive
207
+ policy, close-timing mechanics, shared layer rules, current audit results, or
208
+ implementation steps. It links to their owners.
@@ -61,6 +61,12 @@ export const docs = {
61
61
  "Alignment along the placement axis. Use 'end' to align the menu with the trigger's trailing edge, which is usually what an overflow menu wants.",
62
62
  default: "'start'",
63
63
  },
64
+ {
65
+ name: 'presentation',
66
+ type: "'popover' | 'bottom-sheet' | 'adaptive'",
67
+ description: 'Presentation policy forwarded to DropdownMenu. `adaptive` keeps the anchored popover on pointer-based layouts and uses a BottomSheet at 768px and below when the primary pointer is coarse.',
68
+ default: "'popover'",
69
+ },
64
70
  {
65
71
  name: 'onOpenChange',
66
72
  type: '(isOpen: boolean) => void',
@@ -93,6 +99,7 @@ export const docs = {
93
99
  bestPractices: [
94
100
  { guidance: true, description: 'Use for overflow or secondary actions; keep primary actions visible outside the menu.' },
95
101
  { guidance: true, description: 'Use dividers or sections to group related actions when the menu has many items.' },
102
+ { guidance: true, description: 'Use `presentation="adaptive"` when the visible overflow trigger should open a thumb-reachable BottomSheet on compact touch devices.' },
96
103
  { guidance: false, description: 'Hide primary actions inside a MoreMenu; they should be directly visible.' },
97
104
  ],
98
105
  },
@@ -153,6 +160,12 @@ export const docsZh = {
153
160
  "沿放置轴的对齐方式。使用 'end' 让菜单与触发器的尾部边缘对齐,这通常是溢出菜单所需的效果。",
154
161
  default: "'start'",
155
162
  },
163
+ {
164
+ name: 'presentation',
165
+ type: "'popover' | 'bottom-sheet' | 'adaptive'",
166
+ description: '菜单呈现策略。`adaptive` 在指针布局中使用锚定浮层,在主指针为粗略指针且宽度不超过 768px 时使用 BottomSheet。',
167
+ default: "'popover'",
168
+ },
156
169
  {
157
170
  name: 'xstyle',
158
171
  type: 'StyleXStyles',
@@ -171,6 +184,7 @@ export const docsZh = {
171
184
  bestPractices: [
172
185
  { guidance: true, description: 'Use for overflow or secondary actions; keep primary actions visible outside the menu.' },
173
186
  { guidance: true, description: 'Use dividers or sections to group related actions when the menu has many items.' },
187
+ { guidance: true, description: 'Use `presentation="adaptive"` when the visible overflow trigger should open a thumb-reachable BottomSheet on compact touch devices.' },
174
188
  { guidance: false, description: 'Hide primary actions inside a MoreMenu; they should be directly visible.' },
175
189
  ],
176
190
  },
@@ -186,6 +200,7 @@ export const docsDense = {
186
200
  bestPractices: [
187
201
  { guidance: true, description: 'Use for overflow or secondary actions; keep primary actions visible outside the menu.' },
188
202
  { guidance: true, description: 'Use dividers or sections to group related actions when the menu has many items.' },
203
+ { guidance: true, description: 'Use `presentation="adaptive"` when the visible overflow trigger should open a thumb-reachable BottomSheet on compact touch devices.' },
189
204
  { guidance: false, description: 'Hide primary actions inside a MoreMenu; they should be directly visible.' },
190
205
  ],
191
206
  },
@@ -199,6 +214,8 @@ export const docsDense = {
199
214
  placement: 'Menu position relative to trigger. Logical (RTL mirrors).',
200
215
  alignment:
201
216
  "Alignment along the placement axis. 'end' aligns with the trigger's trailing edge.",
217
+ presentation:
218
+ 'Popover, BottomSheet, or adaptive compact-touch presentation.',
202
219
  xstyle:
203
220
  'StyleX styles for layout customization (margins, positioning, sizing). Must be stylex.create() value.',
204
221
  },
@@ -17,6 +17,30 @@ import {DropdownMenu} from '../DropdownMenu/DropdownMenu';
17
17
 
18
18
  // Mock showPopover and hidePopover methods since they're not implemented in jsdom
19
19
  beforeEach(() => {
20
+ HTMLDialogElement.prototype.showModal = vi.fn(function (
21
+ this: HTMLDialogElement,
22
+ ) {
23
+ this.setAttribute('open', '');
24
+ });
25
+ HTMLDialogElement.prototype.show = vi.fn(function (this: HTMLDialogElement) {
26
+ this.setAttribute('open', '');
27
+ });
28
+ HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
29
+ this.removeAttribute('open');
30
+ });
31
+ vi.stubGlobal(
32
+ 'matchMedia',
33
+ vi.fn().mockImplementation((query: string) => ({
34
+ matches: false,
35
+ media: query,
36
+ onchange: null,
37
+ addEventListener: vi.fn(),
38
+ removeEventListener: vi.fn(),
39
+ addListener: vi.fn(),
40
+ removeListener: vi.fn(),
41
+ dispatchEvent: vi.fn(),
42
+ })),
43
+ );
20
44
  HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
21
45
  this.setAttribute('popover-open', '');
22
46
  const event = new Event('toggle', {bubbles: false});
@@ -85,6 +109,29 @@ describe('MoreMenu', () => {
85
109
  expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
86
110
  });
87
111
 
112
+ it('keeps the trigger visually pressed while the menu is open', async () => {
113
+ const user = userEvent.setup();
114
+ render(<MoreMenu items={defaultItems} />);
115
+
116
+ const trigger = screen.getByRole('button', {name: 'More options'});
117
+ expect(trigger.className).not.toContain('DropdownMenu__styles.triggerOpen');
118
+
119
+ await user.click(trigger);
120
+
121
+ expect(trigger.className).toContain('DropdownMenu__styles.triggerOpen');
122
+ });
123
+
124
+ it('forwards BottomSheet presentation to DropdownMenu', async () => {
125
+ const user = userEvent.setup();
126
+ render(<MoreMenu items={defaultItems} presentation="bottom-sheet" />);
127
+
128
+ await user.click(screen.getByRole('button', {name: 'More options'}));
129
+ await waitFor(() =>
130
+ expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledOnce(),
131
+ );
132
+ expect(HTMLElement.prototype.showPopover).not.toHaveBeenCalled();
133
+ });
134
+
88
135
  it('calls onClick when item is clicked', async () => {
89
136
  const handleEdit = vi.fn();
90
137
  const items = [
@@ -22,7 +22,10 @@ import type {ReactNode} from 'react';
22
22
  import {useIcon} from '../Icon';
23
23
  import {DropdownMenu} from '../DropdownMenu/DropdownMenu';
24
24
  import {useSize} from '../SizeContext/SizeContext';
25
- import type {DropdownMenuOption} from '../DropdownMenu';
25
+ import type {
26
+ DropdownMenuOption,
27
+ DropdownMenuPresentation,
28
+ } from '../DropdownMenu';
26
29
  import type {LayerAlignment, LayerPlacement} from '../Layer';
27
30
  import type {ButtonVariant, ButtonSize} from '../Button';
28
31
  import type {BaseProps} from '../BaseProps';
@@ -87,6 +90,13 @@ export interface MoreMenuProps extends Pick<
87
90
  */
88
91
  alignment?: LayerAlignment;
89
92
 
93
+ /**
94
+ * Menu presentation policy. `adaptive` uses a BottomSheet on compact
95
+ * coarse-pointer viewports and an anchored popover elsewhere.
96
+ * @default 'popover'
97
+ */
98
+ presentation?: DropdownMenuPresentation;
99
+
90
100
  /**
91
101
  * Controlled open state for the menu.
92
102
  */
@@ -125,6 +135,7 @@ export function MoreMenu({
125
135
  isDisabled = false,
126
136
  placement,
127
137
  alignment,
138
+ presentation,
128
139
  isMenuOpen,
129
140
  onOpenChange,
130
141
  xstyle,
@@ -151,6 +162,7 @@ export function MoreMenu({
151
162
  onOpenChange={onOpenChange}
152
163
  placement={placement}
153
164
  alignment={alignment}
165
+ presentation={presentation}
154
166
  button={{
155
167
  label,
156
168
  icon: icon ?? moreIcon,