@multiplatform.one/components 7.79.0 → 7.81.0

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 (411) hide show
  1. package/dist/cjs/CheckGlyph/MenuCheckColumn.cjs +56 -0
  2. package/dist/cjs/CheckGlyph/MenuCheckColumn.native.js +60 -0
  3. package/dist/cjs/CheckGlyph/MenuCheckColumn.native.js.map +1 -0
  4. package/dist/cjs/CheckGlyph/index.cjs +40 -0
  5. package/dist/cjs/CheckGlyph/index.native.js +44 -0
  6. package/dist/cjs/CheckGlyph/index.native.js.map +1 -0
  7. package/dist/cjs/ContextMenu/index.cjs +5 -22
  8. package/dist/cjs/ContextMenu/index.native.js +7 -25
  9. package/dist/cjs/ContextMenu/index.native.js.map +1 -1
  10. package/dist/cjs/DropdownMenu/index.cjs +5 -17
  11. package/dist/cjs/DropdownMenu/index.native.js +7 -19
  12. package/dist/cjs/DropdownMenu/index.native.js.map +1 -1
  13. package/dist/cjs/Popover.cjs +2 -2
  14. package/dist/cjs/Popover.native.js +2 -2
  15. package/dist/cjs/Popover.native.js.map +1 -1
  16. package/dist/cjs/SchemeToggle/index.cjs +2 -2
  17. package/dist/cjs/SchemeToggle/index.native.js +2 -2
  18. package/dist/cjs/SchemeToggle/index.native.js.map +1 -1
  19. package/dist/cjs/SheetModal/index.cjs +4 -1
  20. package/dist/cjs/SheetModal/index.native.js +1 -1
  21. package/dist/cjs/SheetModal/index.native.js.map +1 -1
  22. package/dist/cjs/TreeSelect/index.cjs +2 -6
  23. package/dist/cjs/TreeSelect/index.native.js +2 -6
  24. package/dist/cjs/TreeSelect/index.native.js.map +1 -1
  25. package/dist/cjs/appShell/AppShell.cjs +438 -0
  26. package/dist/cjs/appShell/AppShell.native.js +505 -0
  27. package/dist/cjs/appShell/AppShell.native.js.map +1 -0
  28. package/dist/cjs/appShell/ScopeMenu.cjs +39 -16
  29. package/dist/cjs/appShell/ScopeMenu.native.js +43 -17
  30. package/dist/cjs/appShell/ScopeMenu.native.js.map +1 -1
  31. package/dist/cjs/appShell/TabHost.native.js +41 -10
  32. package/dist/cjs/appShell/TabHost.native.js.map +1 -1
  33. package/dist/cjs/appShell/index.cjs +1 -0
  34. package/dist/cjs/appShell/index.native.js +1 -0
  35. package/dist/cjs/appShell/index.native.js.map +1 -1
  36. package/dist/cjs/layouts/page.cjs +3 -0
  37. package/dist/cjs/layouts/page.native.js +3 -0
  38. package/dist/cjs/layouts/page.native.js.map +1 -1
  39. package/dist/cjs/layouts/screenScroll.cjs +55 -0
  40. package/dist/cjs/layouts/screenScroll.native.js +59 -0
  41. package/dist/cjs/layouts/screenScroll.native.js.map +1 -1
  42. package/dist/cjs/layouts/sidebar.cjs +74 -16
  43. package/dist/cjs/layouts/sidebar.native.js +78 -16
  44. package/dist/cjs/layouts/sidebar.native.js.map +1 -1
  45. package/dist/esm/Accordion/index.native.js.map +1 -1
  46. package/dist/esm/AnimateHeight/index.native.js.map +1 -1
  47. package/dist/esm/Avatar/getInitials.native.js.map +1 -1
  48. package/dist/esm/Avatar/index.native.js.map +1 -1
  49. package/dist/esm/Badge/index.native.js.map +1 -1
  50. package/dist/esm/Button.native.js.map +1 -1
  51. package/dist/esm/Carousel/index.native.js.map +1 -1
  52. package/dist/esm/CheckGlyph/MenuCheckColumn.mjs +31 -0
  53. package/dist/esm/CheckGlyph/MenuCheckColumn.mjs.map +1 -0
  54. package/dist/esm/CheckGlyph/MenuCheckColumn.native.js +32 -0
  55. package/dist/esm/CheckGlyph/MenuCheckColumn.native.js.map +1 -0
  56. package/dist/esm/CheckGlyph/index.mjs +15 -0
  57. package/dist/esm/CheckGlyph/index.mjs.map +1 -0
  58. package/dist/esm/CheckGlyph/index.native.js +16 -0
  59. package/dist/esm/CheckGlyph/index.native.js.map +1 -0
  60. package/dist/esm/Chip/index.native.js.map +1 -1
  61. package/dist/esm/Collapsible.native.js.map +1 -1
  62. package/dist/esm/ContextMenu/index.mjs +6 -23
  63. package/dist/esm/ContextMenu/index.mjs.map +1 -1
  64. package/dist/esm/ContextMenu/index.native.js +8 -26
  65. package/dist/esm/ContextMenu/index.native.js.map +1 -1
  66. package/dist/esm/CopyField/CopyMenuButton.native.js.map +1 -1
  67. package/dist/esm/CopyField/index.native.js.map +1 -1
  68. package/dist/esm/Dialog.native.js.map +1 -1
  69. package/dist/esm/Diff/diff.native.js.map +1 -1
  70. package/dist/esm/Diff/index.native.js.map +1 -1
  71. package/dist/esm/DotIndicator/index.native.js.map +1 -1
  72. package/dist/esm/DropdownMenu/index.mjs +6 -18
  73. package/dist/esm/DropdownMenu/index.mjs.map +1 -1
  74. package/dist/esm/DropdownMenu/index.native.js +8 -20
  75. package/dist/esm/DropdownMenu/index.native.js.map +1 -1
  76. package/dist/esm/DropdownMenu/menuSettle.native.js.map +1 -1
  77. package/dist/esm/ElapsedProgress/index.native.js.map +1 -1
  78. package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
  79. package/dist/esm/Heading.native.js.map +1 -1
  80. package/dist/esm/Kbd/index.native.js.map +1 -1
  81. package/dist/esm/Kbd/keys.native.js.map +1 -1
  82. package/dist/esm/Lightbox/MediaControl.native.js.map +1 -1
  83. package/dist/esm/Lightbox/index.gnome.native.js.map +1 -1
  84. package/dist/esm/Lightbox/shared.native.js.map +1 -1
  85. package/dist/esm/Lightbox/useLightbox.native.js.map +1 -1
  86. package/dist/esm/MachineLiteral/index.native.js.map +1 -1
  87. package/dist/esm/Material/index.native.js.map +1 -1
  88. package/dist/esm/Meter/index.native.js.map +1 -1
  89. package/dist/esm/Meter/meter.native.js.map +1 -1
  90. package/dist/esm/OverflowRow/index.native.js.map +1 -1
  91. package/dist/esm/Pagination.native.js.map +1 -1
  92. package/dist/esm/Popover.mjs +2 -2
  93. package/dist/esm/Popover.mjs.map +1 -1
  94. package/dist/esm/Popover.native.js +2 -2
  95. package/dist/esm/Popover.native.js.map +1 -1
  96. package/dist/esm/ProgressSteps/index.native.js.map +1 -1
  97. package/dist/esm/Quote/index.native.js.map +1 -1
  98. package/dist/esm/SchemeToggle/index.mjs +3 -3
  99. package/dist/esm/SchemeToggle/index.mjs.map +1 -1
  100. package/dist/esm/SchemeToggle/index.native.js +3 -3
  101. package/dist/esm/SchemeToggle/index.native.js.map +1 -1
  102. package/dist/esm/ScrollView/revealFocusedField.native.js.map +1 -1
  103. package/dist/esm/Separator/index.native.js.map +1 -1
  104. package/dist/esm/Sheet.native.js.map +1 -1
  105. package/dist/esm/SheetModal/SheetHeader.native.js.map +1 -1
  106. package/dist/esm/SheetModal/SheetNavigation.native.js.map +1 -1
  107. package/dist/esm/SheetModal/detents.native.js.map +1 -1
  108. package/dist/esm/SheetModal/index.mjs +5 -2
  109. package/dist/esm/SheetModal/index.mjs.map +1 -1
  110. package/dist/esm/SheetModal/index.native.js +2 -2
  111. package/dist/esm/SheetModal/index.native.js.map +1 -1
  112. package/dist/esm/SheetModal/useSheetFrameFocus.native.js.map +1 -1
  113. package/dist/esm/Skeleton/index.native.js.map +1 -1
  114. package/dist/esm/SkipLink/index.native.js.map +1 -1
  115. package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
  116. package/dist/esm/SortableList/index.native.js.map +1 -1
  117. package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
  118. package/dist/esm/SortableList/useUserOrder.native.js.map +1 -1
  119. package/dist/esm/Tabs/index.native.js.map +1 -1
  120. package/dist/esm/Text.native.js.map +1 -1
  121. package/dist/esm/ThemePresetPicker/index.native.js.map +1 -1
  122. package/dist/esm/Toast/index.native.js.map +1 -1
  123. package/dist/esm/Toast/store.native.js.map +1 -1
  124. package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
  125. package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
  126. package/dist/esm/TreeSelect/index.mjs +4 -8
  127. package/dist/esm/TreeSelect/index.mjs.map +1 -1
  128. package/dist/esm/TreeSelect/index.native.js +4 -8
  129. package/dist/esm/TreeSelect/index.native.js.map +1 -1
  130. package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
  131. package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
  132. package/dist/esm/Video/VideoFrame.native.js.map +1 -1
  133. package/dist/esm/Video/types.native.js.map +1 -1
  134. package/dist/esm/Wheel/index.native.js.map +1 -1
  135. package/dist/esm/Wizard/index.native.js.map +1 -1
  136. package/dist/esm/actions/ActionBar.native.js.map +1 -1
  137. package/dist/esm/actions/DangerSection.native.js.map +1 -1
  138. package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
  139. package/dist/esm/actions/HeaderActions.native.js.map +1 -1
  140. package/dist/esm/actions/StandardAction.native.js.map +1 -1
  141. package/dist/esm/actions/devWarn.native.js.map +1 -1
  142. package/dist/esm/actions/index.native.js.map +1 -1
  143. package/dist/esm/actions/severity.native.js.map +1 -1
  144. package/dist/esm/appShell/AppShell.mjs +412 -0
  145. package/dist/esm/appShell/AppShell.mjs.map +1 -0
  146. package/dist/esm/appShell/AppShell.native.js +476 -0
  147. package/dist/esm/appShell/AppShell.native.js.map +1 -0
  148. package/dist/esm/appShell/Dock.native.js.map +1 -1
  149. package/dist/esm/appShell/MpoTabHost.native.js.map +1 -1
  150. package/dist/esm/appShell/ScopeMenu.mjs +43 -20
  151. package/dist/esm/appShell/ScopeMenu.mjs.map +1 -1
  152. package/dist/esm/appShell/ScopeMenu.native.js +47 -21
  153. package/dist/esm/appShell/ScopeMenu.native.js.map +1 -1
  154. package/dist/esm/appShell/SettingsSplit.native.js.map +1 -1
  155. package/dist/esm/appShell/Springboard.native.js.map +1 -1
  156. package/dist/esm/appShell/SystemBar.native.js.map +1 -1
  157. package/dist/esm/appShell/TabBar.mjs.map +1 -1
  158. package/dist/esm/appShell/TabBar.native.js.map +1 -1
  159. package/dist/esm/appShell/TabHost.native.js +41 -12
  160. package/dist/esm/appShell/TabHost.native.js.map +1 -1
  161. package/dist/esm/appShell/appTile.native.js.map +1 -1
  162. package/dist/esm/appShell/bottomChrome.native.js.map +1 -1
  163. package/dist/esm/appShell/index.mjs +1 -0
  164. package/dist/esm/appShell/index.mjs.map +1 -1
  165. package/dist/esm/appShell/index.native.js +1 -0
  166. package/dist/esm/appShell/index.native.js.map +1 -1
  167. package/dist/esm/charts/Axis.native.js.map +1 -1
  168. package/dist/esm/charts/BarChart.native.js.map +1 -1
  169. package/dist/esm/charts/ChartDataView.native.js.map +1 -1
  170. package/dist/esm/charts/ChartSurface.native.js.map +1 -1
  171. package/dist/esm/charts/LineChart.native.js.map +1 -1
  172. package/dist/esm/charts/PieChart.native.js.map +1 -1
  173. package/dist/esm/charts/Sparkline.native.js.map +1 -1
  174. package/dist/esm/charts/accessibility.native.js.map +1 -1
  175. package/dist/esm/charts/chartHeight.native.js.map +1 -1
  176. package/dist/esm/charts/composeChart.native.js.map +1 -1
  177. package/dist/esm/charts/index.native.js.map +1 -1
  178. package/dist/esm/charts/math.native.js.map +1 -1
  179. package/dist/esm/charts/series.native.js.map +1 -1
  180. package/dist/esm/charts/svg.native.js.map +1 -1
  181. package/dist/esm/charts/types.native.js.map +1 -1
  182. package/dist/esm/charts/useChartTheme.native.js.map +1 -1
  183. package/dist/esm/code/Code/index.native.js.map +1 -1
  184. package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
  185. package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
  186. package/dist/esm/code/Highlighter/houseSyntaxTheme.native.js.map +1 -1
  187. package/dist/esm/code/Highlighter/index.native.js.map +1 -1
  188. package/dist/esm/code/Highlighter/namedGroupMatchAll.native.js.map +1 -1
  189. package/dist/esm/code/Pre/index.native.js.map +1 -1
  190. package/dist/esm/code/index.native.js.map +1 -1
  191. package/dist/esm/componentColors.native.js.map +1 -1
  192. package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
  193. package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
  194. package/dist/esm/feedback/copy.native.js.map +1 -1
  195. package/dist/esm/feedback/index.native.js.map +1 -1
  196. package/dist/esm/feedback/notify.native.js.map +1 -1
  197. package/dist/esm/feedback/route.native.js.map +1 -1
  198. package/dist/esm/feedback/store.native.js.map +1 -1
  199. package/dist/esm/formLayouts/FormLayouts.native.js.map +1 -1
  200. package/dist/esm/formLayouts/PreferencePanel.native.js.map +1 -1
  201. package/dist/esm/formLayouts/index.native.js.map +1 -1
  202. package/dist/esm/hooks/index.native.js.map +1 -1
  203. package/dist/esm/hooks/useClipboard.native.js.map +1 -1
  204. package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
  205. package/dist/esm/hooks/useHoldCue.native.js.map +1 -1
  206. package/dist/esm/icons/CheckRegular.native.js.map +1 -1
  207. package/dist/esm/icons/MinusRegular.native.js.map +1 -1
  208. package/dist/esm/icons/index.native.js.map +1 -1
  209. package/dist/esm/icons/lucide.native.js.map +1 -1
  210. package/dist/esm/icons/themed.native.js.map +1 -1
  211. package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
  212. package/dist/esm/images/Image/framing.native.js.map +1 -1
  213. package/dist/esm/images/Image/mediaRadius.native.js.map +1 -1
  214. package/dist/esm/images/Image/shared.native.js.map +1 -1
  215. package/dist/esm/images/Image/types.native.js.map +1 -1
  216. package/dist/esm/images/index.native.js.map +1 -1
  217. package/dist/esm/index.native.js.map +1 -1
  218. package/dist/esm/knobImmuneExports.native.js.map +1 -1
  219. package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
  220. package/dist/esm/layouts/Debug/index.native.js.map +1 -1
  221. package/dist/esm/layouts/EmptyState.native.js.map +1 -1
  222. package/dist/esm/layouts/IdentityPlate.native.js.map +1 -1
  223. package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
  224. package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
  225. package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
  226. package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
  227. package/dist/esm/layouts/StatusDot.native.js.map +1 -1
  228. package/dist/esm/layouts/StatusGlyphStrip.native.js.map +1 -1
  229. package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
  230. package/dist/esm/layouts/attachments.native.js.map +1 -1
  231. package/dist/esm/layouts/bottomAccessory.native.js.map +1 -1
  232. package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
  233. package/dist/esm/layouts/index.native.js.map +1 -1
  234. package/dist/esm/layouts/page.mjs +4 -2
  235. package/dist/esm/layouts/page.mjs.map +1 -1
  236. package/dist/esm/layouts/page.native.js +4 -2
  237. package/dist/esm/layouts/page.native.js.map +1 -1
  238. package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
  239. package/dist/esm/layouts/screenScroll.mjs +53 -1
  240. package/dist/esm/layouts/screenScroll.mjs.map +1 -1
  241. package/dist/esm/layouts/screenScroll.native.js +57 -1
  242. package/dist/esm/layouts/screenScroll.native.js.map +1 -1
  243. package/dist/esm/layouts/shellSidebarSlot.native.js.map +1 -1
  244. package/dist/esm/layouts/sidebar.mjs +74 -19
  245. package/dist/esm/layouts/sidebar.mjs.map +1 -1
  246. package/dist/esm/layouts/sidebar.native.js +78 -19
  247. package/dist/esm/layouts/sidebar.native.js.map +1 -1
  248. package/dist/esm/layouts/tags.native.js.map +1 -1
  249. package/dist/esm/layouts/timeline.native.js.map +1 -1
  250. package/dist/esm/layouts/toolbar.native.js.map +1 -1
  251. package/dist/esm/shared/actionScan.native.js.map +1 -1
  252. package/dist/esm/shared/i18n.native.js.map +1 -1
  253. package/dist/esm/shared/restoreRetries.native.js.map +1 -1
  254. package/dist/esm/shared/t.native.js.map +1 -1
  255. package/dist/esm/shared/textRidesText.native.js.map +1 -1
  256. package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
  257. package/dist/esm/surfaces.native.js.map +1 -1
  258. package/dist/esm/tamagui.native.js.map +1 -1
  259. package/dist/esm/testSetup.native.js.map +1 -1
  260. package/dist/esm/utils/setTinted.native.js.map +1 -1
  261. package/dist/esm/utils/unwrapText.native.js.map +1 -1
  262. package/dist/esm/views/Calendar.native.js.map +1 -1
  263. package/dist/esm/views/Dashboard.native.js.map +1 -1
  264. package/dist/esm/views/Gantt.native.js.map +1 -1
  265. package/dist/esm/views/GanttDateGrip.native.js.map +1 -1
  266. package/dist/esm/views/ImageGrid.native.js.map +1 -1
  267. package/dist/esm/views/List.native.js.map +1 -1
  268. package/dist/esm/views/ListGroup.native.js.map +1 -1
  269. package/dist/esm/views/ListRow.native.js.map +1 -1
  270. package/dist/esm/views/Matrix.native.js.map +1 -1
  271. package/dist/esm/views/ReportBuilder.native.js.map +1 -1
  272. package/dist/esm/views/ShortcutCard.native.js.map +1 -1
  273. package/dist/esm/views/TreeView.native.js.map +1 -1
  274. package/dist/esm/views/calendarMath.native.js.map +1 -1
  275. package/dist/esm/views/ganttIntervals.native.js.map +1 -1
  276. package/dist/esm/views/ganttMath.native.js.map +1 -1
  277. package/dist/esm/views/index.native.js.map +1 -1
  278. package/dist/esm/views/kanbanDnd.native.js.map +1 -1
  279. package/dist/esm/views/matrixMath.native.js.map +1 -1
  280. package/dist/esm/views/sameNameScope.native.js.map +1 -1
  281. package/dist/esm/views/treeViewDnd.native.js.map +1 -1
  282. package/dist/esm/views/treeViewLayout.native.js.map +1 -1
  283. package/dist/esm/views/treeViewMotion.native.js.map +1 -1
  284. package/dist/esm/views/treeViewReveal.native.js.map +1 -1
  285. package/dist/esm/views/usePageFlowLoadMore.native.js.map +1 -1
  286. package/dist/esm/views/widgetChrome.native.js.map +1 -1
  287. package/dist/jsx/CheckGlyph/MenuCheckColumn.mjs +31 -0
  288. package/dist/jsx/CheckGlyph/MenuCheckColumn.mjs.map +1 -0
  289. package/dist/jsx/CheckGlyph/MenuCheckColumn.native.js +60 -0
  290. package/dist/jsx/CheckGlyph/MenuCheckColumn.native.js.map +1 -0
  291. package/dist/jsx/CheckGlyph/index.mjs +15 -0
  292. package/dist/jsx/CheckGlyph/index.mjs.map +1 -0
  293. package/dist/jsx/CheckGlyph/index.native.js +44 -0
  294. package/dist/jsx/CheckGlyph/index.native.js.map +1 -0
  295. package/dist/jsx/ContextMenu/index.mjs +6 -23
  296. package/dist/jsx/ContextMenu/index.mjs.map +1 -1
  297. package/dist/jsx/ContextMenu/index.native.js +7 -25
  298. package/dist/jsx/ContextMenu/index.native.js.map +1 -1
  299. package/dist/jsx/DropdownMenu/index.mjs +6 -18
  300. package/dist/jsx/DropdownMenu/index.mjs.map +1 -1
  301. package/dist/jsx/DropdownMenu/index.native.js +7 -19
  302. package/dist/jsx/DropdownMenu/index.native.js.map +1 -1
  303. package/dist/jsx/Popover.mjs +2 -2
  304. package/dist/jsx/Popover.mjs.map +1 -1
  305. package/dist/jsx/Popover.native.js +2 -2
  306. package/dist/jsx/Popover.native.js.map +1 -1
  307. package/dist/jsx/SchemeToggle/index.mjs +3 -3
  308. package/dist/jsx/SchemeToggle/index.mjs.map +1 -1
  309. package/dist/jsx/SchemeToggle/index.native.js +2 -2
  310. package/dist/jsx/SchemeToggle/index.native.js.map +1 -1
  311. package/dist/jsx/SheetModal/index.mjs +5 -2
  312. package/dist/jsx/SheetModal/index.mjs.map +1 -1
  313. package/dist/jsx/SheetModal/index.native.js +1 -1
  314. package/dist/jsx/SheetModal/index.native.js.map +1 -1
  315. package/dist/jsx/TreeSelect/index.mjs +4 -8
  316. package/dist/jsx/TreeSelect/index.mjs.map +1 -1
  317. package/dist/jsx/TreeSelect/index.native.js +2 -6
  318. package/dist/jsx/TreeSelect/index.native.js.map +1 -1
  319. package/dist/jsx/appShell/AppShell.mjs +412 -0
  320. package/dist/jsx/appShell/AppShell.mjs.map +1 -0
  321. package/dist/jsx/appShell/AppShell.native.js +505 -0
  322. package/dist/jsx/appShell/AppShell.native.js.map +1 -0
  323. package/dist/jsx/appShell/ScopeMenu.mjs +43 -20
  324. package/dist/jsx/appShell/ScopeMenu.mjs.map +1 -1
  325. package/dist/jsx/appShell/ScopeMenu.native.js +43 -17
  326. package/dist/jsx/appShell/ScopeMenu.native.js.map +1 -1
  327. package/dist/jsx/appShell/TabBar.mjs.map +1 -1
  328. package/dist/jsx/appShell/TabBar.native.js.map +1 -1
  329. package/dist/jsx/appShell/TabHost.native.js +41 -10
  330. package/dist/jsx/appShell/TabHost.native.js.map +1 -1
  331. package/dist/jsx/appShell/index.mjs +1 -0
  332. package/dist/jsx/appShell/index.mjs.map +1 -1
  333. package/dist/jsx/appShell/index.native.js +1 -0
  334. package/dist/jsx/appShell/index.native.js.map +1 -1
  335. package/dist/jsx/feedback/NotifyHost.native.js +1 -1
  336. package/dist/jsx/formLayouts/PreferencePanel.native.js +1 -1
  337. package/dist/jsx/index.native.js +1 -1
  338. package/dist/jsx/layouts/AsyncBoundary.native.js +1 -1
  339. package/dist/jsx/layouts/AsyncBoundary.native.js.map +1 -1
  340. package/dist/jsx/layouts/page.mjs +4 -2
  341. package/dist/jsx/layouts/page.mjs.map +1 -1
  342. package/dist/jsx/layouts/page.native.js +3 -0
  343. package/dist/jsx/layouts/page.native.js.map +1 -1
  344. package/dist/jsx/layouts/screenScroll.mjs +53 -1
  345. package/dist/jsx/layouts/screenScroll.mjs.map +1 -1
  346. package/dist/jsx/layouts/screenScroll.native.js +59 -0
  347. package/dist/jsx/layouts/screenScroll.native.js.map +1 -1
  348. package/dist/jsx/layouts/sidebar.mjs +74 -19
  349. package/dist/jsx/layouts/sidebar.mjs.map +1 -1
  350. package/dist/jsx/layouts/sidebar.native.js +78 -16
  351. package/dist/jsx/layouts/sidebar.native.js.map +1 -1
  352. package/package.json +9 -9
  353. package/src/CheckGlyph/CheckGlyph.spec.tsx +46 -0
  354. package/src/CheckGlyph/MenuCheckColumn.tsx +32 -0
  355. package/src/CheckGlyph/index.tsx +12 -0
  356. package/src/ContextMenu/ContextMenu.spec.tsx +46 -1
  357. package/src/ContextMenu/index.tsx +5 -22
  358. package/src/DropdownMenu/DropdownMenu.avatarTrigger.spec.tsx +6 -9
  359. package/src/DropdownMenu/index.tsx +5 -18
  360. package/src/Popover.coverTrigger.spec.tsx +3 -3
  361. package/src/Popover.tsx +5 -5
  362. package/src/SchemeToggle/SchemeToggle.spec.tsx +5 -11
  363. package/src/SchemeToggle/SchemeToggle.stories.tsx +21 -0
  364. package/src/SchemeToggle/index.tsx +2 -1
  365. package/src/SheetModal/index.native.tsx +2 -1
  366. package/src/SheetModal/index.tsx +5 -2
  367. package/src/TreeSelect/index.tsx +2 -4
  368. package/src/appShell/AppShell.spec.tsx +289 -0
  369. package/src/appShell/AppShell.stories.tsx +112 -0
  370. package/src/appShell/AppShell.tsx +535 -0
  371. package/src/appShell/ScopeMenu.spec.tsx +22 -0
  372. package/src/appShell/ScopeMenu.tsx +57 -18
  373. package/src/appShell/TabBar.tsx +13 -3
  374. package/src/appShell/TabHost.native.spec.tsx +53 -6
  375. package/src/appShell/TabHost.native.tsx +50 -14
  376. package/src/appShell/TabHost.stories.tsx +72 -0
  377. package/src/appShell/index.ts +1 -0
  378. package/src/layouts/PageHeader.stories.tsx +3 -3
  379. package/src/layouts/Sidebar.spec.tsx +92 -1
  380. package/src/layouts/page.tsx +8 -0
  381. package/src/layouts/pageActionButton.spec.tsx +7 -3
  382. package/src/layouts/screenScroll.tsx +71 -0
  383. package/src/layouts/sidebar.tsx +102 -10
  384. package/types/CheckGlyph/MenuCheckColumn.d.ts +11 -0
  385. package/types/CheckGlyph/MenuCheckColumn.d.ts.map +1 -0
  386. package/types/CheckGlyph/index.d.ts +10 -0
  387. package/types/CheckGlyph/index.d.ts.map +1 -0
  388. package/types/ContextMenu/index.d.ts.map +1 -1
  389. package/types/DropdownMenu/index.d.ts.map +1 -1
  390. package/types/Popover.d.ts +1 -1
  391. package/types/Popover.d.ts.map +1 -1
  392. package/types/SchemeToggle/index.d.ts.map +1 -1
  393. package/types/SheetModal/index.d.ts.map +1 -1
  394. package/types/SheetModal/index.native.d.ts.map +1 -1
  395. package/types/TreeSelect/index.d.ts.map +1 -1
  396. package/types/appShell/AppShell.d.ts +47 -0
  397. package/types/appShell/AppShell.d.ts.map +1 -0
  398. package/types/appShell/ScopeMenu.d.ts +0 -18
  399. package/types/appShell/ScopeMenu.d.ts.map +1 -1
  400. package/types/appShell/TabBar.d.ts +12 -6
  401. package/types/appShell/TabBar.d.ts.map +1 -1
  402. package/types/appShell/TabHost.native.d.ts +23 -5
  403. package/types/appShell/TabHost.native.d.ts.map +1 -1
  404. package/types/appShell/index.d.ts +1 -0
  405. package/types/appShell/index.d.ts.map +1 -1
  406. package/types/layouts/page.d.ts +5 -0
  407. package/types/layouts/page.d.ts.map +1 -1
  408. package/types/layouts/screenScroll.d.ts +24 -0
  409. package/types/layouts/screenScroll.d.ts.map +1 -1
  410. package/types/layouts/sidebar.d.ts +37 -1
  411. package/types/layouts/sidebar.d.ts.map +1 -1
@@ -0,0 +1,112 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
2
+ import {
3
+ GearIcon,
4
+ HouseIcon,
5
+ LightningIcon,
6
+ SparkleIcon,
7
+ SquaresFourIcon,
8
+ StackIcon,
9
+ } from "@phosphor-icons/react";
10
+ import { useState } from "react";
11
+ import { XStack, YStack } from "tamagui";
12
+ import { Button } from "@multiplatform.one/forms";
13
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
14
+ import { PageBarButton, PageHeader, PageSection, Screen } from "../layouts/page";
15
+ import { Pagination } from "../Pagination";
16
+ import { Text } from "../Text";
17
+ import { AppShell, type AppShellDestination } from "./AppShell";
18
+
19
+ const destinations: AppShellDestination[] = [
20
+ { key: "/", label: "Home", icon: HouseIcon },
21
+ { key: "/pokemon", label: "Pokemon", icon: SparkleIcon },
22
+ { key: "/moves", label: "Moves", icon: LightningIcon },
23
+ { key: "/backoffice", label: "Backoffice", icon: SquaresFourIcon },
24
+ { key: "/settings", label: "Settings", icon: GearIcon, placement: "more" },
25
+ ];
26
+
27
+ function Mark() {
28
+ const { knobProps } = useResolvedKnobs();
29
+ return (
30
+ <YStack
31
+ width="$2"
32
+ height="$2"
33
+ borderRadius="$3"
34
+ backgroundColor="$color4"
35
+ alignItems="center"
36
+ justifyContent="center"
37
+ >
38
+ <StackIcon size={knobProps.controlIcon.width} weight="bold" />
39
+ </YStack>
40
+ );
41
+ }
42
+
43
+ function DemoPage({ path, open }: { path: string; open: (path: string) => void }) {
44
+ const pushed = path.split("/").filter(Boolean).length > 1;
45
+ const name = destinations.find((item) => path.startsWith(item.key) && item.key !== "/");
46
+ const title = pushed ? "Pikachu" : (name?.label ?? "Home");
47
+ return (
48
+ <Screen>
49
+ <PageHeader title={title} subtitle={pushed ? "#025 · Electric" : "1,025 entries"} />
50
+ <PageSection title="Recent">
51
+ {Array.from({ length: 14 }, (_, index) => (
52
+ <XStack key={index} alignItems="center" minHeight="$4">
53
+ <Text flexGrow={1}>Row {index + 1}</Text>
54
+ {pushed ? null : (
55
+ <Button
56
+ size="$3"
57
+ chromeless
58
+ onPress={() => open(`${path === "/" ? "" : path}/pikachu`)}
59
+ >
60
+ Open
61
+ </Button>
62
+ )}
63
+ </XStack>
64
+ ))}
65
+ </PageSection>
66
+ </Screen>
67
+ );
68
+ }
69
+
70
+ function Demo({ initialPath, accessory }: { initialPath: string; accessory?: boolean }) {
71
+ const [path, setPath] = useState(initialPath);
72
+ return (
73
+ <AppShell
74
+ destinations={destinations}
75
+ path={path}
76
+ onNavigate={setPath}
77
+ onBack={setPath}
78
+ title="multiplatform.one"
79
+ logo={<Mark />}
80
+ actions={<PageBarButton aria-label="Theme" icon={GearIcon} edge="end" />}
81
+ accessory={accessory ? <Pagination total={41} defaultPage={1} variant="dots" /> : undefined}
82
+ ownChrome={["/backoffice"]}
83
+ >
84
+ <DemoPage path={path} open={setPath} />
85
+ </AppShell>
86
+ );
87
+ }
88
+
89
+ /**
90
+ * The composed app frame (LC-175). Size class follows the viewport: below
91
+ * `md` the phone frame (nav bar, content, bottom accessory, tab bar), above
92
+ * it the split frame (sidebar of destinations beside the content column).
93
+ */
94
+ const meta: Meta<typeof AppShell> = {
95
+ title: "Components/AppShell",
96
+ component: AppShell,
97
+ parameters: { layout: "fullscreen", status: { type: "beta" } },
98
+ };
99
+
100
+ export default meta;
101
+ type Story = StoryObj<typeof AppShell>;
102
+
103
+ /** A tab root: the mark leads, the large title sits in the page, tabs below. */
104
+ export const TabRoot: Story = { render: () => <Demo initialPath="/pokemon" /> };
105
+
106
+ /** A pushed route: tinted back to the destination, its tab still selected. */
107
+ export const Pushed: Story = { render: () => <Demo initialPath="/pokemon/pikachu" /> };
108
+
109
+ /** A page that hands the shell a bottom accessory: the tab bar floats it above itself. */
110
+ export const WithAccessory: Story = {
111
+ render: () => <Demo initialPath="/pokemon" accessory />,
112
+ };
@@ -0,0 +1,535 @@
1
+ /**
2
+ * AppShell — the one composed app frame every MPO app mounts at its root
3
+ * (LC-175 MPO-ONLY-REUSE). An app declares its destinations and hands over
4
+ * the current path; the shell draws the rest:
5
+ *
6
+ * - compact (phone): the nav bar on top (UINavigationBar: tinted back on a
7
+ * pushed route, the page's large title collapsing into it, trailing
8
+ * actions), the routed content, and the `tab` destinations in TabHost
9
+ * (FZ-8105): the system tab bar where the binary has one, the floating
10
+ * MPO TabBar (LC-150) elsewhere. The bar publishes the height it covers,
11
+ * which a Screen pads its content by. `more` destinations open from a
12
+ * pull-down in the bar.
13
+ * - regular (tablet, desktop): MPO's Sidebar on the translucent chrome
14
+ * material with the app's name, every destination and the app's actions
15
+ * at its foot (UISplitViewController's sidebar), the content centred at a
16
+ * readable width, and a page's panel stacked under the sidebar's rows
17
+ * (LC-101 ONE-SIDEBAR).
18
+ *
19
+ * A route listed in `ownChrome` mounts its own full chrome (a desk), so the
20
+ * shell draws no bar or sidebar around it. On a phone, one that is also a tab
21
+ * destination keeps the tab bar, the only way to the other tabs (FZ-8105). A page whose `Screen` pins its own top bar
22
+ * (LC-157) owns the nav bar on a phone, so the shell's bar steps aside there.
23
+ */
24
+ import { zIndex } from "@multiplatform.one/forms";
25
+ import { isWeb } from "@multiplatform.one/platform";
26
+ import {
27
+ MIN_PRESS_TARGET,
28
+ Surface,
29
+ hairline,
30
+ transitionProps,
31
+ useLayoutSizeClass,
32
+ useResolvedKnobs,
33
+ } from "@multiplatform.one/theme";
34
+ import { ListIcon } from "@phosphor-icons/react";
35
+ import { createElement, useEffect, useMemo, useState, type ReactNode } from "react";
36
+ import type { LayoutChangeEvent } from "react-native";
37
+ import { SizableText, XStack, YStack } from "tamagui";
38
+ import { componentColors } from "../componentColors";
39
+ import { DropdownMenu, type DropdownMenuEntry } from "../DropdownMenu";
40
+ import { PageBackButton, PageBarButton, READABLE_MAX_WIDTH } from "../layouts/page";
41
+ import { ShellScrollReportScope, useShellScrollReports } from "../layouts/screenScroll";
42
+ import {
43
+ ShellSidebarSlotProvider,
44
+ useShellSidebarSlotState,
45
+ type ShellSidebarPanel,
46
+ } from "../layouts/shellSidebarSlot";
47
+ import { BottomAccessory } from "../layouts/bottomAccessory";
48
+ import { Sidebar } from "../layouts/sidebar";
49
+ import { useTranslation } from "../shared/i18n";
50
+ import type { TabBarIcon, TabBarItem } from "./TabBar";
51
+ import { TabHost } from "./TabHost";
52
+ import { useBottomChromeInset, useFloatingBarMounted } from "./bottomChrome";
53
+
54
+ export interface AppShellDestination {
55
+ /** The destination's route; a path under it keeps it selected. */
56
+ key: string;
57
+ /** One short word: it is the tab caption and the back label. */
58
+ label: string;
59
+ icon: TabBarIcon;
60
+ /** The SF Symbol the iOS system tab bar draws for this destination. */
61
+ nativeIcon?: TabBarItem["nativeIcon"];
62
+ /**
63
+ * `tab` (default) sits in the phone's tab bar; `more` opens from the bar's
64
+ * pull-down. Every destination sits in the regular-width sidebar.
65
+ */
66
+ placement?: "tab" | "more";
67
+ /** Runs instead of `onNavigate(key)`, e.g. an external link. */
68
+ onPress?: () => void;
69
+ }
70
+
71
+ export interface AppShellProps {
72
+ destinations: readonly AppShellDestination[];
73
+ /** The current route path. */
74
+ path: string;
75
+ onNavigate: (key: string) => void;
76
+ /** The app's name: the sidebar's title and the home control's label. */
77
+ title: string;
78
+ /** The app's mark, leading the bar on a phone and the name in the sidebar. */
79
+ logo?: ReactNode;
80
+ /** Pressing the name or mark. Default: the first destination. */
81
+ onTitlePress?: () => void;
82
+ /** Trailing bar actions (theme, account). */
83
+ actions?: ReactNode;
84
+ /** Shows a tinted back on a route pushed below its destination (its key). */
85
+ onBack?: (destinationKey: string) => void;
86
+ /** The phone's bottom accessory (LC-174), handed to the tab bar or pinned to the bottom edge. */
87
+ accessory?: ReactNode;
88
+ /** Route prefixes that mount their own chrome; the shell draws none there. */
89
+ ownChrome?: readonly string[];
90
+ children?: ReactNode;
91
+ testID?: string;
92
+ }
93
+
94
+ function underKey(path: string, key: string): boolean {
95
+ if (key === "/") return path === "/";
96
+ return path === key || path.startsWith(`${key}/`);
97
+ }
98
+
99
+ /** The destination that owns `path` (the longest key it sits under). */
100
+ export function appShellRoute(
101
+ destinations: readonly AppShellDestination[],
102
+ path: string,
103
+ ): { destination: AppShellDestination | undefined; pushed: boolean } {
104
+ const normal = path.length > 1 ? path.replace(/\/+$/, "") : path;
105
+ let destination: AppShellDestination | undefined;
106
+ for (const candidate of destinations) {
107
+ if (candidate.onPress || !underKey(normal, candidate.key)) continue;
108
+ if (!destination || candidate.key.length > destination.key.length) destination = candidate;
109
+ }
110
+ return { destination, pushed: Boolean(destination && normal !== destination.key) };
111
+ }
112
+
113
+ /** Web: the document scrolls under the sticky bar; native reports via Screen. */
114
+ function useDocumentScrolled(enabled: boolean): boolean {
115
+ const [scrolled, setScrolled] = useState(false);
116
+ useEffect(() => {
117
+ if (!enabled || typeof document === "undefined") return;
118
+ const sync = () => setScrolled(window.scrollY > 1);
119
+ sync();
120
+ window.addEventListener("scroll", sync, { passive: true });
121
+ return () => window.removeEventListener("scroll", sync);
122
+ }, [enabled]);
123
+ return scrolled;
124
+ }
125
+
126
+ const sticky = (edge: "top" | "bottom") =>
127
+ isWeb ? { position: "sticky" as const, [edge]: 0, zIndex: zIndex.stickyHeader } : null;
128
+
129
+ function HomeControl({
130
+ title,
131
+ onPress,
132
+ children,
133
+ }: {
134
+ title: string;
135
+ onPress: () => void;
136
+ children: ReactNode;
137
+ }) {
138
+ return (
139
+ <XStack
140
+ role="link"
141
+ aria-label={title}
142
+ alignItems="center"
143
+ minHeight={MIN_PRESS_TARGET}
144
+ flexShrink={1}
145
+ minWidth={0}
146
+ gap="$2"
147
+ cursor="pointer"
148
+ onPress={onPress}
149
+ data-testid="app-shell-home"
150
+ >
151
+ {children}
152
+ </XStack>
153
+ );
154
+ }
155
+
156
+ interface NavBarProps {
157
+ /** Centres the bar's row in the content column's readable width. */
158
+ maxWidth?: number;
159
+ leading: ReactNode;
160
+ trailing: ReactNode;
161
+ title: string | null;
162
+ showTitle: boolean;
163
+ scrolled: boolean;
164
+ }
165
+
166
+ /**
167
+ * The shell's nav bar: one 44pt row on the page fill. Its hairline appears
168
+ * once content scrolls under it (scroll-edge appearance) and the inline
169
+ * title fades in once the page's large title has scrolled away, centred
170
+ * clear of the wider side group, both on the control motion role (LC-68).
171
+ */
172
+ function AppShellNavBar({ maxWidth, leading, trailing, title, showTitle, scrolled }: NavBarProps) {
173
+ const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
174
+ const [sides, setSides] = useState({ leading: 0, trailing: 0 });
175
+ const measure = (side: "leading" | "trailing") => (event: LayoutChangeEvent) => {
176
+ const width = Math.ceil(event.nativeEvent.layout.width);
177
+ setSides((prev) => (prev[side] === width ? prev : { ...prev, [side]: width }));
178
+ };
179
+ const motion = transitionProps(knobProps.motion.control);
180
+ const clearance = Math.max(sides.leading, sides.trailing);
181
+ return (
182
+ <Surface bar>
183
+ <YStack
184
+ role="banner"
185
+ data-testid="app-shell-bar"
186
+ data-scroll-edge={scrolled ? "" : undefined}
187
+ backgroundColor="$background"
188
+ flexShrink={0}
189
+ {...hairline.bottom}
190
+ borderColor={(scrolled ? componentColors.divider : "transparent") as never}
191
+ {...motion}
192
+ {...sticky("top")}
193
+ >
194
+ <XStack
195
+ position="relative"
196
+ width="100%"
197
+ {...(maxWidth ? { maxWidth, alignSelf: "center" as const } : null)}
198
+ minHeight={MIN_PRESS_TARGET}
199
+ alignItems="center"
200
+ flexWrap="nowrap"
201
+ paddingHorizontal={knobProps.panelPadding.padding as "$4"}
202
+ {...knobProps.gap}
203
+ >
204
+ <XStack
205
+ alignItems="center"
206
+ flexShrink={1000}
207
+ minWidth={0}
208
+ {...knobProps.gap}
209
+ onLayout={measure("leading")}
210
+ >
211
+ {leading}
212
+ </XStack>
213
+ <XStack flexGrow={1} />
214
+ <XStack
215
+ alignItems="center"
216
+ flexShrink={1}
217
+ minWidth={0}
218
+ justifyContent="flex-end"
219
+ {...knobProps.gap}
220
+ onLayout={measure("trailing")}
221
+ >
222
+ {trailing}
223
+ </XStack>
224
+ {title ? (
225
+ <XStack
226
+ position="absolute"
227
+ top={0}
228
+ bottom={0}
229
+ left={clearance}
230
+ right={clearance}
231
+ alignItems="center"
232
+ justifyContent="center"
233
+ pointerEvents="none"
234
+ aria-hidden={!showTitle}
235
+ data-testid="app-shell-bar-title"
236
+ opacity={showTitle ? 1 : 0}
237
+ {...motion}
238
+ >
239
+ <SizableText {...knobProps.body} fontWeight="600" color="$color12" numberOfLines={1}>
240
+ {title}
241
+ </SizableText>
242
+ </XStack>
243
+ ) : null}
244
+ </XStack>
245
+ </YStack>
246
+ </Surface>
247
+ );
248
+ }
249
+
250
+ function StackedPanel({ panel }: { panel: ShellSidebarPanel }) {
251
+ const { knobProps } = useResolvedKnobs();
252
+ return (
253
+ <YStack
254
+ paddingHorizontal={knobProps.panelPadding.padding as "$4"}
255
+ {...knobProps.gap}
256
+ data-testid="app-shell-page-panel"
257
+ >
258
+ <SizableText
259
+ {...knobProps.body}
260
+ size="$2"
261
+ textTransform="uppercase"
262
+ color={knobProps.textAccentColor}
263
+ numberOfLines={1}
264
+ >
265
+ {panel.title}
266
+ </SizableText>
267
+ {panel.content}
268
+ </YStack>
269
+ );
270
+ }
271
+
272
+ export function AppShell({
273
+ destinations,
274
+ path,
275
+ onNavigate,
276
+ title,
277
+ logo,
278
+ onTitlePress,
279
+ actions,
280
+ onBack,
281
+ accessory,
282
+ ownChrome = [],
283
+ children,
284
+ testID = "app-shell",
285
+ }: AppShellProps) {
286
+ const { t } = useTranslation();
287
+ const { knobProps } = useResolvedKnobs();
288
+ const compact = useLayoutSizeClass() === "compact";
289
+ const { report, reporter } = useShellScrollReports();
290
+ const sidebarSlot = useShellSidebarSlotState();
291
+ const documentScrolled = useDocumentScrolled(isWeb);
292
+ const chromeInset = useBottomChromeInset();
293
+ const floatingBar = useFloatingBarMounted();
294
+
295
+ const { destination, pushed } = appShellRoute(destinations, path);
296
+ const ownsChrome = ownChrome.some((prefix) => underKey(path, prefix));
297
+ const go = (item: AppShellDestination) => (item.onPress ? item.onPress() : onNavigate(item.key));
298
+ const goHome = onTitlePress ?? (() => destinations[0] && go(destinations[0]));
299
+
300
+ const tabs = useMemo(
301
+ () => destinations.filter((item) => (item.placement ?? "tab") === "tab"),
302
+ [destinations],
303
+ );
304
+ const more = useMemo(
305
+ () => destinations.filter((item) => item.placement === "more"),
306
+ [destinations],
307
+ );
308
+
309
+ const showTabs = compact && Boolean(destination && tabs.includes(destination));
310
+ const tabItems: TabBarItem[] = tabs.map(({ key, label, icon, nativeIcon }) => ({
311
+ key,
312
+ label,
313
+ icon,
314
+ nativeIcon,
315
+ }));
316
+ const onTabPress = (key: string) => {
317
+ const item = tabs.find((tab) => tab.key === key);
318
+ if (item) go(item);
319
+ };
320
+
321
+ if (ownsChrome) {
322
+ return (
323
+ <YStack
324
+ flex={1}
325
+ minHeight={0}
326
+ backgroundColor="$background"
327
+ {...(isWeb ? { height: "100dvh" as never } : null)}
328
+ data-testid={testID}
329
+ data-chrome="own"
330
+ >
331
+ {showTabs ? (
332
+ <TabHost items={tabItems} activeKey={destination?.key} onTabPress={onTabPress}>
333
+ {children}
334
+ </TabHost>
335
+ ) : (
336
+ children
337
+ )}
338
+ </YStack>
339
+ );
340
+ }
341
+
342
+ const pageBar = Boolean(report?.ownTopBar);
343
+ const scrolled = isWeb ? documentScrolled : Boolean(report?.scrolled);
344
+ const largeTitle = report?.title ?? null;
345
+ const barTitle = largeTitle ?? destination?.label ?? null;
346
+ const showTitle = largeTitle ? Boolean(report?.collapsed) : Boolean(barTitle);
347
+ const back =
348
+ pushed && onBack && destination && !pageBar ? (
349
+ <PageBackButton label={destination.label} onPress={() => onBack(destination.key)} />
350
+ ) : null;
351
+ const showBar = compact ? !pageBar : Boolean(back);
352
+
353
+ const moreMenu =
354
+ compact && more.length > 0 ? (
355
+ <DropdownMenu
356
+ placement="bottom-end"
357
+ items={more.map<DropdownMenuEntry>((item) => ({
358
+ key: item.key,
359
+ label: item.label,
360
+ icon: createElement(item.icon),
361
+ onSelect: () => go(item),
362
+ }))}
363
+ >
364
+ <PageBarButton
365
+ edge="end"
366
+ icon={ListIcon}
367
+ aria-label={t("More")}
368
+ data-testid="app-shell-more"
369
+ />
370
+ </DropdownMenu>
371
+ ) : null;
372
+
373
+ const bar = showBar ? (
374
+ <AppShellNavBar
375
+ leading={
376
+ back ??
377
+ (compact && logo ? (
378
+ <HomeControl title={title} onPress={goHome}>
379
+ {logo}
380
+ </HomeControl>
381
+ ) : null)
382
+ }
383
+ trailing={
384
+ compact ? (
385
+ <>
386
+ {actions}
387
+ {moreMenu}
388
+ </>
389
+ ) : null
390
+ }
391
+ maxWidth={compact ? undefined : READABLE_MAX_WIDTH}
392
+ title={barTitle}
393
+ showTitle={showTitle}
394
+ scrolled={scrolled}
395
+ />
396
+ ) : null;
397
+
398
+ const content = (
399
+ <YStack
400
+ role="main"
401
+ flex={1}
402
+ minWidth={0}
403
+ {...(isWeb ? null : { minHeight: 0 })}
404
+ data-testid="app-shell-content"
405
+ >
406
+ {compact ? (
407
+ children
408
+ ) : (
409
+ <YStack
410
+ flex={1}
411
+ width="100%"
412
+ maxWidth={READABLE_MAX_WIDTH}
413
+ alignSelf="center"
414
+ {...(isWeb ? null : { minHeight: 0 })}
415
+ data-testid="app-shell-readable"
416
+ >
417
+ {children}
418
+ </YStack>
419
+ )}
420
+ </YStack>
421
+ );
422
+
423
+ // The floating TabBar takes the accessory into its own capsule. Otherwise
424
+ // it renders in place, and on native it clears the system tab bar, which
425
+ // the content runs under.
426
+ const pageAccessory = accessory ? (
427
+ <YStack flexShrink={0} {...(isWeb || floatingBar ? null : { paddingBottom: chromeInset })}>
428
+ <BottomAccessory>{accessory}</BottomAccessory>
429
+ </YStack>
430
+ ) : null;
431
+
432
+ const frame = compact ? (
433
+ <YStack
434
+ flex={1}
435
+ backgroundColor="$background"
436
+ {...(isWeb ? { minHeight: "100dvh" as never } : null)}
437
+ data-testid={testID}
438
+ data-size="compact"
439
+ >
440
+ {bar}
441
+ {tabItems.length > 0 ? (
442
+ <TabHost
443
+ items={tabItems}
444
+ activeKey={showTabs ? destination?.key : undefined}
445
+ onTabPress={onTabPress}
446
+ >
447
+ {content}
448
+ {pageAccessory}
449
+ </TabHost>
450
+ ) : (
451
+ <>
452
+ {content}
453
+ {pageAccessory}
454
+ </>
455
+ )}
456
+ </YStack>
457
+ ) : (
458
+ <XStack
459
+ flex={1}
460
+ backgroundColor="$background"
461
+ {...(isWeb ? { minHeight: "100dvh" as never } : null)}
462
+ data-testid={testID}
463
+ data-size="regular"
464
+ >
465
+ <YStack
466
+ flexShrink={0}
467
+ alignSelf="stretch"
468
+ {...(isWeb ? { position: "sticky" as const, top: 0, height: "100dvh" as never } : null)}
469
+ data-testid="app-shell-sidebar"
470
+ >
471
+ <Sidebar
472
+ material
473
+ aria-label={title}
474
+ header={
475
+ <XStack
476
+ paddingHorizontal={knobProps.panelPadding.padding as "$4"}
477
+ alignItems="center"
478
+ minHeight={MIN_PRESS_TARGET}
479
+ >
480
+ <HomeControl title={title} onPress={goHome}>
481
+ {logo}
482
+ <SizableText
483
+ {...knobProps.heading}
484
+ fontWeight="700"
485
+ color="$color12"
486
+ numberOfLines={1}
487
+ >
488
+ {title}
489
+ </SizableText>
490
+ </HomeControl>
491
+ </XStack>
492
+ }
493
+ footer={
494
+ actions ? (
495
+ <XStack
496
+ paddingHorizontal={knobProps.panelPadding.padding as "$4"}
497
+ alignItems="center"
498
+ minHeight={MIN_PRESS_TARGET}
499
+ flexWrap="wrap"
500
+ {...knobProps.gap}
501
+ data-testid="app-shell-sidebar-actions"
502
+ >
503
+ {actions}
504
+ </XStack>
505
+ ) : undefined
506
+ }
507
+ sections={[
508
+ {
509
+ items: destinations.map((item) => ({
510
+ label: item.label,
511
+ value: item.key,
512
+ icon: createElement(item.icon),
513
+ active: item === destination,
514
+ onPress: () => go(item),
515
+ })),
516
+ },
517
+ ]}
518
+ >
519
+ {sidebarSlot.panel ? <StackedPanel panel={sidebarSlot.panel} /> : null}
520
+ </Sidebar>
521
+ </YStack>
522
+ <YStack flex={1} minWidth={0}>
523
+ {bar}
524
+ {content}
525
+ {pageAccessory}
526
+ </YStack>
527
+ </XStack>
528
+ );
529
+
530
+ return (
531
+ <ShellSidebarSlotProvider value={compact ? null : sidebarSlot.value}>
532
+ <ShellScrollReportScope reporter={reporter}>{frame}</ShellScrollReportScope>
533
+ </ShellSidebarSlotProvider>
534
+ );
535
+ }
@@ -135,3 +135,25 @@ describe("ScopeMenu (LC-105 COVER-TRIGGER)", () => {
135
135
  expect(screen.getByLabelText("Environment: Production")).toBeTruthy();
136
136
  });
137
137
  });
138
+
139
+ describe("ScopeMenu as a phone sheet (FZ-8557)", () => {
140
+ it("titles the sheet with the picker's label instead of echoing the trigger", async () => {
141
+ const width = window.innerWidth;
142
+ Object.defineProperty(window, "innerWidth", { configurable: true, writable: true, value: 390 });
143
+ try {
144
+ renderWithProviders(<Tenant />);
145
+ fireEvent.click(screen.getByTestId("tenant-trigger"));
146
+ const header = await screen.findByTestId("tenant-sheet-header");
147
+ expect(screen.getByRole("heading", { name: "Tenant" })).toBeTruthy();
148
+ expect(header.closest('[data-testid="floating-panel-sheet"]')).not.toBeNull();
149
+ expect(screen.queryByTestId("tenant-first-row")).toBeNull();
150
+ expect(screen.getAllByRole("menuitemradio")).toHaveLength(2);
151
+ } finally {
152
+ Object.defineProperty(window, "innerWidth", {
153
+ configurable: true,
154
+ writable: true,
155
+ value: width,
156
+ });
157
+ }
158
+ });
159
+ });