@multiplatform.one/components 7.83.2 → 7.84.1
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.
- package/dist/cjs/Accordion/index.native.js +1 -1
- package/dist/cjs/Accordion/index.native.js.map +1 -1
- package/dist/cjs/Alert/index.native.js.map +1 -1
- package/dist/cjs/AnimateHeight/index.native.js.map +1 -1
- package/dist/cjs/Avatar/getInitials.native.js.map +1 -1
- package/dist/cjs/Avatar/index.native.js.map +1 -1
- package/dist/cjs/Badge/index.native.js.map +1 -1
- package/dist/cjs/Button.native.js.map +1 -1
- package/dist/cjs/Card.native.js.map +1 -1
- package/dist/cjs/CardFooter.native.js.map +1 -1
- package/dist/cjs/CardHeader.native.js.map +1 -1
- package/dist/cjs/Carousel/index.cjs +11 -9
- package/dist/cjs/Carousel/index.native.js +15 -13
- package/dist/cjs/Carousel/index.native.js.map +1 -1
- package/dist/cjs/CheckGlyph/MenuCheckColumn.native.js.map +1 -1
- package/dist/cjs/CheckGlyph/index.native.js.map +1 -1
- package/dist/cjs/Chip/index.native.js.map +1 -1
- package/dist/cjs/Collapsible.native.js.map +1 -1
- package/dist/cjs/ContextMenu/index.native.js.map +1 -1
- package/dist/cjs/CopyField/CopyMenuButton.cjs +1 -3
- package/dist/cjs/CopyField/CopyMenuButton.native.js +2 -4
- package/dist/cjs/CopyField/CopyMenuButton.native.js.map +1 -1
- package/dist/cjs/CopyField/index.native.js.map +1 -1
- package/dist/cjs/Dialog.native.js.map +1 -1
- package/dist/cjs/Diff/diff.native.js.map +1 -1
- package/dist/cjs/Diff/index.native.js.map +1 -1
- package/dist/cjs/DotIndicator/index.native.js.map +1 -1
- package/dist/cjs/DropdownMenu/index.native.js.map +1 -1
- package/dist/cjs/DropdownMenu/menuSettle.native.js.map +1 -1
- package/dist/cjs/ElapsedProgress/index.native.js.map +1 -1
- package/dist/cjs/ErrorBoundary/index.native.js.map +1 -1
- package/dist/cjs/Heading.native.js.map +1 -1
- package/dist/cjs/Kbd/index.native.js.map +1 -1
- package/dist/cjs/Kbd/keys.native.js.map +1 -1
- package/dist/cjs/Lightbox/MediaControl.native.js.map +1 -1
- package/dist/cjs/Lightbox/index.gnome.native.js.map +1 -1
- package/dist/cjs/Lightbox/index.native.js +1 -1
- package/dist/cjs/Lightbox/shared.native.js.map +1 -1
- package/dist/cjs/Lightbox/useLightbox.native.js.map +1 -1
- package/dist/cjs/MachineLiteral/index.native.js.map +1 -1
- package/dist/cjs/Material/index.native.js.map +1 -1
- package/dist/cjs/Meter/index.native.js +1 -1
- package/dist/cjs/Meter/index.native.js.map +1 -1
- package/dist/cjs/Meter/meter.cjs +1 -1
- package/dist/cjs/Meter/meter.native.js +1 -1
- package/dist/cjs/Meter/meter.native.js.map +1 -1
- package/dist/cjs/OverflowRow/index.native.js +1 -1
- package/dist/cjs/OverflowRow/index.native.js.map +1 -1
- package/dist/cjs/Pagination.native.js +1 -1
- package/dist/cjs/Pagination.native.js.map +1 -1
- package/dist/cjs/Popover.native.js +1 -1
- package/dist/cjs/Popover.native.js.map +1 -1
- package/dist/cjs/ProgressSteps/index.native.js.map +1 -1
- package/dist/cjs/Quote/index.native.js.map +1 -1
- package/dist/cjs/SchemeToggle/index.native.js +1 -1
- package/dist/cjs/SchemeToggle/index.native.js.map +1 -1
- package/dist/cjs/ScrollView/revealFocusedField.native.js.map +1 -1
- package/dist/cjs/Separator/index.native.js.map +1 -1
- package/dist/cjs/Sheet.native.js.map +1 -1
- package/dist/cjs/SheetModal/SheetHeader.native.js.map +1 -1
- package/dist/cjs/SheetModal/SheetNavigation.native.js.map +1 -1
- package/dist/cjs/SheetModal/detents.native.js.map +1 -1
- package/dist/cjs/SheetModal/index.cjs +33 -5
- package/dist/cjs/SheetModal/useSheetFrameFocus.native.js.map +1 -1
- package/dist/cjs/Skeleton/index.native.js.map +1 -1
- package/dist/cjs/SkipLink/index.native.js.map +1 -1
- package/dist/cjs/SortableList/SortableHandle.native.js.map +1 -1
- package/dist/cjs/SortableList/index.native.js.map +1 -1
- package/dist/cjs/SortableList/useSortableRows.native.js.map +1 -1
- package/dist/cjs/SortableList/useUserOrder.native.js.map +1 -1
- package/dist/cjs/Tabs/index.native.js +1 -1
- package/dist/cjs/Tabs/index.native.js.map +1 -1
- package/dist/cjs/Text.native.js.map +1 -1
- package/dist/cjs/ThemePresetPicker/index.native.js +2 -2
- package/dist/cjs/ThemePresetPicker/index.native.js.map +1 -1
- package/dist/cjs/Toast/index.native.js.map +1 -1
- package/dist/cjs/Toast/store.native.js.map +1 -1
- package/dist/cjs/Tooltip/nativePress.native.js.map +1 -1
- package/dist/cjs/Tooltip/tooltipShared.native.js.map +1 -1
- package/dist/cjs/TreeSelect/index.native.js +2 -2
- package/dist/cjs/TreeSelect/index.native.js.map +1 -1
- package/dist/cjs/TreeSelect/treeSelectModel.native.js.map +1 -1
- package/dist/cjs/Video/PlayerChrome.native.js.map +1 -1
- package/dist/cjs/Video/VideoFrame.native.js.map +1 -1
- package/dist/cjs/Video/types.native.js.map +1 -1
- package/dist/cjs/Wheel/index.native.js.map +1 -1
- package/dist/cjs/Wizard/index.native.js +2 -2
- package/dist/cjs/Wizard/index.native.js.map +1 -1
- package/dist/cjs/actions/ActionBar.native.js.map +1 -1
- package/dist/cjs/actions/DangerSection.native.js.map +1 -1
- package/dist/cjs/actions/DestructiveAction.native.js.map +1 -1
- package/dist/cjs/actions/HeaderActions.native.js +2 -2
- package/dist/cjs/actions/HeaderActions.native.js.map +1 -1
- package/dist/cjs/actions/StandardAction.native.js +1 -1
- package/dist/cjs/actions/StandardAction.native.js.map +1 -1
- package/dist/cjs/actions/devWarn.native.js.map +1 -1
- package/dist/cjs/actions/index.native.js.map +1 -1
- package/dist/cjs/actions/severity.native.js.map +1 -1
- package/dist/cjs/appShell/AppShell.native.js +1 -1
- package/dist/cjs/appShell/AppShell.native.js.map +1 -1
- package/dist/cjs/appShell/BottomChromeClearance.native.js.map +1 -1
- package/dist/cjs/appShell/Dock.native.js.map +1 -1
- package/dist/cjs/appShell/MpoTabHost.native.js.map +1 -1
- package/dist/cjs/appShell/ScopeMenu.native.js +1 -1
- package/dist/cjs/appShell/ScopeMenu.native.js.map +1 -1
- package/dist/cjs/appShell/SettingsSplit.native.js +1 -1
- package/dist/cjs/appShell/SettingsSplit.native.js.map +1 -1
- package/dist/cjs/appShell/Springboard.native.js +1 -1
- package/dist/cjs/appShell/Springboard.native.js.map +1 -1
- package/dist/cjs/appShell/SystemBar.native.js.map +1 -1
- package/dist/cjs/appShell/TabBar.cjs +1 -1
- package/dist/cjs/appShell/TabBar.native.js +2 -2
- package/dist/cjs/appShell/TabBar.native.js.map +1 -1
- package/dist/cjs/appShell/TabHost.native.js +52 -5
- package/dist/cjs/appShell/TabHost.native.js.map +1 -1
- package/dist/cjs/appShell/appTile.native.js +1 -1
- package/dist/cjs/appShell/appTile.native.js.map +1 -1
- package/dist/cjs/appShell/bottomChrome.native.js.map +1 -1
- package/dist/cjs/appShell/index.native.js.map +1 -1
- package/dist/cjs/charts/Axis.native.js.map +1 -1
- package/dist/cjs/charts/BarChart.native.js.map +1 -1
- package/dist/cjs/charts/ChartDataView.native.js +2 -2
- package/dist/cjs/charts/ChartDataView.native.js.map +1 -1
- package/dist/cjs/charts/ChartSurface.native.js.map +1 -1
- package/dist/cjs/charts/LineChart.native.js.map +1 -1
- package/dist/cjs/charts/PieChart.native.js.map +1 -1
- package/dist/cjs/charts/Sparkline.native.js.map +1 -1
- package/dist/cjs/charts/accessibility.native.js.map +1 -1
- package/dist/cjs/charts/chartHeight.native.js.map +1 -1
- package/dist/cjs/charts/composeChart.native.js.map +1 -1
- package/dist/cjs/charts/index.native.js.map +1 -1
- package/dist/cjs/charts/math.native.js.map +1 -1
- package/dist/cjs/charts/series.native.js.map +1 -1
- package/dist/cjs/charts/svg.native.js.map +1 -1
- package/dist/cjs/charts/types.native.js.map +1 -1
- package/dist/cjs/charts/useChartTheme.native.js.map +1 -1
- package/dist/cjs/code/Code/index.native.js.map +1 -1
- package/dist/cjs/code/CodeBlock/index.native.js +7 -7
- package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
- package/dist/cjs/code/HighlightedCode/index.native.js.map +1 -1
- package/dist/cjs/code/Highlighter/houseSyntaxTheme.native.js.map +1 -1
- package/dist/cjs/code/Highlighter/index.native.js.map +1 -1
- package/dist/cjs/code/Highlighter/namedGroupMatchAll.native.js.map +1 -1
- package/dist/cjs/code/LogView.native.js.map +1 -1
- package/dist/cjs/code/Pre/index.native.js.map +1 -1
- package/dist/cjs/code/index.native.js +5 -5
- package/dist/cjs/code/index.native.js.map +1 -1
- package/dist/cjs/componentColors.native.js.map +1 -1
- package/dist/cjs/feedback/ConfirmDialog.native.js.map +1 -1
- package/dist/cjs/feedback/NotifyHost.native.js +2 -2
- package/dist/cjs/feedback/NotifyHost.native.js.map +1 -1
- package/dist/cjs/feedback/copy.native.js.map +1 -1
- package/dist/cjs/feedback/index.native.js.map +1 -1
- package/dist/cjs/feedback/notify.native.js.map +1 -1
- package/dist/cjs/feedback/route.native.js.map +1 -1
- package/dist/cjs/feedback/store.native.js.map +1 -1
- package/dist/cjs/formLayouts/FormLayouts.native.js +1 -1
- package/dist/cjs/formLayouts/FormLayouts.native.js.map +1 -1
- package/dist/cjs/formLayouts/PreferencePanel.native.js +2 -2
- package/dist/cjs/formLayouts/PreferencePanel.native.js.map +1 -1
- package/dist/cjs/formLayouts/index.native.js.map +1 -1
- package/dist/cjs/hooks/index.native.js +1 -1
- package/dist/cjs/hooks/index.native.js.map +1 -1
- package/dist/cjs/hooks/useClipboard.native.js.map +1 -1
- package/dist/cjs/hooks/useDocumentTitle.native.js.map +1 -1
- package/dist/cjs/hooks/useHoldCue.native.js.map +1 -1
- package/dist/cjs/icons/CheckRegular.native.js.map +1 -1
- package/dist/cjs/icons/MinusRegular.native.js.map +1 -1
- package/dist/cjs/icons/index.native.js.map +1 -1
- package/dist/cjs/icons/lucide.native.js.map +1 -1
- package/dist/cjs/icons/themed.native.js.map +1 -1
- package/dist/cjs/images/Image/ImageFrame.native.js +1 -1
- package/dist/cjs/images/Image/ImageFrame.native.js.map +1 -1
- package/dist/cjs/images/Image/framing.native.js.map +1 -1
- package/dist/cjs/images/Image/index.native.js +1 -1
- package/dist/cjs/images/Image/mediaRadius.native.js.map +1 -1
- package/dist/cjs/images/Image/shared.native.js +1 -1
- package/dist/cjs/images/Image/shared.native.js.map +1 -1
- package/dist/cjs/images/Image/types.native.js.map +1 -1
- package/dist/cjs/images/index.native.js.map +1 -1
- package/dist/cjs/index.native.js +39 -39
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/knobImmuneExports.native.js.map +1 -1
- package/dist/cjs/layouts/AsyncBoundary.native.js +2 -2
- package/dist/cjs/layouts/AsyncBoundary.native.js.map +1 -1
- package/dist/cjs/layouts/Debug/index.native.js.map +1 -1
- package/dist/cjs/layouts/EmptyState.native.js +1 -1
- package/dist/cjs/layouts/EmptyState.native.js.map +1 -1
- package/dist/cjs/layouts/IdentityPlate.native.js.map +1 -1
- package/dist/cjs/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
- package/dist/cjs/layouts/PageTemplates.cjs +15 -12
- package/dist/cjs/layouts/PageTemplates.native.js +16 -13
- package/dist/cjs/layouts/PageTemplates.native.js.map +1 -1
- package/dist/cjs/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/cjs/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
- package/dist/cjs/layouts/StatusDot.native.js.map +1 -1
- package/dist/cjs/layouts/StatusGlyphStrip.native.js +1 -1
- package/dist/cjs/layouts/StatusGlyphStrip.native.js.map +1 -1
- package/dist/cjs/layouts/ViewSwitcher.cjs +12 -2
- package/dist/cjs/layouts/ViewSwitcher.native.js +15 -2
- package/dist/cjs/layouts/ViewSwitcher.native.js.map +1 -1
- package/dist/cjs/layouts/attachments.native.js +1 -1
- package/dist/cjs/layouts/attachments.native.js.map +1 -1
- package/dist/cjs/layouts/barTitleSpan.native.js.map +1 -1
- package/dist/cjs/layouts/bottomAccessory.cjs +25 -13
- package/dist/cjs/layouts/bottomAccessory.native.js +29 -13
- package/dist/cjs/layouts/bottomAccessory.native.js.map +1 -1
- package/dist/cjs/layouts/breadcrumbs.native.js.map +1 -1
- package/dist/cjs/layouts/index.native.js +3 -3
- package/dist/cjs/layouts/index.native.js.map +1 -1
- package/dist/cjs/layouts/page.cjs +44 -6
- package/dist/cjs/layouts/page.native.js +50 -8
- package/dist/cjs/layouts/page.native.js.map +1 -1
- package/dist/cjs/layouts/pageHeadingScope.native.js.map +1 -1
- package/dist/cjs/layouts/screenScroll.native.js.map +1 -1
- package/dist/cjs/layouts/shellSidebarSlot.native.js.map +1 -1
- package/dist/cjs/layouts/sidebar.native.js +1 -1
- package/dist/cjs/layouts/sidebar.native.js.map +1 -1
- package/dist/cjs/layouts/tags.native.js +2 -2
- package/dist/cjs/layouts/tags.native.js.map +1 -1
- package/dist/cjs/layouts/timeline.native.js +2 -2
- package/dist/cjs/layouts/timeline.native.js.map +1 -1
- package/dist/cjs/layouts/toolbar.native.js +1 -1
- package/dist/cjs/layouts/toolbar.native.js.map +1 -1
- package/dist/cjs/shared/actionScan.native.js.map +1 -1
- package/dist/cjs/shared/i18n.native.js.map +1 -1
- package/dist/cjs/shared/restoreRetries.native.js.map +1 -1
- package/dist/cjs/shared/t.native.js.map +1 -1
- package/dist/cjs/shared/textRidesText.native.js.map +1 -1
- package/dist/cjs/shared/useReturnFocusOnClose.native.js.map +1 -1
- package/dist/cjs/surfaces.native.js +1 -1
- package/dist/cjs/surfaces.native.js.map +1 -1
- package/dist/cjs/tamagui.native.js +1 -1
- package/dist/cjs/tamagui.native.js.map +1 -1
- package/dist/cjs/testSetup.native.js.map +1 -1
- package/dist/cjs/utils/setTinted.native.js.map +1 -1
- package/dist/cjs/utils/unwrapText.native.js.map +1 -1
- package/dist/cjs/views/Calendar.native.js +1 -1
- package/dist/cjs/views/Calendar.native.js.map +1 -1
- package/dist/cjs/views/Dashboard.native.js +2 -2
- package/dist/cjs/views/Dashboard.native.js.map +1 -1
- package/dist/cjs/views/Gantt.native.js +2 -2
- package/dist/cjs/views/Gantt.native.js.map +1 -1
- package/dist/cjs/views/GanttDateGrip.native.js.map +1 -1
- package/dist/cjs/views/ImageGrid.native.js +2 -2
- package/dist/cjs/views/ImageGrid.native.js.map +1 -1
- package/dist/cjs/views/Kanban.native.js +1 -1
- package/dist/cjs/views/List.native.js.map +1 -1
- package/dist/cjs/views/ListGroup.native.js.map +1 -1
- package/dist/cjs/views/ListRow.native.js.map +1 -1
- package/dist/cjs/views/Matrix.native.js +2 -2
- package/dist/cjs/views/Matrix.native.js.map +1 -1
- package/dist/cjs/views/ReportBuilder.native.js +2 -2
- package/dist/cjs/views/ReportBuilder.native.js.map +1 -1
- package/dist/cjs/views/ShortcutCard.native.js.map +1 -1
- package/dist/cjs/views/TreeView.native.js.map +1 -1
- package/dist/cjs/views/calendarMath.native.js.map +1 -1
- package/dist/cjs/views/ganttIntervals.native.js.map +1 -1
- package/dist/cjs/views/ganttMath.native.js.map +1 -1
- package/dist/cjs/views/index.native.js +1 -1
- package/dist/cjs/views/index.native.js.map +1 -1
- package/dist/cjs/views/kanbanDnd.native.js.map +1 -1
- package/dist/cjs/views/matrixMath.native.js.map +1 -1
- package/dist/cjs/views/sameNameScope.native.js.map +1 -1
- package/dist/cjs/views/treeViewDnd.native.js.map +1 -1
- package/dist/cjs/views/treeViewLayout.native.js.map +1 -1
- package/dist/cjs/views/treeViewMotion.native.js.map +1 -1
- package/dist/cjs/views/treeViewReveal.native.js.map +1 -1
- package/dist/cjs/views/usePageFlowLoadMore.native.js.map +1 -1
- package/dist/cjs/views/widgetChrome.native.js.map +1 -1
- package/dist/esm/Carousel/index.mjs +12 -10
- package/dist/esm/Carousel/index.mjs.map +1 -1
- package/dist/esm/Carousel/index.native.js +13 -11
- package/dist/esm/Carousel/index.native.js.map +1 -1
- package/dist/esm/CopyField/CopyMenuButton.mjs +1 -3
- package/dist/esm/CopyField/CopyMenuButton.mjs.map +1 -1
- package/dist/esm/CopyField/CopyMenuButton.native.js +1 -3
- package/dist/esm/CopyField/CopyMenuButton.native.js.map +1 -1
- package/dist/esm/Meter/meter.mjs +1 -1
- package/dist/esm/Meter/meter.mjs.map +1 -1
- package/dist/esm/Meter/meter.native.js +1 -1
- package/dist/esm/Meter/meter.native.js.map +1 -1
- package/dist/esm/SheetModal/index.mjs +34 -6
- package/dist/esm/SheetModal/index.mjs.map +1 -1
- package/dist/esm/appShell/TabBar.mjs +2 -2
- package/dist/esm/appShell/TabBar.mjs.map +1 -1
- package/dist/esm/appShell/TabBar.native.js +2 -2
- package/dist/esm/appShell/TabBar.native.js.map +1 -1
- package/dist/esm/appShell/TabHost.native.js +53 -9
- package/dist/esm/appShell/TabHost.native.js.map +1 -1
- package/dist/esm/layouts/PageTemplates.mjs +16 -13
- package/dist/esm/layouts/PageTemplates.mjs.map +1 -1
- package/dist/esm/layouts/PageTemplates.native.js +17 -14
- package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
- package/dist/esm/layouts/ViewSwitcher.mjs +12 -2
- package/dist/esm/layouts/ViewSwitcher.mjs.map +1 -1
- package/dist/esm/layouts/ViewSwitcher.native.js +15 -2
- package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
- package/dist/esm/layouts/bottomAccessory.mjs +26 -15
- package/dist/esm/layouts/bottomAccessory.mjs.map +1 -1
- package/dist/esm/layouts/bottomAccessory.native.js +30 -15
- package/dist/esm/layouts/bottomAccessory.native.js.map +1 -1
- package/dist/esm/layouts/page.mjs +44 -8
- package/dist/esm/layouts/page.mjs.map +1 -1
- package/dist/esm/layouts/page.native.js +48 -8
- package/dist/esm/layouts/page.native.js.map +1 -1
- package/dist/jsx/Carousel/index.mjs +12 -10
- package/dist/jsx/Carousel/index.mjs.map +1 -1
- package/dist/jsx/Carousel/index.native.js +12 -10
- package/dist/jsx/Carousel/index.native.js.map +1 -1
- package/dist/jsx/CopyField/CopyMenuButton.mjs +1 -3
- package/dist/jsx/CopyField/CopyMenuButton.mjs.map +1 -1
- package/dist/jsx/CopyField/CopyMenuButton.native.js +1 -3
- package/dist/jsx/CopyField/CopyMenuButton.native.js.map +1 -1
- package/dist/jsx/Meter/meter.mjs +1 -1
- package/dist/jsx/Meter/meter.mjs.map +1 -1
- package/dist/jsx/Meter/meter.native.js +1 -1
- package/dist/jsx/Meter/meter.native.js.map +1 -1
- package/dist/jsx/SheetModal/index.mjs +34 -6
- package/dist/jsx/SheetModal/index.mjs.map +1 -1
- package/dist/jsx/appShell/TabBar.mjs +2 -2
- package/dist/jsx/appShell/TabBar.mjs.map +1 -1
- package/dist/jsx/appShell/TabBar.native.js +1 -1
- package/dist/jsx/appShell/TabBar.native.js.map +1 -1
- package/dist/jsx/appShell/TabHost.native.js +52 -5
- package/dist/jsx/appShell/TabHost.native.js.map +1 -1
- package/dist/jsx/feedback/NotifyHost.native.js +1 -1
- package/dist/jsx/formLayouts/PreferencePanel.native.js +1 -1
- package/dist/jsx/index.native.js +1 -1
- package/dist/jsx/layouts/AsyncBoundary.native.js +1 -1
- package/dist/jsx/layouts/AsyncBoundary.native.js.map +1 -1
- package/dist/jsx/layouts/PageTemplates.mjs +16 -13
- package/dist/jsx/layouts/PageTemplates.mjs.map +1 -1
- package/dist/jsx/layouts/PageTemplates.native.js +16 -13
- package/dist/jsx/layouts/PageTemplates.native.js.map +1 -1
- package/dist/jsx/layouts/ViewSwitcher.mjs +12 -2
- package/dist/jsx/layouts/ViewSwitcher.mjs.map +1 -1
- package/dist/jsx/layouts/ViewSwitcher.native.js +15 -2
- package/dist/jsx/layouts/ViewSwitcher.native.js.map +1 -1
- package/dist/jsx/layouts/bottomAccessory.mjs +26 -15
- package/dist/jsx/layouts/bottomAccessory.mjs.map +1 -1
- package/dist/jsx/layouts/bottomAccessory.native.js +29 -13
- package/dist/jsx/layouts/bottomAccessory.native.js.map +1 -1
- package/dist/jsx/layouts/page.mjs +44 -8
- package/dist/jsx/layouts/page.mjs.map +1 -1
- package/dist/jsx/layouts/page.native.js +48 -6
- package/dist/jsx/layouts/page.native.js.map +1 -1
- package/package.json +11 -11
- package/src/Carousel/Carousel.native.spec.tsx +25 -8
- package/src/Carousel/index.tsx +18 -11
- package/src/CopyField/CopyMenuButton.spec.tsx +2 -0
- package/src/CopyField/CopyMenuButton.tsx +1 -8
- package/src/Meter/Meter.spec.tsx +5 -4
- package/src/Meter/meter.ts +6 -4
- package/src/SheetModal/SheetModal.regularWidth.spec.tsx +87 -0
- package/src/SheetModal/index.tsx +52 -5
- package/src/Text.stories.tsx +13 -0
- package/src/appShell/TabBar.spec.tsx +10 -4
- package/src/appShell/TabBar.tsx +4 -8
- package/src/appShell/TabHost.native.spec.tsx +109 -1
- package/src/appShell/TabHost.native.tsx +84 -13
- package/src/icons/themedButtonInk.spec.tsx +117 -0
- package/src/layouts/DensePageLayout.spec.tsx +105 -0
- package/src/layouts/PageTemplates.tsx +14 -11
- package/src/layouts/Screen.chromeClearance.spec.tsx +28 -0
- package/src/layouts/ViewSwitcher.native-press.spec.tsx +55 -0
- package/src/layouts/ViewSwitcher.tsx +13 -2
- package/src/layouts/bottomAccessory.tsx +40 -15
- package/src/layouts/page.tsx +73 -19
- package/src/layouts/pageTitleOneLine.spec.tsx +42 -0
- package/src/layouts/pushedPageTitle.spec.tsx +116 -0
- package/types/Carousel/index.d.ts.map +1 -1
- package/types/CopyField/CopyMenuButton.d.ts.map +1 -1
- package/types/Meter/meter.d.ts +6 -4
- package/types/Meter/meter.d.ts.map +1 -1
- package/types/SheetModal/index.d.ts.map +1 -1
- package/types/appShell/TabBar.d.ts.map +1 -1
- package/types/appShell/TabHost.native.d.ts +13 -1
- package/types/appShell/TabHost.native.d.ts.map +1 -1
- package/types/layouts/PageTemplates.d.ts.map +1 -1
- package/types/layouts/ViewSwitcher.d.ts.map +1 -1
- package/types/layouts/bottomAccessory.d.ts +6 -1
- package/types/layouts/bottomAccessory.d.ts.map +1 -1
- package/types/layouts/page.d.ts +16 -1
- package/types/layouts/page.d.ts.map +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*
|
|
4
|
+
* FZ-8577: a `themed` glyph inside a Button fills with the Button's ink on
|
|
5
|
+
* web, as it does on native. Before, an accentInk icon Button painted its
|
|
6
|
+
* frame in the bar tint while the svg filled with the theme's `var(--color)`,
|
|
7
|
+
* so the SHC console's search, graph and gauge glyphs drew near-black on web
|
|
8
|
+
* and purple on iOS. The same held for an intent icon button and a disabled
|
|
9
|
+
* one (FZ-8044, LC-40: the disabled twin keeps its ink, dimmed).
|
|
10
|
+
*
|
|
11
|
+
* jsdom cascades no Tamagui CSS, so the frame's ink is read from the rule
|
|
12
|
+
* behind its `_col-` class, and the glyph's from its own fill: either that
|
|
13
|
+
* ink itself or `currentColor`, which inherits it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
17
|
+
import { Button } from "@multiplatform.one/forms";
|
|
18
|
+
import { createDefaultThemeConfig, Surface } from "@multiplatform.one/theme";
|
|
19
|
+
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
|
|
20
|
+
import type { ReactElement } from "react";
|
|
21
|
+
import { TamaguiProvider, YStack } from "tamagui";
|
|
22
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
23
|
+
import { themed } from "./themed";
|
|
24
|
+
|
|
25
|
+
afterEach(cleanup);
|
|
26
|
+
|
|
27
|
+
const houseConfig = createDefaultThemeConfig();
|
|
28
|
+
const SearchGlyph = themed(MagnifyingGlassIcon);
|
|
29
|
+
|
|
30
|
+
function renderUnder(ui: ReactElement, theme: string) {
|
|
31
|
+
return render(
|
|
32
|
+
<TamaguiProvider config={houseConfig.tamagui} defaultTheme={theme}>
|
|
33
|
+
<YStack backgroundColor="$background">{ui}</YStack>
|
|
34
|
+
</TamaguiProvider>,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ruleValue(className: string, property: string): string | null {
|
|
39
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
40
|
+
for (const rule of Array.from(sheet.cssRules)) {
|
|
41
|
+
if (!(rule instanceof CSSStyleRule)) continue;
|
|
42
|
+
if (!rule.selectorText.endsWith(`.${className}`)) continue;
|
|
43
|
+
const value = rule.style.getPropertyValue(property);
|
|
44
|
+
if (value) return value.trim();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function frameInk(label: string): string | null {
|
|
51
|
+
const frame = screen.getByLabelText(label);
|
|
52
|
+
const cls = Array.from(frame.classList).find(
|
|
53
|
+
(c) => c.startsWith("_col-") && !/^_col-0(hover|active|focus|press)/.test(c),
|
|
54
|
+
);
|
|
55
|
+
return cls ? ruleValue(cls, "color") : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function glyphFill(label: string): string | null {
|
|
59
|
+
const svg = screen.getByLabelText(label).querySelector("svg");
|
|
60
|
+
return svg?.getAttribute("fill") ?? null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The glyph takes the frame's ink: the same value, or currentColor, which inherits it. */
|
|
64
|
+
function expectGlyphTakesFrameInk(label: string) {
|
|
65
|
+
const ink = frameInk(label);
|
|
66
|
+
const fill = glyphFill(label);
|
|
67
|
+
expect(ink, `${label} frame ink`).toBeTruthy();
|
|
68
|
+
expect(fill, `${label} glyph fill`).toBeTruthy();
|
|
69
|
+
expect(fill).not.toBe("var(--color)");
|
|
70
|
+
if (fill !== "currentColor") expect(fill?.toLowerCase()).toBe(ink?.toLowerCase());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("FZ-8577: a themed glyph in a Button fills with the Button's ink on web", () => {
|
|
74
|
+
for (const theme of ["light_purple", "dark_purple", "light_blue", "dark_blue"]) {
|
|
75
|
+
it(`${theme}: an accentInk icon button in a bar`, () => {
|
|
76
|
+
renderUnder(
|
|
77
|
+
<Surface bar>
|
|
78
|
+
<Button chromeless accentInk icon={SearchGlyph} aria-label="Search" />
|
|
79
|
+
</Surface>,
|
|
80
|
+
theme,
|
|
81
|
+
);
|
|
82
|
+
expectGlyphTakesFrameInk("Search");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it(`${theme}: an accentInk icon button in content`, () => {
|
|
86
|
+
renderUnder(<Button chromeless accentInk icon={SearchGlyph} aria-label="Find" />, theme);
|
|
87
|
+
expectGlyphTakesFrameInk("Find");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it(`${theme}: an intent icon button`, () => {
|
|
91
|
+
renderUnder(<Button error icon={SearchGlyph} aria-label="Remove" />, theme);
|
|
92
|
+
expectGlyphTakesFrameInk("Remove");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it(`${theme}: a disabled accentInk icon button keeps the enabled twin's ink`, () => {
|
|
96
|
+
renderUnder(
|
|
97
|
+
<Surface bar>
|
|
98
|
+
<Button chromeless accentInk icon={SearchGlyph} aria-label="Next" />
|
|
99
|
+
<Button chromeless accentInk disabled icon={SearchGlyph} aria-label="Previous" />
|
|
100
|
+
</Surface>,
|
|
101
|
+
theme,
|
|
102
|
+
);
|
|
103
|
+
expectGlyphTakesFrameInk("Previous");
|
|
104
|
+
expect(frameInk("Previous")).toBe(frameInk("Next"));
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
it("a glyph with its own colour keeps it", () => {
|
|
109
|
+
renderUnder(
|
|
110
|
+
<Surface bar>
|
|
111
|
+
<Button chromeless accentInk icon={<SearchGlyph color="red" />} aria-label="Own" />
|
|
112
|
+
</Surface>,
|
|
113
|
+
"light_purple",
|
|
114
|
+
);
|
|
115
|
+
expect(glyphFill("Own")).toBe("red");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { cleanup, screen } from "@testing-library/react";
|
|
2
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
3
|
+
import { layoutBreakpoints } from "@multiplatform.one/theme";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { Button, Paragraph } from "tamagui";
|
|
6
|
+
import { CONTENT_MAX_WIDTH } from "./page";
|
|
7
|
+
import { DensePageLayout } from "./PageTemplates";
|
|
8
|
+
|
|
9
|
+
const size = vi.hoisted(() => ({ value: "medium" as string }));
|
|
10
|
+
vi.mock("@multiplatform.one/theme", async (importOriginal) => ({
|
|
11
|
+
...(await importOriginal<typeof import("@multiplatform.one/theme")>()),
|
|
12
|
+
useLayoutSizeClass: () => size.value,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
cleanup();
|
|
17
|
+
size.value = "medium";
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function Log(props: Partial<Parameters<typeof DensePageLayout>[0]>) {
|
|
21
|
+
return (
|
|
22
|
+
<DensePageLayout
|
|
23
|
+
kind="log"
|
|
24
|
+
title="Sync worker"
|
|
25
|
+
status={<Paragraph>Running</Paragraph>}
|
|
26
|
+
actions={<Button aria-label="Pause the stream">Pause</Button>}
|
|
27
|
+
data-testid="dense-shell"
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
<Paragraph>log rows</Paragraph>
|
|
31
|
+
</DensePageLayout>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Tamagui's atomic classes on web: `_pl-t-space-4` is paddingLeft $4. */
|
|
36
|
+
function atom(el: HTMLElement, prefix: string) {
|
|
37
|
+
return el.className.split(" ").find((name) => name.startsWith(prefix)) ?? null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function surface(kind: string) {
|
|
41
|
+
return document.querySelector(`[data-dense-surface="${kind}"]`) as HTMLElement;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("DensePageLayout (LC-215)", () => {
|
|
45
|
+
it("keeps the page head and gives the surface the rest of the page", () => {
|
|
46
|
+
renderWithProviders(<Log />);
|
|
47
|
+
expect(screen.getByRole("heading", { name: "Sync worker" })).toBeTruthy();
|
|
48
|
+
expect(screen.getByText("Running")).toBeTruthy();
|
|
49
|
+
expect(screen.getByLabelText("Pause the stream")).toBeTruthy();
|
|
50
|
+
const region = surface("log");
|
|
51
|
+
expect(region.textContent).toBe("log rows");
|
|
52
|
+
expect(region.contains(screen.getByText("Sync worker"))).toBe(false);
|
|
53
|
+
expect(region).toHaveStyle({ flexGrow: "1", minHeight: "0px" });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("from medium up holds a log to the page column and a chart to the readable measure", () => {
|
|
57
|
+
renderWithProviders(<Log />);
|
|
58
|
+
expect(screen.getByTestId("dense-shell")).toHaveStyle({ maxWidth: `${CONTENT_MAX_WIDTH}px` });
|
|
59
|
+
const log = surface("log");
|
|
60
|
+
expect(atom(log, "_ox-")).toBe("_ox-hidden");
|
|
61
|
+
expect(atom(log, "_btlr-")).toMatch(/^_btlr-t-radius-/);
|
|
62
|
+
cleanup();
|
|
63
|
+
renderWithProviders(<Log kind="chart" measure="readable" />);
|
|
64
|
+
expect(screen.getByTestId("dense-shell")).toHaveStyle({
|
|
65
|
+
maxWidth: `${layoutBreakpoints.expanded}px`,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("on a phone bleeds the surface to both edges, with a gutter only when asked", () => {
|
|
70
|
+
size.value = "compact";
|
|
71
|
+
renderWithProviders(<Log />);
|
|
72
|
+
const shell = screen.getByTestId("dense-shell");
|
|
73
|
+
expect(getComputedStyle(shell).maxWidth).not.toBe(`${CONTENT_MAX_WIDTH}px`);
|
|
74
|
+
const region = surface("log");
|
|
75
|
+
expect([atom(region, "_pl-"), atom(region, "_pr-"), atom(region, "_ox-")]).toEqual([
|
|
76
|
+
null,
|
|
77
|
+
null,
|
|
78
|
+
null,
|
|
79
|
+
]);
|
|
80
|
+
expect(region.contains(screen.getByText("Sync worker"))).toBe(false);
|
|
81
|
+
cleanup();
|
|
82
|
+
renderWithProviders(<Log kind="chart" gutter />);
|
|
83
|
+
const chart = surface("chart");
|
|
84
|
+
expect(atom(chart, "_pl-")).toMatch(/^_pl-t-space-/);
|
|
85
|
+
expect(atom(chart, "_pl-")?.slice(4)).toBe(atom(chart, "_pr-")?.slice(4));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("takes a caller's header in place of its own, and draws none with nothing to show", () => {
|
|
89
|
+
renderWithProviders(
|
|
90
|
+
<DensePageLayout kind="log" header={<Paragraph>Custom head</Paragraph>}>
|
|
91
|
+
<Paragraph>rows</Paragraph>
|
|
92
|
+
</DensePageLayout>,
|
|
93
|
+
);
|
|
94
|
+
expect(screen.getByText("Custom head")).toBeTruthy();
|
|
95
|
+
expect(screen.queryByRole("heading")).toBeNull();
|
|
96
|
+
cleanup();
|
|
97
|
+
renderWithProviders(
|
|
98
|
+
<DensePageLayout kind="log">
|
|
99
|
+
<Paragraph>rows</Paragraph>
|
|
100
|
+
</DensePageLayout>,
|
|
101
|
+
);
|
|
102
|
+
expect(screen.queryByRole("heading")).toBeNull();
|
|
103
|
+
expect(surface("log").textContent).toBe("rows");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
} from "@multiplatform.one/theme";
|
|
33
33
|
import { useState, type ReactNode } from "react";
|
|
34
34
|
import { XStack, YStack, isWeb, type SizeTokens } from "tamagui";
|
|
35
|
-
import { PageHeader, Screen, ScreenToolbar, type ScreenProps } from "./page";
|
|
35
|
+
import { PageHeader, PushedPage, Screen, ScreenToolbar, type ScreenProps } from "./page";
|
|
36
36
|
import { SupportingPaneLayout } from "./PaneScaffold";
|
|
37
37
|
import { useShellSidebarPanel } from "./shellSidebarSlot";
|
|
38
38
|
|
|
@@ -266,16 +266,19 @@ export function DetailPageLayout({
|
|
|
266
266
|
return (
|
|
267
267
|
// LC-157: the toolbar is the Screen's pinned top bar, so it stays put
|
|
268
268
|
// while the record scrolls and takes the title once the header scrolls away.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
269
|
+
// LC-270: a detail page is pushed, so on a phone its title starts there.
|
|
270
|
+
<PushedPage>
|
|
271
|
+
<Screen
|
|
272
|
+
data-canonical="detail-page"
|
|
273
|
+
topBar={toolbar}
|
|
274
|
+
// LC-263: one readable column; a supporting pane gets the full width.
|
|
275
|
+
maxWidth={aside == null ? DETAIL_READABLE_WIDTH : undefined}
|
|
276
|
+
{...rest}
|
|
277
|
+
>
|
|
278
|
+
{renderTemplateHeader({ title, subtitle, actions, header, nav })}
|
|
279
|
+
{body}
|
|
280
|
+
</Screen>
|
|
281
|
+
</PushedPage>
|
|
279
282
|
);
|
|
280
283
|
}
|
|
281
284
|
|
|
@@ -7,6 +7,7 @@ import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
|
7
7
|
import { act, cleanup, screen } from "@testing-library/react";
|
|
8
8
|
import { afterEach, expect, it } from "vitest";
|
|
9
9
|
import { setBottomChromeInset } from "../appShell/bottomChrome";
|
|
10
|
+
import { BottomAccessory } from "./bottomAccessory";
|
|
10
11
|
import { Screen } from "./page";
|
|
11
12
|
|
|
12
13
|
const bar = Symbol("bar");
|
|
@@ -39,3 +40,30 @@ it("clears the bar on a screen that does not scroll", () => {
|
|
|
39
40
|
renderWithProviders(<Screen scroll={false}>row</Screen>);
|
|
40
41
|
expect(screen.getByTestId("screen-chrome-clearance")).toBeTruthy();
|
|
41
42
|
});
|
|
43
|
+
|
|
44
|
+
it("hands the clearance to a slot accessory that stands on a bar it does not ride (FZ-7995)", () => {
|
|
45
|
+
act(() => setBottomChromeInset(bar, 83));
|
|
46
|
+
renderWithProviders(
|
|
47
|
+
<Screen>
|
|
48
|
+
row
|
|
49
|
+
<BottomAccessory>
|
|
50
|
+
<span data-testid="pager">pager</span>
|
|
51
|
+
</BottomAccessory>
|
|
52
|
+
</Screen>,
|
|
53
|
+
);
|
|
54
|
+
expect(screen.queryByTestId("screen-chrome-clearance")).toBeNull();
|
|
55
|
+
const lift = screen.getByTestId("bottom-accessory-lift");
|
|
56
|
+
expect(lift.contains(screen.getByTestId("pager"))).toBe(true);
|
|
57
|
+
expect(getComputedStyle(lift).paddingBottom === "83px" || /83px/.test(lift.outerHTML)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("grows its scrolling column to its content, so the clearance ends the content, not the viewport", () => {
|
|
61
|
+
act(() => setBottomChromeInset(bar, 77));
|
|
62
|
+
renderWithProviders(<Screen>row</Screen>);
|
|
63
|
+
const column = screen.getByTestId("screen");
|
|
64
|
+
const style = getComputedStyle(column);
|
|
65
|
+
const html = column.outerHTML;
|
|
66
|
+
expect(style.flexShrink === "0" || /_fs-0\b|flex-shrink: 0/.test(html)).toBe(true);
|
|
67
|
+
expect(style.flexBasis === "0px" || /_fb-0px\b|flex-basis: 0px/.test(html)).toBe(false);
|
|
68
|
+
expect(column.nextElementSibling).toBe(screen.getByTestId("screen-chrome-clearance"));
|
|
69
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FZ-8991, under tamagui's NATIVE build (vitest.native.config.mjs). On the iOS
|
|
3
|
+
* simulator One's Moves list ignored every tap on the ViewSwitcher's Table
|
|
4
|
+
* segment, so a phone user could never reach the table view. A press on a
|
|
5
|
+
* segment's label has to reach the segment and switch the view.
|
|
6
|
+
*/
|
|
7
|
+
import { cleanup, fireEvent, screen } from "@testing-library/react";
|
|
8
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
9
|
+
import { isWeb } from "tamagui";
|
|
10
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
11
|
+
import { ViewSwitcher } from "./ViewSwitcher";
|
|
12
|
+
|
|
13
|
+
afterEach(cleanup);
|
|
14
|
+
|
|
15
|
+
const views = [
|
|
16
|
+
{ type: "list", label: "List" },
|
|
17
|
+
{ type: "table", label: "Table" },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function press(el: Element) {
|
|
21
|
+
fireEvent.mouseDown(el, { button: 0, buttons: 1 });
|
|
22
|
+
fireEvent.mouseUp(el, { button: 0, buttons: 0 });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("ViewSwitcher press on native", () => {
|
|
26
|
+
it("runs tamagui's native build", () => {
|
|
27
|
+
expect(isWeb).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("a press on the Table segment switches to table", () => {
|
|
31
|
+
const onViewChange = vi.fn();
|
|
32
|
+
renderWithProviders(
|
|
33
|
+
<ViewSwitcher views={views} currentView="list" onViewChange={onViewChange} />,
|
|
34
|
+
);
|
|
35
|
+
press(screen.getByText("Table"));
|
|
36
|
+
expect(onViewChange).toHaveBeenCalledWith("table");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("a press on the List segment switches back to list", () => {
|
|
40
|
+
const onViewChange = vi.fn();
|
|
41
|
+
renderWithProviders(
|
|
42
|
+
<ViewSwitcher views={views} currentView="table" onViewChange={onViewChange} />,
|
|
43
|
+
);
|
|
44
|
+
press(screen.getByText("List"));
|
|
45
|
+
expect(onViewChange).toHaveBeenCalledWith("list");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// The selected state rides accessibilityState, which this DOM shim does not
|
|
49
|
+
// render; the simulator's accessibility tree is where that is checked.
|
|
50
|
+
it("each segment is a named button", () => {
|
|
51
|
+
renderWithProviders(<ViewSwitcher views={views} currentView="list" onViewChange={() => {}} />);
|
|
52
|
+
const buttons = screen.getAllByRole("button");
|
|
53
|
+
expect(buttons.map((b) => b.getAttribute("aria-label"))).toEqual(["List", "Table"]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -126,7 +126,10 @@ export function ViewSwitcher({
|
|
|
126
126
|
overflow="hidden"
|
|
127
127
|
focusStyle={groupRing}
|
|
128
128
|
focusVisibleStyle={groupRing}
|
|
129
|
-
focusWithinStyle
|
|
129
|
+
// FZ-8991: on native a focusWithinStyle hands every descendant focus
|
|
130
|
+
// events, and the segment's label then takes the touch, so the segment's
|
|
131
|
+
// onPress never runs. The ring is a keyboard affordance: web only.
|
|
132
|
+
{...(isWeb ? { focusWithinStyle: groupRing } : undefined)}
|
|
130
133
|
// Carbon / Apple equal-width: inline style so Tamagui's flex class
|
|
131
134
|
// cannot drop `gridAutoFlow` (a Tamagui `display="grid"` prop did).
|
|
132
135
|
{...(isWeb
|
|
@@ -167,7 +170,15 @@ export function ViewSwitcher({
|
|
|
167
170
|
<ToggleGroup.Item
|
|
168
171
|
key={view.type}
|
|
169
172
|
value={view.type}
|
|
170
|
-
{...(isWeb
|
|
173
|
+
{...(isWeb
|
|
174
|
+
? { tabIndex: isTabStop ? 0 : -1 }
|
|
175
|
+
: {
|
|
176
|
+
accessible: true,
|
|
177
|
+
role: "button" as const,
|
|
178
|
+
accessibilityRole: "button" as const,
|
|
179
|
+
accessibilityLabel: view.label,
|
|
180
|
+
accessibilityState: { selected: isSelected },
|
|
181
|
+
})}
|
|
171
182
|
paddingHorizontal={knobProps.tab.paddingHorizontal}
|
|
172
183
|
width="auto"
|
|
173
184
|
height={knobProps.tab.height}
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* UIPageControl under a paged list), not inline after the content.
|
|
5
5
|
*
|
|
6
6
|
* Under a floating TabBar the accessory moves into the bar, which floats it
|
|
7
|
-
* as its own capsule above the tab capsule (LC-150)
|
|
7
|
+
* as its own capsule above the tab capsule (LC-150), and under the system tab
|
|
8
|
+
* bar on iOS 26 into its bottom accessory (FZ-7995). A tab bar that hosts no
|
|
9
|
+
* accessory (the system bar before iOS 26) still covers the bottom of the
|
|
10
|
+
* screen, so the slot's bar stands on it instead of on the home indicator. Otherwise a page shell
|
|
8
11
|
* (`Screen`) owns one bottom-accessory slot. Any component
|
|
9
12
|
* hands it a control with `<BottomAccessory>`; the shell draws it in a bar
|
|
10
13
|
* under its scroll view that stays put while the content scrolls, so the
|
|
@@ -30,7 +33,11 @@ import {
|
|
|
30
33
|
type ReactNode,
|
|
31
34
|
} from "react";
|
|
32
35
|
import { YStack, isWeb } from "tamagui";
|
|
33
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
setBottomAccessory,
|
|
38
|
+
useBottomChromeInset,
|
|
39
|
+
useFloatingBarMounted,
|
|
40
|
+
} from "../appShell/bottomChrome";
|
|
34
41
|
import { componentColors } from "../componentColors";
|
|
35
42
|
import { BottomSafeArea } from "./bottomSafeArea";
|
|
36
43
|
|
|
@@ -140,6 +147,15 @@ export function BottomAccessory({ children }: BottomAccessoryProps) {
|
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
/** A page shell's slot: the bar the registered accessories render in. */
|
|
150
|
+
/** True while a page's slot holds an accessory. */
|
|
151
|
+
export function useBottomAccessoryOccupied(store: BottomAccessoryStore): boolean {
|
|
152
|
+
return useSyncExternalStore(
|
|
153
|
+
store.subscribe,
|
|
154
|
+
() => store.getSnapshot().length > 0,
|
|
155
|
+
() => false,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
143
159
|
export function BottomAccessoryHost({
|
|
144
160
|
store,
|
|
145
161
|
inset,
|
|
@@ -161,8 +177,8 @@ export function BottomAccessoryHost({
|
|
|
161
177
|
}
|
|
162
178
|
|
|
163
179
|
/**
|
|
164
|
-
* The bar: the page's fill under a top hairline, and the
|
|
165
|
-
* inset
|
|
180
|
+
* The bar: the page's fill under a top hairline, and below the controls the
|
|
181
|
+
* home indicator's inset, or in a page's slot the height a tab bar covers.
|
|
166
182
|
*/
|
|
167
183
|
function BottomAccessoryBar({
|
|
168
184
|
children,
|
|
@@ -176,6 +192,20 @@ function BottomAccessoryBar({
|
|
|
176
192
|
hosted?: boolean;
|
|
177
193
|
}) {
|
|
178
194
|
const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
|
|
195
|
+
const chromeInset = useBottomChromeInset();
|
|
196
|
+
const lift = hosted && chromeInset > 0;
|
|
197
|
+
const controls = (
|
|
198
|
+
<YStack
|
|
199
|
+
width="100%"
|
|
200
|
+
{...(maxWidth ? { maxWidth, alignSelf: "center" as const } : null)}
|
|
201
|
+
alignItems="center"
|
|
202
|
+
paddingHorizontal={(inset ?? knobProps.panelPadding.padding) as "$4"}
|
|
203
|
+
paddingVertical={knobProps.gap.gap as "$2"}
|
|
204
|
+
{...knobProps.gap}
|
|
205
|
+
>
|
|
206
|
+
{children}
|
|
207
|
+
</YStack>
|
|
208
|
+
);
|
|
179
209
|
return (
|
|
180
210
|
<Surface bar>
|
|
181
211
|
<YStack
|
|
@@ -188,18 +218,13 @@ function BottomAccessoryBar({
|
|
|
188
218
|
borderColor={componentColors.divider as never}
|
|
189
219
|
style={webSticky as never}
|
|
190
220
|
>
|
|
191
|
-
|
|
192
|
-
<YStack
|
|
193
|
-
|
|
194
|
-
{...(maxWidth ? { maxWidth, alignSelf: "center" as const } : null)}
|
|
195
|
-
alignItems="center"
|
|
196
|
-
paddingHorizontal={(inset ?? knobProps.panelPadding.padding) as "$4"}
|
|
197
|
-
paddingVertical={knobProps.gap.gap as "$2"}
|
|
198
|
-
{...knobProps.gap}
|
|
199
|
-
>
|
|
200
|
-
{children}
|
|
221
|
+
{lift ? (
|
|
222
|
+
<YStack width="100%" paddingBottom={chromeInset} data-testid="bottom-accessory-lift">
|
|
223
|
+
{controls}
|
|
201
224
|
</YStack>
|
|
202
|
-
|
|
225
|
+
) : (
|
|
226
|
+
<BottomSafeArea>{controls}</BottomSafeArea>
|
|
227
|
+
)}
|
|
203
228
|
</YStack>
|
|
204
229
|
</Surface>
|
|
205
230
|
);
|
package/src/layouts/page.tsx
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
type ButtonProps,
|
|
41
41
|
} from "@multiplatform.one/forms";
|
|
42
42
|
import { CaretLeftIcon, CaretRightIcon, DotsThreeIcon } from "@phosphor-icons/react";
|
|
43
|
-
import { useState, type ReactNode } from "react";
|
|
43
|
+
import { createContext, useContext, useState, type ReactNode } from "react";
|
|
44
44
|
import { barTitleSpan, sideExtents } from "./barTitleSpan";
|
|
45
45
|
import type { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent } from "react-native";
|
|
46
46
|
import type { ScrollViewProps as TamaguiScrollViewProps, XStackProps, YStackProps } from "tamagui";
|
|
@@ -66,6 +66,7 @@ import { ScrollView } from "../ScrollView";
|
|
|
66
66
|
import { KeyboardAvoidingWrapper } from "./KeyboardAvoidingWrapper";
|
|
67
67
|
import {
|
|
68
68
|
BottomAccessoryHost,
|
|
69
|
+
useBottomAccessoryOccupied,
|
|
69
70
|
BottomAccessoryScope,
|
|
70
71
|
createBottomAccessoryStore,
|
|
71
72
|
} from "./bottomAccessory";
|
|
@@ -293,7 +294,10 @@ function PageShell({
|
|
|
293
294
|
<YStack
|
|
294
295
|
ref={scrollState.contentRoot as never}
|
|
295
296
|
data-testid="screen"
|
|
296
|
-
|
|
297
|
+
// In a scroll view the column grows to its content (basis auto, no
|
|
298
|
+
// shrink): with flex 1 it was held to the viewport on web, its rows
|
|
299
|
+
// overflowed it, and the chrome clearance after it landed mid-list.
|
|
300
|
+
{...(scroll ? { flexGrow: 1, flexShrink: 0, flexBasis: "auto" as const } : { flex: 1 })}
|
|
297
301
|
width="100%"
|
|
298
302
|
{...(maxWidth && !bleed ? { maxWidth, alignSelf: "center" } : null)}
|
|
299
303
|
{...(noPadding ? null : knobProps.panelPadding)}
|
|
@@ -334,8 +338,12 @@ function PageShell({
|
|
|
334
338
|
};
|
|
335
339
|
|
|
336
340
|
// The floating tab bar covers the bottom of the screen; content scrolls
|
|
337
|
-
// under it, and this clears its last row.
|
|
338
|
-
|
|
341
|
+
// under it, and this clears its last row. A slot accessory stands on the bar
|
|
342
|
+
// and ends the scroll view above it, so it takes the clearance instead.
|
|
343
|
+
const accessoryOccupied = useBottomAccessoryOccupied(accessories);
|
|
344
|
+
const chromeClearance = accessoryOccupied ? null : (
|
|
345
|
+
<BottomChromeClearance testID="screen-chrome-clearance" />
|
|
346
|
+
);
|
|
339
347
|
|
|
340
348
|
if (!scroll) {
|
|
341
349
|
return shell(
|
|
@@ -416,6 +424,32 @@ export interface PageHeaderProps extends YStackProps {
|
|
|
416
424
|
titleScale?: PageTitleScale;
|
|
417
425
|
}
|
|
418
426
|
|
|
427
|
+
const PushedPageContext = createContext<"inline" | "hero" | null>(null);
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* LC-270 PUSHED-PAGE-INLINE-TITLE: marks a pushed page, a detail or form page
|
|
431
|
+
* reached through a back control. At compact width its title rides the top
|
|
432
|
+
* bar, centred between back and the actions, and its `PageHeader` draws no
|
|
433
|
+
* large title. Root pages keep LC-157's large title. `hero` is the App Store
|
|
434
|
+
* and Music variant: the page's own title (an identity block's name) stays in
|
|
435
|
+
* the content, the bar shows nothing at rest, and the same title fades into
|
|
436
|
+
* the bar once that name scrolls out of view (LC-157's measured collapse).
|
|
437
|
+
*/
|
|
438
|
+
export function PushedPage({ hero = false, children }: { hero?: boolean; children: ReactNode }) {
|
|
439
|
+
return (
|
|
440
|
+
<PushedPageContext.Provider value={hero ? "hero" : "inline"}>
|
|
441
|
+
{children}
|
|
442
|
+
</PushedPageContext.Provider>
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** True when the page's title belongs in its top bar from rest (LC-270). */
|
|
447
|
+
export function useInlinePageTitle(): boolean {
|
|
448
|
+
const pushed = useContext(PushedPageContext);
|
|
449
|
+
const compact = useLayoutSizeClass() === "compact";
|
|
450
|
+
return pushed === "inline" && compact;
|
|
451
|
+
}
|
|
452
|
+
|
|
419
453
|
/**
|
|
420
454
|
* Standard page header (Polaris Page / Primer PageHeader / Linear).
|
|
421
455
|
*
|
|
@@ -452,24 +486,32 @@ export function PageHeader({
|
|
|
452
486
|
// LC-157: the page title is the Screen's large title; its top bar shows it
|
|
453
487
|
// once it scrolls away.
|
|
454
488
|
const largeTitle = useLargeTitle(title, headingLevel === 1 && !heading);
|
|
489
|
+
// LC-270: a pushed page's title is in its top bar at compact width.
|
|
490
|
+
const inlineTitle = useInlinePageTitle() && headingLevel === 1;
|
|
491
|
+
// LC-143: a phone's page title takes the width its actions leave, on one
|
|
492
|
+
// line, so wide actions truncate it instead of breaking it into letters.
|
|
493
|
+
const compact = useLayoutSizeClass() === "compact";
|
|
455
494
|
// D-11: the page-title step comes from the knob (`moderate` by default), so
|
|
456
495
|
// one dial re-scales every composed screen; `titleScale` is the explicit
|
|
457
496
|
// per-instance eject and resolves through the same table. The semantic tag
|
|
458
497
|
// still follows headingLevel — the step is emphasis, not structure.
|
|
459
498
|
const titleSize = titleScale ? resolvePageTitleScale(titleScale) : knobProps.pageTitle;
|
|
460
|
-
const titleNode =
|
|
461
|
-
|
|
462
|
-
(
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
499
|
+
const titleNode = inlineTitle
|
|
500
|
+
? null
|
|
501
|
+
: (heading ??
|
|
502
|
+
(title ? (
|
|
503
|
+
<TitleTag
|
|
504
|
+
margin={0}
|
|
505
|
+
{...(compact ? { numberOfLines: 1 } : null)}
|
|
506
|
+
{...largeTitle}
|
|
507
|
+
{...titleSize}
|
|
508
|
+
{...knobProps.heading}
|
|
509
|
+
{...(knobProps.heading.fontWeight ? { "data-font-weight-knob": "bold" } : null)}
|
|
510
|
+
>
|
|
511
|
+
{title}
|
|
512
|
+
</TitleTag>
|
|
513
|
+
) : null));
|
|
514
|
+
if (inlineTitle && !subtitle && !status && !actions && !nav && !children) return null;
|
|
473
515
|
|
|
474
516
|
// When nav is present the strip's active indicator sits ON this rule, so
|
|
475
517
|
// there is one hairline — not a second competing line. The header then
|
|
@@ -688,6 +730,9 @@ function useBarTitleSpan() {
|
|
|
688
730
|
};
|
|
689
731
|
}
|
|
690
732
|
|
|
733
|
+
// LC-270: the pushed page's bar title is the page's heading.
|
|
734
|
+
const pageTitleRole = { role: "heading", "aria-level": 1 } as const;
|
|
735
|
+
|
|
691
736
|
function BarTitle({
|
|
692
737
|
title,
|
|
693
738
|
span,
|
|
@@ -766,6 +811,7 @@ function InlineToolbar({ leading, trailing, title, children, ...rest }: ScreenTo
|
|
|
766
811
|
const bleed = insetToken ? { marginHorizontal: negateSpaceToken(insetToken) as "$4" } : null;
|
|
767
812
|
// LC-145: a phone top bar is one row; the leading group shrinks first.
|
|
768
813
|
const oneRow = useLayoutSizeClass() === "compact";
|
|
814
|
+
const pushedTitle = useInlinePageTitle();
|
|
769
815
|
// LC-153: the toolbar is a bar, so its neutral buttons paint plain.
|
|
770
816
|
return (
|
|
771
817
|
<Surface bar>
|
|
@@ -804,7 +850,12 @@ function InlineToolbar({ leading, trailing, title, children, ...rest }: ScreenTo
|
|
|
804
850
|
{trailing}
|
|
805
851
|
</IconButtonGroup>
|
|
806
852
|
{title ? (
|
|
807
|
-
<BarTitle
|
|
853
|
+
<BarTitle
|
|
854
|
+
title={title}
|
|
855
|
+
span={titleSpan.span}
|
|
856
|
+
onTitleWidth={titleSpan.measureTitle}
|
|
857
|
+
{...(pushedTitle ? pageTitleRole : null)}
|
|
858
|
+
/>
|
|
808
859
|
) : null}
|
|
809
860
|
</XStack>
|
|
810
861
|
</Surface>
|
|
@@ -834,6 +885,8 @@ function PinnedTopBar({
|
|
|
834
885
|
const titleSpan = useBarTitleSpan();
|
|
835
886
|
const motion = transitionProps(knobProps.motion.control);
|
|
836
887
|
const title = inlineTitle ?? largeTitle;
|
|
888
|
+
// LC-270: a pushed page shows its title in the bar from rest, as its heading.
|
|
889
|
+
const pushedTitle = useInlinePageTitle();
|
|
837
890
|
// LC-153: the pinned bar is a bar, so its neutral buttons paint plain.
|
|
838
891
|
return (
|
|
839
892
|
<Surface bar>
|
|
@@ -888,7 +941,8 @@ function PinnedTopBar({
|
|
|
888
941
|
title={title}
|
|
889
942
|
span={titleSpan.span}
|
|
890
943
|
onTitleWidth={titleSpan.measureTitle}
|
|
891
|
-
shown={Boolean(inlineTitle) || collapsed}
|
|
944
|
+
shown={Boolean(inlineTitle) || collapsed || pushedTitle}
|
|
945
|
+
{...(pushedTitle ? pageTitleRole : null)}
|
|
892
946
|
{...motion}
|
|
893
947
|
/>
|
|
894
948
|
) : null}
|