@multiplatform.one/components 7.81.2 → 7.82.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/appShell/AppShell.cjs +1 -9
- package/dist/cjs/appShell/AppShell.native.js +1 -9
- package/dist/cjs/appShell/AppShell.native.js.map +1 -1
- package/dist/cjs/appShell/BottomChromeClearance.cjs +41 -0
- package/dist/cjs/appShell/BottomChromeClearance.native.js +45 -0
- package/dist/cjs/appShell/BottomChromeClearance.native.js.map +1 -0
- package/dist/cjs/appShell/bottomChrome.cjs +1 -12
- package/dist/cjs/appShell/bottomChrome.native.js +1 -13
- package/dist/cjs/appShell/bottomChrome.native.js.map +1 -1
- package/dist/cjs/appShell/index.cjs +3 -1
- package/dist/cjs/appShell/index.native.js +2 -0
- package/dist/cjs/appShell/index.native.js.map +1 -1
- package/dist/cjs/code/CodeBlock/index.cjs +38 -31
- package/dist/cjs/code/CodeBlock/index.native.js +38 -31
- package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
- package/dist/cjs/layouts/EmptyState.cjs +9 -43
- package/dist/cjs/layouts/EmptyState.native.js +9 -45
- package/dist/cjs/layouts/EmptyState.native.js.map +1 -1
- package/dist/cjs/layouts/barTitleSpan.cjs +54 -0
- package/dist/cjs/layouts/barTitleSpan.native.js +78 -0
- package/dist/cjs/layouts/barTitleSpan.native.js.map +1 -0
- package/dist/cjs/layouts/page.cjs +134 -49
- package/dist/cjs/layouts/page.native.js +147 -53
- package/dist/cjs/layouts/page.native.js.map +1 -1
- package/dist/esm/Accordion/index.native.js.map +1 -1
- package/dist/esm/AnimateHeight/index.native.js.map +1 -1
- package/dist/esm/Avatar/getInitials.native.js.map +1 -1
- package/dist/esm/Avatar/index.native.js.map +1 -1
- package/dist/esm/Badge/index.native.js.map +1 -1
- package/dist/esm/Button.native.js.map +1 -1
- package/dist/esm/Carousel/index.native.js.map +1 -1
- package/dist/esm/CheckGlyph/MenuCheckColumn.native.js.map +1 -1
- package/dist/esm/CheckGlyph/index.native.js.map +1 -1
- package/dist/esm/Chip/index.native.js.map +1 -1
- package/dist/esm/Collapsible.native.js.map +1 -1
- package/dist/esm/ContextMenu/index.native.js.map +1 -1
- package/dist/esm/CopyField/CopyMenuButton.native.js.map +1 -1
- package/dist/esm/CopyField/index.native.js.map +1 -1
- package/dist/esm/Dialog.native.js.map +1 -1
- package/dist/esm/Diff/diff.native.js.map +1 -1
- package/dist/esm/Diff/index.native.js.map +1 -1
- package/dist/esm/DotIndicator/index.native.js.map +1 -1
- package/dist/esm/DropdownMenu/index.native.js.map +1 -1
- package/dist/esm/DropdownMenu/menuSettle.native.js.map +1 -1
- package/dist/esm/ElapsedProgress/index.native.js.map +1 -1
- package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
- package/dist/esm/Heading.native.js.map +1 -1
- package/dist/esm/Kbd/index.native.js.map +1 -1
- package/dist/esm/Kbd/keys.native.js.map +1 -1
- package/dist/esm/Lightbox/MediaControl.native.js.map +1 -1
- package/dist/esm/Lightbox/index.gnome.native.js.map +1 -1
- package/dist/esm/Lightbox/shared.native.js.map +1 -1
- package/dist/esm/Lightbox/useLightbox.native.js.map +1 -1
- package/dist/esm/MachineLiteral/index.native.js.map +1 -1
- package/dist/esm/Material/index.native.js.map +1 -1
- package/dist/esm/Meter/index.native.js.map +1 -1
- package/dist/esm/Meter/meter.native.js.map +1 -1
- package/dist/esm/OverflowRow/index.native.js.map +1 -1
- package/dist/esm/Pagination.native.js.map +1 -1
- package/dist/esm/Popover.native.js.map +1 -1
- package/dist/esm/ProgressSteps/index.native.js.map +1 -1
- package/dist/esm/Quote/index.native.js.map +1 -1
- package/dist/esm/SchemeToggle/index.native.js.map +1 -1
- package/dist/esm/ScrollView/revealFocusedField.native.js.map +1 -1
- package/dist/esm/Separator/index.native.js.map +1 -1
- package/dist/esm/Sheet.native.js.map +1 -1
- package/dist/esm/SheetModal/SheetHeader.native.js.map +1 -1
- package/dist/esm/SheetModal/SheetNavigation.native.js.map +1 -1
- package/dist/esm/SheetModal/detents.native.js.map +1 -1
- package/dist/esm/SheetModal/useSheetFrameFocus.native.js.map +1 -1
- package/dist/esm/Skeleton/index.native.js.map +1 -1
- package/dist/esm/SkipLink/index.native.js.map +1 -1
- package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
- package/dist/esm/SortableList/index.native.js.map +1 -1
- package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
- package/dist/esm/SortableList/useUserOrder.native.js.map +1 -1
- package/dist/esm/Tabs/index.native.js.map +1 -1
- package/dist/esm/Text.native.js.map +1 -1
- package/dist/esm/ThemePresetPicker/index.native.js.map +1 -1
- package/dist/esm/Toast/index.native.js.map +1 -1
- package/dist/esm/Toast/store.native.js.map +1 -1
- package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
- package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
- package/dist/esm/TreeSelect/index.native.js.map +1 -1
- package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
- package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
- package/dist/esm/Video/VideoFrame.native.js.map +1 -1
- package/dist/esm/Video/types.native.js.map +1 -1
- package/dist/esm/Wheel/index.native.js.map +1 -1
- package/dist/esm/Wizard/index.native.js.map +1 -1
- package/dist/esm/actions/ActionBar.native.js.map +1 -1
- package/dist/esm/actions/DangerSection.native.js.map +1 -1
- package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
- package/dist/esm/actions/HeaderActions.native.js.map +1 -1
- package/dist/esm/actions/StandardAction.native.js.map +1 -1
- package/dist/esm/actions/devWarn.native.js.map +1 -1
- package/dist/esm/actions/index.native.js.map +1 -1
- package/dist/esm/actions/severity.native.js.map +1 -1
- package/dist/esm/appShell/AppShell.mjs +2 -10
- package/dist/esm/appShell/AppShell.mjs.map +1 -1
- package/dist/esm/appShell/AppShell.native.js +2 -10
- package/dist/esm/appShell/AppShell.native.js.map +1 -1
- package/dist/esm/appShell/BottomChromeClearance.mjs +16 -0
- package/dist/esm/appShell/BottomChromeClearance.mjs.map +1 -0
- package/dist/esm/appShell/BottomChromeClearance.native.js +17 -0
- package/dist/esm/appShell/BottomChromeClearance.native.js.map +1 -0
- package/dist/esm/appShell/Dock.native.js.map +1 -1
- package/dist/esm/appShell/MpoTabHost.native.js.map +1 -1
- package/dist/esm/appShell/ScopeMenu.native.js.map +1 -1
- package/dist/esm/appShell/SettingsSplit.native.js.map +1 -1
- package/dist/esm/appShell/Springboard.native.js.map +1 -1
- package/dist/esm/appShell/SystemBar.native.js.map +1 -1
- package/dist/esm/appShell/TabBar.native.js.map +1 -1
- package/dist/esm/appShell/appTile.native.js.map +1 -1
- package/dist/esm/appShell/bottomChrome.mjs +3 -13
- package/dist/esm/appShell/bottomChrome.mjs.map +1 -1
- package/dist/esm/appShell/bottomChrome.native.js +3 -14
- package/dist/esm/appShell/bottomChrome.native.js.map +1 -1
- package/dist/esm/appShell/index.mjs +2 -1
- package/dist/esm/appShell/index.mjs.map +1 -1
- package/dist/esm/appShell/index.native.js +2 -1
- package/dist/esm/appShell/index.native.js.map +1 -1
- package/dist/esm/charts/Axis.native.js.map +1 -1
- package/dist/esm/charts/BarChart.native.js.map +1 -1
- package/dist/esm/charts/ChartDataView.native.js.map +1 -1
- package/dist/esm/charts/ChartSurface.native.js.map +1 -1
- package/dist/esm/charts/LineChart.native.js.map +1 -1
- package/dist/esm/charts/PieChart.native.js.map +1 -1
- package/dist/esm/charts/Sparkline.native.js.map +1 -1
- package/dist/esm/charts/accessibility.native.js.map +1 -1
- package/dist/esm/charts/chartHeight.native.js.map +1 -1
- package/dist/esm/charts/composeChart.native.js.map +1 -1
- package/dist/esm/charts/index.native.js.map +1 -1
- package/dist/esm/charts/math.native.js.map +1 -1
- package/dist/esm/charts/series.native.js.map +1 -1
- package/dist/esm/charts/svg.native.js.map +1 -1
- package/dist/esm/charts/types.native.js.map +1 -1
- package/dist/esm/charts/useChartTheme.native.js.map +1 -1
- package/dist/esm/code/Code/index.native.js.map +1 -1
- package/dist/esm/code/CodeBlock/index.mjs +39 -32
- package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
- package/dist/esm/code/CodeBlock/index.native.js +39 -32
- package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
- package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
- package/dist/esm/code/Highlighter/houseSyntaxTheme.native.js.map +1 -1
- package/dist/esm/code/Highlighter/index.native.js.map +1 -1
- package/dist/esm/code/Highlighter/namedGroupMatchAll.native.js.map +1 -1
- package/dist/esm/code/LogView.native.js.map +1 -1
- package/dist/esm/code/Pre/index.native.js.map +1 -1
- package/dist/esm/code/index.native.js.map +1 -1
- package/dist/esm/componentColors.native.js.map +1 -1
- package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
- package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
- package/dist/esm/feedback/copy.native.js.map +1 -1
- package/dist/esm/feedback/index.native.js.map +1 -1
- package/dist/esm/feedback/notify.native.js.map +1 -1
- package/dist/esm/feedback/route.native.js.map +1 -1
- package/dist/esm/feedback/store.native.js.map +1 -1
- package/dist/esm/formLayouts/FormLayouts.native.js.map +1 -1
- package/dist/esm/formLayouts/PreferencePanel.native.js.map +1 -1
- package/dist/esm/formLayouts/index.native.js.map +1 -1
- package/dist/esm/hooks/index.native.js.map +1 -1
- package/dist/esm/hooks/useClipboard.native.js.map +1 -1
- package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
- package/dist/esm/hooks/useHoldCue.native.js.map +1 -1
- package/dist/esm/icons/CheckRegular.native.js.map +1 -1
- package/dist/esm/icons/MinusRegular.native.js.map +1 -1
- package/dist/esm/icons/index.native.js.map +1 -1
- package/dist/esm/icons/lucide.native.js.map +1 -1
- package/dist/esm/icons/themed.native.js.map +1 -1
- package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
- package/dist/esm/images/Image/framing.native.js.map +1 -1
- package/dist/esm/images/Image/mediaRadius.native.js.map +1 -1
- package/dist/esm/images/Image/shared.native.js.map +1 -1
- package/dist/esm/images/Image/types.native.js.map +1 -1
- package/dist/esm/images/index.native.js.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/knobImmuneExports.native.js.map +1 -1
- package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
- package/dist/esm/layouts/Debug/index.native.js.map +1 -1
- package/dist/esm/layouts/EmptyState.mjs +9 -43
- package/dist/esm/layouts/EmptyState.mjs.map +1 -1
- package/dist/esm/layouts/EmptyState.native.js +9 -45
- package/dist/esm/layouts/EmptyState.native.js.map +1 -1
- package/dist/esm/layouts/IdentityPlate.native.js.map +1 -1
- package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
- package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
- package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
- package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
- package/dist/esm/layouts/StatusDot.native.js.map +1 -1
- package/dist/esm/layouts/StatusGlyphStrip.native.js.map +1 -1
- package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
- package/dist/esm/layouts/attachments.native.js.map +1 -1
- package/dist/esm/layouts/barTitleSpan.mjs +27 -0
- package/dist/esm/layouts/barTitleSpan.mjs.map +1 -0
- package/dist/esm/layouts/barTitleSpan.native.js +48 -0
- package/dist/esm/layouts/barTitleSpan.native.js.map +1 -0
- package/dist/esm/layouts/bottomAccessory.native.js.map +1 -1
- package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
- package/dist/esm/layouts/index.native.js.map +1 -1
- package/dist/esm/layouts/page.mjs +136 -51
- package/dist/esm/layouts/page.mjs.map +1 -1
- package/dist/esm/layouts/page.native.js +149 -55
- package/dist/esm/layouts/page.native.js.map +1 -1
- package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
- package/dist/esm/layouts/screenScroll.native.js.map +1 -1
- package/dist/esm/layouts/shellSidebarSlot.native.js.map +1 -1
- package/dist/esm/layouts/sidebar.native.js.map +1 -1
- package/dist/esm/layouts/tags.native.js.map +1 -1
- package/dist/esm/layouts/timeline.native.js.map +1 -1
- package/dist/esm/layouts/toolbar.native.js.map +1 -1
- package/dist/esm/shared/actionScan.native.js.map +1 -1
- package/dist/esm/shared/i18n.native.js.map +1 -1
- package/dist/esm/shared/restoreRetries.native.js.map +1 -1
- package/dist/esm/shared/t.native.js.map +1 -1
- package/dist/esm/shared/textRidesText.native.js.map +1 -1
- package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
- package/dist/esm/surfaces.native.js.map +1 -1
- package/dist/esm/tamagui.native.js.map +1 -1
- package/dist/esm/testSetup.native.js.map +1 -1
- package/dist/esm/utils/setTinted.native.js.map +1 -1
- package/dist/esm/utils/unwrapText.native.js.map +1 -1
- package/dist/esm/views/Calendar.native.js.map +1 -1
- package/dist/esm/views/Dashboard.native.js.map +1 -1
- package/dist/esm/views/Gantt.native.js.map +1 -1
- package/dist/esm/views/GanttDateGrip.native.js.map +1 -1
- package/dist/esm/views/ImageGrid.native.js.map +1 -1
- package/dist/esm/views/List.native.js.map +1 -1
- package/dist/esm/views/ListGroup.native.js.map +1 -1
- package/dist/esm/views/ListRow.native.js.map +1 -1
- package/dist/esm/views/Matrix.native.js.map +1 -1
- package/dist/esm/views/ReportBuilder.native.js.map +1 -1
- package/dist/esm/views/ShortcutCard.native.js.map +1 -1
- package/dist/esm/views/TreeView.native.js.map +1 -1
- package/dist/esm/views/calendarMath.native.js.map +1 -1
- package/dist/esm/views/ganttIntervals.native.js.map +1 -1
- package/dist/esm/views/ganttMath.native.js.map +1 -1
- package/dist/esm/views/index.native.js.map +1 -1
- package/dist/esm/views/kanbanDnd.native.js.map +1 -1
- package/dist/esm/views/matrixMath.native.js.map +1 -1
- package/dist/esm/views/sameNameScope.native.js.map +1 -1
- package/dist/esm/views/treeViewDnd.native.js.map +1 -1
- package/dist/esm/views/treeViewLayout.native.js.map +1 -1
- package/dist/esm/views/treeViewMotion.native.js.map +1 -1
- package/dist/esm/views/treeViewReveal.native.js.map +1 -1
- package/dist/esm/views/usePageFlowLoadMore.native.js.map +1 -1
- package/dist/esm/views/widgetChrome.native.js.map +1 -1
- package/dist/jsx/appShell/AppShell.mjs +2 -10
- package/dist/jsx/appShell/AppShell.mjs.map +1 -1
- package/dist/jsx/appShell/AppShell.native.js +1 -9
- package/dist/jsx/appShell/AppShell.native.js.map +1 -1
- package/dist/jsx/appShell/BottomChromeClearance.mjs +16 -0
- package/dist/jsx/appShell/BottomChromeClearance.mjs.map +1 -0
- package/dist/jsx/appShell/BottomChromeClearance.native.js +45 -0
- package/dist/jsx/appShell/BottomChromeClearance.native.js.map +1 -0
- package/dist/jsx/appShell/bottomChrome.mjs +3 -13
- package/dist/jsx/appShell/bottomChrome.mjs.map +1 -1
- package/dist/jsx/appShell/bottomChrome.native.js +1 -13
- package/dist/jsx/appShell/bottomChrome.native.js.map +1 -1
- package/dist/jsx/appShell/index.mjs +2 -1
- package/dist/jsx/appShell/index.mjs.map +1 -1
- package/dist/jsx/appShell/index.native.js +2 -0
- package/dist/jsx/appShell/index.native.js.map +1 -1
- package/dist/jsx/code/CodeBlock/index.mjs +39 -32
- package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
- package/dist/jsx/code/CodeBlock/index.native.js +38 -31
- package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
- package/dist/jsx/layouts/EmptyState.mjs +9 -43
- package/dist/jsx/layouts/EmptyState.mjs.map +1 -1
- package/dist/jsx/layouts/EmptyState.native.js +9 -45
- package/dist/jsx/layouts/EmptyState.native.js.map +1 -1
- package/dist/jsx/layouts/barTitleSpan.mjs +27 -0
- package/dist/jsx/layouts/barTitleSpan.mjs.map +1 -0
- package/dist/jsx/layouts/barTitleSpan.native.js +78 -0
- package/dist/jsx/layouts/barTitleSpan.native.js.map +1 -0
- package/dist/jsx/layouts/page.mjs +136 -51
- package/dist/jsx/layouts/page.mjs.map +1 -1
- package/dist/jsx/layouts/page.native.js +147 -53
- package/dist/jsx/layouts/page.native.js.map +1 -1
- package/package.json +9 -9
- package/src/appShell/AppShell.spec.tsx +11 -7
- package/src/appShell/AppShell.tsx +2 -9
- package/src/appShell/BottomChromeClearance.spec.tsx +42 -0
- package/src/appShell/BottomChromeClearance.tsx +15 -0
- package/src/appShell/bottomChrome.ts +3 -24
- package/src/appShell/index.ts +1 -0
- package/src/code/CodeBlock/CodeBlock.copy.spec.tsx +13 -0
- package/src/code/CodeBlock/index.tsx +46 -30
- package/src/layouts/EmptyState.spec.tsx +2 -0
- package/src/layouts/EmptyState.tsx +6 -39
- package/src/layouts/PageHeader.stories.tsx +238 -38
- package/src/layouts/barTitleSpan.spec.ts +35 -0
- package/src/layouts/barTitleSpan.ts +44 -0
- package/src/layouts/glyphCentre.spec.tsx +218 -0
- package/src/layouts/navBar.spec.tsx +58 -2
- package/src/layouts/page.tsx +128 -45
- package/types/appShell/AppShell.d.ts.map +1 -1
- package/types/appShell/BottomChromeClearance.d.ts +10 -0
- package/types/appShell/BottomChromeClearance.d.ts.map +1 -0
- package/types/appShell/bottomChrome.d.ts +1 -11
- package/types/appShell/bottomChrome.d.ts.map +1 -1
- package/types/appShell/index.d.ts +1 -0
- package/types/appShell/index.d.ts.map +1 -1
- package/types/code/CodeBlock/index.d.ts.map +1 -1
- package/types/layouts/EmptyState.d.ts.map +1 -1
- package/types/layouts/barTitleSpan.d.ts +29 -0
- package/types/layouts/barTitleSpan.d.ts.map +1 -0
- package/types/layouts/page.d.ts +6 -1
- package/types/layouts/page.d.ts.map +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LC-176 (FZ-8442): a glyph on a glass circle sits at the circle's centre.
|
|
3
|
+
* Clay on the Kitchen Sink SHC Nodes story: "the icons inside of these are
|
|
4
|
+
* aligned wrong". The back caret was pulled toward the bar's edge and the "+"
|
|
5
|
+
* pushed past its circle's centre.
|
|
6
|
+
*
|
|
7
|
+
* jsdom has no layout, so the ink centre is computed: the offset each box
|
|
8
|
+
* between the glyph and its circle adds (a margin moves a centred child by
|
|
9
|
+
* half of it, a translate by all of it), plus where the glyph's ink sits in
|
|
10
|
+
* its 256-unit Phosphor grid, read from the rendered path.
|
|
11
|
+
*/
|
|
12
|
+
import { IconButton } from "@multiplatform.one/forms";
|
|
13
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
14
|
+
import { Surface } from "@multiplatform.one/theme";
|
|
15
|
+
import { PlusIcon, XIcon } from "@phosphor-icons/react";
|
|
16
|
+
import { cleanup, screen } from "@testing-library/react";
|
|
17
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
18
|
+
import { PageBackButton, ScreenToolbar } from "./page";
|
|
19
|
+
|
|
20
|
+
const IPHONE =
|
|
21
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 26_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1";
|
|
22
|
+
const GRID = 256;
|
|
23
|
+
const TOLERANCE_PT = 0.5;
|
|
24
|
+
|
|
25
|
+
type Box = { minX: number; maxX: number; minY: number; maxY: number };
|
|
26
|
+
|
|
27
|
+
function arcExtremes(
|
|
28
|
+
box: Box,
|
|
29
|
+
from: [number, number],
|
|
30
|
+
r: number,
|
|
31
|
+
large: number,
|
|
32
|
+
sweep: number,
|
|
33
|
+
to: [number, number],
|
|
34
|
+
) {
|
|
35
|
+
const [x1, y1] = from;
|
|
36
|
+
const [x2, y2] = to;
|
|
37
|
+
const mx = (x1 - x2) / 2;
|
|
38
|
+
const my = (y1 - y2) / 2;
|
|
39
|
+
const d2 = mx * mx + my * my;
|
|
40
|
+
const radius = Math.max(r, Math.sqrt(d2));
|
|
41
|
+
const k = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, radius ** 2 / d2 - 1));
|
|
42
|
+
const cx = k * my + (x1 + x2) / 2;
|
|
43
|
+
const cy = -k * mx + (y1 + y2) / 2;
|
|
44
|
+
const start = Math.atan2(y1 - cy, x1 - cx);
|
|
45
|
+
let delta = Math.atan2(y2 - cy, x2 - cx) - start;
|
|
46
|
+
if (sweep && delta < 0) delta += 2 * Math.PI;
|
|
47
|
+
if (!sweep && delta > 0) delta -= 2 * Math.PI;
|
|
48
|
+
for (const angle of [0, Math.PI / 2, Math.PI, -Math.PI / 2]) {
|
|
49
|
+
let t = angle - start;
|
|
50
|
+
while (t < 0) t += 2 * Math.PI;
|
|
51
|
+
while (t >= 2 * Math.PI) t -= 2 * Math.PI;
|
|
52
|
+
const within = delta >= 0 ? t <= delta : t >= 2 * Math.PI + delta || t === 0;
|
|
53
|
+
if (within) include(box, cx + radius * Math.cos(angle), cy + radius * Math.sin(angle));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function include(box: Box, x: number, y: number) {
|
|
58
|
+
box.minX = Math.min(box.minX, x);
|
|
59
|
+
box.maxX = Math.max(box.maxX, x);
|
|
60
|
+
box.minY = Math.min(box.minY, y);
|
|
61
|
+
box.maxY = Math.max(box.maxY, y);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The ink box of an SVG path made of the commands Phosphor uses. */
|
|
65
|
+
function inkBox(d: string): Box {
|
|
66
|
+
const box: Box = { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity };
|
|
67
|
+
const tokens = d.match(/[a-zA-Z]|-?(?:\d*\.\d+|\d+)(?:e-?\d+)?/g) ?? [];
|
|
68
|
+
let i = 0;
|
|
69
|
+
let command = "";
|
|
70
|
+
let x = 0;
|
|
71
|
+
let y = 0;
|
|
72
|
+
let startX = 0;
|
|
73
|
+
let startY = 0;
|
|
74
|
+
const num = () => Number(tokens[i++]);
|
|
75
|
+
while (i < tokens.length) {
|
|
76
|
+
if (/[a-zA-Z]/.test(tokens[i]!)) command = tokens[i++]!;
|
|
77
|
+
const rel = command === command.toLowerCase();
|
|
78
|
+
const ox = rel ? x : 0;
|
|
79
|
+
const oy = rel ? y : 0;
|
|
80
|
+
switch (command.toUpperCase()) {
|
|
81
|
+
case "M":
|
|
82
|
+
x = ox + num();
|
|
83
|
+
y = oy + num();
|
|
84
|
+
startX = x;
|
|
85
|
+
startY = y;
|
|
86
|
+
include(box, x, y);
|
|
87
|
+
command = rel ? "l" : "L";
|
|
88
|
+
break;
|
|
89
|
+
case "L":
|
|
90
|
+
x = ox + num();
|
|
91
|
+
y = oy + num();
|
|
92
|
+
include(box, x, y);
|
|
93
|
+
break;
|
|
94
|
+
case "H":
|
|
95
|
+
x = ox + num();
|
|
96
|
+
include(box, x, y);
|
|
97
|
+
break;
|
|
98
|
+
case "V":
|
|
99
|
+
y = oy + num();
|
|
100
|
+
include(box, x, y);
|
|
101
|
+
break;
|
|
102
|
+
case "C": {
|
|
103
|
+
for (let n = 0; n < 2; n++) include(box, ox + num(), oy + num());
|
|
104
|
+
x = ox + num();
|
|
105
|
+
y = oy + num();
|
|
106
|
+
include(box, x, y);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case "A": {
|
|
110
|
+
const r = num();
|
|
111
|
+
num();
|
|
112
|
+
num();
|
|
113
|
+
const large = num();
|
|
114
|
+
const sweep = num();
|
|
115
|
+
const to: [number, number] = [ox + num(), oy + num()];
|
|
116
|
+
arcExtremes(box, [x, y], r, large, sweep, to);
|
|
117
|
+
[x, y] = to;
|
|
118
|
+
include(box, x, y);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case "Z":
|
|
122
|
+
x = startX;
|
|
123
|
+
y = startY;
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
throw new Error(`inkBox: unhandled path command ${command}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return box;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const px = (value: string | undefined) => Number.parseFloat(value || "0") || 0;
|
|
133
|
+
|
|
134
|
+
function translate(transform: string, axis: "X" | "Y") {
|
|
135
|
+
const one = transform.match(new RegExp(`translate${axis}\\((-?[\\d.]+)px\\)`));
|
|
136
|
+
if (one) return Number(one[1]);
|
|
137
|
+
const both = transform.match(/translate\((-?[\d.]+)px(?:,\s*(-?[\d.]+)px)?\)/);
|
|
138
|
+
if (!both) return 0;
|
|
139
|
+
return axis === "X" ? Number(both[1]) : Number(both[2] ?? 0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** How far the glyph's ink centre sits from its circle's centre, in pt. */
|
|
143
|
+
function inkOffset(button: HTMLElement) {
|
|
144
|
+
const svg = button.querySelector("svg");
|
|
145
|
+
if (!svg) throw new Error("no glyph");
|
|
146
|
+
const size = Number(svg.getAttribute("width"));
|
|
147
|
+
const scale = size / GRID;
|
|
148
|
+
const box = inkBox(
|
|
149
|
+
Array.from(svg.querySelectorAll("path"))
|
|
150
|
+
.map((path) => path.getAttribute("d") ?? "")
|
|
151
|
+
.join(" "),
|
|
152
|
+
);
|
|
153
|
+
let dx = ((box.minX + box.maxX) / 2 - GRID / 2) * scale;
|
|
154
|
+
let dy = ((box.minY + box.maxY) / 2 - GRID / 2) * scale;
|
|
155
|
+
const frame = getComputedStyle(button);
|
|
156
|
+
dx += (px(frame.paddingLeft) - px(frame.paddingRight)) / 2;
|
|
157
|
+
dy += (px(frame.paddingTop) - px(frame.paddingBottom)) / 2;
|
|
158
|
+
for (let el: Element | null = svg; el && el !== button; el = el.parentElement) {
|
|
159
|
+
const style = getComputedStyle(el);
|
|
160
|
+
dx +=
|
|
161
|
+
(px(style.marginLeft) +
|
|
162
|
+
px(style.marginInlineStart) -
|
|
163
|
+
px(style.marginRight) -
|
|
164
|
+
px(style.marginInlineEnd)) /
|
|
165
|
+
2 +
|
|
166
|
+
translate(style.transform, "X");
|
|
167
|
+
dy += (px(style.marginTop) - px(style.marginBottom)) / 2 + translate(style.transform, "Y");
|
|
168
|
+
}
|
|
169
|
+
return { dx, dy, size };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
afterEach(() => {
|
|
173
|
+
vi.unstubAllGlobals();
|
|
174
|
+
cleanup();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("glyph centre == glass circle centre (LC-176, FZ-8442)", () => {
|
|
178
|
+
it("the ink box reads Phosphor's bold glyphs where they draw", () => {
|
|
179
|
+
const plus = inkBox(
|
|
180
|
+
"M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z",
|
|
181
|
+
);
|
|
182
|
+
expect([plus.minX, plus.maxX, plus.minY, plus.maxY].map(Math.round)).toEqual([
|
|
183
|
+
28, 228, 28, 228,
|
|
184
|
+
]);
|
|
185
|
+
const caret = inkBox(
|
|
186
|
+
"M168.49,199.51a12,12,0,0,1-17,17l-80-80a12,12,0,0,1,0-17l80-80a12,12,0,0,1,17,17L97,128Z",
|
|
187
|
+
);
|
|
188
|
+
expect(Math.round((caret.minX + caret.maxX) / 2)).toBe(120);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("off glass the back caret keeps its optical pull, so the measure sees margins", () => {
|
|
192
|
+
renderWithProviders(<ScreenToolbar leading={<PageBackButton />} />);
|
|
193
|
+
const back = screen.getByLabelText("Back");
|
|
194
|
+
expect(back.getAttribute("data-mp-icon-button")).not.toBe("glass");
|
|
195
|
+
const { dx, size } = inkOffset(back);
|
|
196
|
+
expect(dx).toBeLessThan(-size * 0.05);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("back caret, + and ✕ sit at their circle's centre within 0.5pt on iPhone", () => {
|
|
200
|
+
vi.stubGlobal("navigator", { userAgent: IPHONE, platform: "iPhone", maxTouchPoints: 5 });
|
|
201
|
+
renderWithProviders(
|
|
202
|
+
<Surface bar>
|
|
203
|
+
<ScreenToolbar
|
|
204
|
+
leading={<PageBackButton />}
|
|
205
|
+
trailing={<IconButton icon={PlusIcon} aria-label="Add node" edge="end" />}
|
|
206
|
+
/>
|
|
207
|
+
<IconButton icon={XIcon} aria-label="Close" edge="start" />
|
|
208
|
+
</Surface>,
|
|
209
|
+
);
|
|
210
|
+
for (const name of ["Back", "Add node", "Close"]) {
|
|
211
|
+
const button = screen.getByLabelText(name);
|
|
212
|
+
expect(button.getAttribute("data-mp-icon-button"), name).toBe("glass");
|
|
213
|
+
const { dx, dy } = inkOffset(button);
|
|
214
|
+
expect(Math.abs(dx), `${name} dx`).toBeLessThanOrEqual(TOLERANCE_PT);
|
|
215
|
+
expect(Math.abs(dy), `${name} dy`).toBeLessThanOrEqual(TOLERANCE_PT);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* jsdom has no layout: the large title's geometry is stubbed through
|
|
9
9
|
* getBoundingClientRect, and scrolling sets scrollTop on the scroll element.
|
|
10
10
|
*/
|
|
11
|
-
import { ICON_BUTTON_GLYPH_PX } from "@multiplatform.one/forms";
|
|
11
|
+
import { ICON_BUTTON_GLYPH_PX, IconButton } from "@multiplatform.one/forms";
|
|
12
12
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
13
13
|
import { Preset, useGlyphTintOnSurface } from "@multiplatform.one/theme";
|
|
14
|
-
import { SlidersHorizontalIcon } from "@phosphor-icons/react";
|
|
14
|
+
import { PlusIcon, SlidersHorizontalIcon } from "@phosphor-icons/react";
|
|
15
15
|
import { act, cleanup, fireEvent, screen } from "@testing-library/react";
|
|
16
16
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
17
17
|
import {
|
|
@@ -269,3 +269,59 @@ describe("bar glyphs carry their weight (LC-176)", () => {
|
|
|
269
269
|
expect(screen.getByLabelText("Back").closest("[data-mp-icon-button-group]")).toBeNull();
|
|
270
270
|
});
|
|
271
271
|
});
|
|
272
|
+
|
|
273
|
+
const IPHONE =
|
|
274
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 26_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1";
|
|
275
|
+
|
|
276
|
+
describe("inline bar title and glyphs on glass (FZ-8442)", () => {
|
|
277
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
278
|
+
|
|
279
|
+
it("a title is centred in the flow toolbar and shown at rest, clear of the back control", () => {
|
|
280
|
+
renderWithProviders(
|
|
281
|
+
<ScreenToolbar
|
|
282
|
+
leading={<PageBackButton />}
|
|
283
|
+
title="Inbox"
|
|
284
|
+
trailing={<PageBarButton icon={SlidersHorizontalIcon} aria-label="Filter" />}
|
|
285
|
+
/>,
|
|
286
|
+
);
|
|
287
|
+
const title = document.querySelector("[data-bar-title]") as HTMLElement;
|
|
288
|
+
expect(title.textContent).toBe("Inbox");
|
|
289
|
+
expect(title.getAttribute("aria-hidden")).not.toBe("true");
|
|
290
|
+
const back = screen.getByLabelText("Back");
|
|
291
|
+
expect(title.contains(back)).toBe(false);
|
|
292
|
+
expect(back.parentElement?.contains(title)).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("the pinned bar shows a given title at rest", () => {
|
|
296
|
+
renderWithProviders(
|
|
297
|
+
<Screen topBar={<ScreenToolbar leading={<PageBackButton />} title="Inbox" />}>
|
|
298
|
+
<div style={{ height: 2000 }} />
|
|
299
|
+
</Screen>,
|
|
300
|
+
);
|
|
301
|
+
const title = document.querySelector("[data-top-bar] [data-bar-title]") as HTMLElement;
|
|
302
|
+
expect(title.textContent).toBe("Inbox");
|
|
303
|
+
expect(title.getAttribute("aria-hidden")).not.toBe("true");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("on glass the back caret drops its pull and a lone + keeps its own circle", () => {
|
|
307
|
+
vi.stubGlobal("navigator", { userAgent: IPHONE, platform: "iPhone", maxTouchPoints: 5 });
|
|
308
|
+
renderWithProviders(
|
|
309
|
+
<Screen topBar={<ScreenToolbar leading={<PageBackButton />} />}>
|
|
310
|
+
<PageHeader
|
|
311
|
+
title="Nodes"
|
|
312
|
+
actions={<IconButton icon={PlusIcon} aria-label="Add node" edge="end" />}
|
|
313
|
+
/>
|
|
314
|
+
</Screen>,
|
|
315
|
+
);
|
|
316
|
+
const back = screen.getByLabelText("Back");
|
|
317
|
+
expect(back.getAttribute("data-mp-icon-button")).toBe("glass");
|
|
318
|
+
const caret = back.querySelector("svg")!;
|
|
319
|
+
const pull = Math.round(Number(caret.getAttribute("width")) * (72 / 256));
|
|
320
|
+
expect(Array.from(caret.parentElement!.classList).some((c) => c.includes(`-${pull}px`))).toBe(
|
|
321
|
+
false,
|
|
322
|
+
);
|
|
323
|
+
const add = screen.getByLabelText("Add node");
|
|
324
|
+
expect(add.getAttribute("data-mp-icon-button")).toBe("glass");
|
|
325
|
+
expect(add.closest("[data-mp-glass-capsule]")).toBeNull();
|
|
326
|
+
});
|
|
327
|
+
});
|
package/src/layouts/page.tsx
CHANGED
|
@@ -33,9 +33,15 @@ import {
|
|
|
33
33
|
type PageTitleScale,
|
|
34
34
|
type ParentScreen,
|
|
35
35
|
} from "@multiplatform.one/theme";
|
|
36
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
Button,
|
|
38
|
+
IconButtonGroup,
|
|
39
|
+
useGlyphOnPlate,
|
|
40
|
+
type ButtonProps,
|
|
41
|
+
} from "@multiplatform.one/forms";
|
|
37
42
|
import { CaretLeftIcon, CaretRightIcon, DotsThreeIcon } from "@phosphor-icons/react";
|
|
38
43
|
import { useState, type ReactNode } from "react";
|
|
44
|
+
import { barTitleSpan, sideExtents } from "./barTitleSpan";
|
|
39
45
|
import type { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent } from "react-native";
|
|
40
46
|
import type { ScrollViewProps as TamaguiScrollViewProps, XStackProps, YStackProps } from "tamagui";
|
|
41
47
|
import {
|
|
@@ -52,7 +58,7 @@ import {
|
|
|
52
58
|
YStack,
|
|
53
59
|
isWeb,
|
|
54
60
|
} from "tamagui";
|
|
55
|
-
import {
|
|
61
|
+
import { BottomChromeClearance } from "../appShell/BottomChromeClearance";
|
|
56
62
|
import { componentColors, sectionHeading } from "../componentColors";
|
|
57
63
|
import { useDirection } from "../hooks/useDirection";
|
|
58
64
|
import { useTranslation } from "../shared/i18n";
|
|
@@ -280,7 +286,6 @@ function PageShell({
|
|
|
280
286
|
const scrollState = useScreenScrollController();
|
|
281
287
|
useReportScreenScroll(scrollState.state, Boolean(topBar));
|
|
282
288
|
const [accessories] = useState(createBottomAccessoryStore);
|
|
283
|
-
const chromeInset = useBottomChromeInset();
|
|
284
289
|
const inset = noPadding ? null : (knobProps.panelPadding.padding as string);
|
|
285
290
|
const cap = maxWidth && !bleed ? maxWidth : null;
|
|
286
291
|
|
|
@@ -330,15 +335,7 @@ function PageShell({
|
|
|
330
335
|
|
|
331
336
|
// The floating tab bar covers the bottom of the screen; content scrolls
|
|
332
337
|
// under it, and this clears its last row.
|
|
333
|
-
const chromeClearance =
|
|
334
|
-
chromeInset > 0 ? (
|
|
335
|
-
<YStack
|
|
336
|
-
height={chromeInset}
|
|
337
|
-
flexShrink={0}
|
|
338
|
-
aria-hidden
|
|
339
|
-
data-testid="screen-chrome-clearance"
|
|
340
|
-
/>
|
|
341
|
-
) : null;
|
|
338
|
+
const chromeClearance = <BottomChromeClearance testID="screen-chrome-clearance" />;
|
|
342
339
|
|
|
343
340
|
if (!scroll) {
|
|
344
341
|
return shell(
|
|
@@ -655,11 +652,94 @@ export { EmptyState, type EmptyStateProps } from "./EmptyState";
|
|
|
655
652
|
// ScreenToolbar — minimal back/edit-style screen toolbar
|
|
656
653
|
// ---------------------------------------------------------------------------
|
|
657
654
|
|
|
658
|
-
export interface ScreenToolbarProps extends XStackProps {
|
|
655
|
+
export interface ScreenToolbarProps extends Omit<XStackProps, "title"> {
|
|
659
656
|
/** Leading content (typically a back button). */
|
|
660
657
|
leading?: ReactNode;
|
|
661
658
|
/** Trailing content (typically primary/secondary actions). */
|
|
662
659
|
trailing?: ReactNode;
|
|
660
|
+
/**
|
|
661
|
+
* The bar's inline title (the iOS inline navigation title): centred in the
|
|
662
|
+
* bar, clear of the wider side group, never set against the back control.
|
|
663
|
+
*/
|
|
664
|
+
title?: string;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function useBarTitleSpan() {
|
|
668
|
+
const none = { x: 0, width: 0 };
|
|
669
|
+
const [box, setBox] = useState({ row: 0, leading: none, trailing: none, title: 0 });
|
|
670
|
+
const set = <K extends keyof typeof box>(key: K, value: (typeof box)[K]) =>
|
|
671
|
+
setBox((prev) =>
|
|
672
|
+
JSON.stringify(prev[key]) === JSON.stringify(value) ? prev : { ...prev, [key]: value },
|
|
673
|
+
);
|
|
674
|
+
const extent = (event: LayoutChangeEvent) => {
|
|
675
|
+
const { x, width } = event.nativeEvent.layout;
|
|
676
|
+
return { x, width };
|
|
677
|
+
};
|
|
678
|
+
return {
|
|
679
|
+
span: barTitleSpan({
|
|
680
|
+
row: box.row,
|
|
681
|
+
title: box.title,
|
|
682
|
+
...sideExtents(box.row, box.leading, box.trailing),
|
|
683
|
+
}),
|
|
684
|
+
measureRow: (event: LayoutChangeEvent) => set("row", Math.floor(extent(event).width)),
|
|
685
|
+
measureLeading: (event: LayoutChangeEvent) => set("leading", extent(event)),
|
|
686
|
+
measureTrailing: (event: LayoutChangeEvent) => set("trailing", extent(event)),
|
|
687
|
+
measureTitle: (event: LayoutChangeEvent) => set("title", Math.ceil(extent(event).width)),
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function BarTitle({
|
|
692
|
+
title,
|
|
693
|
+
span,
|
|
694
|
+
onTitleWidth,
|
|
695
|
+
shown = true,
|
|
696
|
+
...rest
|
|
697
|
+
}: {
|
|
698
|
+
title: string;
|
|
699
|
+
span: { left: number; right: number };
|
|
700
|
+
onTitleWidth: (event: LayoutChangeEvent) => void;
|
|
701
|
+
shown?: boolean;
|
|
702
|
+
} & XStackProps) {
|
|
703
|
+
const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
|
|
704
|
+
const text = {
|
|
705
|
+
...knobProps.body,
|
|
706
|
+
fontWeight: "600",
|
|
707
|
+
color: "$color12",
|
|
708
|
+
numberOfLines: 1,
|
|
709
|
+
} as const;
|
|
710
|
+
return (
|
|
711
|
+
<>
|
|
712
|
+
{shown ? (
|
|
713
|
+
<XStack
|
|
714
|
+
position="absolute"
|
|
715
|
+
top={0}
|
|
716
|
+
left={0}
|
|
717
|
+
opacity={0}
|
|
718
|
+
pointerEvents="none"
|
|
719
|
+
aria-hidden
|
|
720
|
+
onLayout={onTitleWidth}
|
|
721
|
+
>
|
|
722
|
+
<SizableText {...text}>{title}</SizableText>
|
|
723
|
+
</XStack>
|
|
724
|
+
) : null}
|
|
725
|
+
<XStack
|
|
726
|
+
position="absolute"
|
|
727
|
+
top={0}
|
|
728
|
+
bottom={0}
|
|
729
|
+
left={span.left}
|
|
730
|
+
right={span.right}
|
|
731
|
+
alignItems="center"
|
|
732
|
+
justifyContent="center"
|
|
733
|
+
pointerEvents="none"
|
|
734
|
+
aria-hidden={!shown}
|
|
735
|
+
data-bar-title=""
|
|
736
|
+
opacity={shown ? 1 : 0}
|
|
737
|
+
{...rest}
|
|
738
|
+
>
|
|
739
|
+
<SizableText {...text}>{title}</SizableText>
|
|
740
|
+
</XStack>
|
|
741
|
+
</>
|
|
742
|
+
);
|
|
663
743
|
}
|
|
664
744
|
|
|
665
745
|
/**
|
|
@@ -679,8 +759,9 @@ export function ScreenToolbar(props: ScreenToolbarProps) {
|
|
|
679
759
|
return slot ? <PinnedTopBar {...props} slot={slot} /> : <InlineToolbar {...props} />;
|
|
680
760
|
}
|
|
681
761
|
|
|
682
|
-
function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarProps) {
|
|
762
|
+
function InlineToolbar({ leading, trailing, title, children, ...rest }: ScreenToolbarProps) {
|
|
683
763
|
const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
|
|
764
|
+
const titleSpan = useBarTitleSpan();
|
|
684
765
|
const insetToken = usePageChromeInset();
|
|
685
766
|
const bleed = insetToken ? { marginHorizontal: negateSpaceToken(insetToken) as "$4" } : null;
|
|
686
767
|
// LC-145: a phone top bar is one row; the leading group shrinks first.
|
|
@@ -692,8 +773,9 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
692
773
|
data-testid="screen-toolbar"
|
|
693
774
|
justifyContent="space-between"
|
|
694
775
|
alignItems="center"
|
|
695
|
-
flexWrap={oneRow ? "nowrap" : "wrap"}
|
|
776
|
+
flexWrap={oneRow || title ? "nowrap" : "wrap"}
|
|
696
777
|
minHeight={MIN_PRESS_TARGET}
|
|
778
|
+
{...(title ? { position: "relative" as const, onLayout: titleSpan.measureRow } : null)}
|
|
697
779
|
{...bleed}
|
|
698
780
|
{...(insetToken ? { paddingHorizontal: insetToken as "$4" } : knobProps.panelPadding)}
|
|
699
781
|
{...knobProps.gap}
|
|
@@ -706,6 +788,7 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
706
788
|
// Shrink weight, not a size: the back label gives way before the actions wrap.
|
|
707
789
|
flexShrink={oneRow ? 1000 : 1}
|
|
708
790
|
minWidth={0}
|
|
791
|
+
{...(title ? { onLayout: titleSpan.measureLeading } : null)}
|
|
709
792
|
>
|
|
710
793
|
{leading}
|
|
711
794
|
</XStack>
|
|
@@ -716,9 +799,13 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
716
799
|
flexShrink={1}
|
|
717
800
|
minWidth={0}
|
|
718
801
|
justifyContent="flex-end"
|
|
802
|
+
{...(title ? { onLayout: titleSpan.measureTrailing } : null)}
|
|
719
803
|
>
|
|
720
804
|
{trailing}
|
|
721
805
|
</IconButtonGroup>
|
|
806
|
+
{title ? (
|
|
807
|
+
<BarTitle title={title} span={titleSpan.span} onTitleWidth={titleSpan.measureTitle} />
|
|
808
|
+
) : null}
|
|
722
809
|
</XStack>
|
|
723
810
|
</Surface>
|
|
724
811
|
);
|
|
@@ -737,19 +824,16 @@ function InlineToolbar({ leading, trailing, children, ...rest }: ScreenToolbarPr
|
|
|
737
824
|
function PinnedTopBar({
|
|
738
825
|
leading,
|
|
739
826
|
trailing,
|
|
827
|
+
title: inlineTitle,
|
|
740
828
|
children,
|
|
741
829
|
slot,
|
|
742
830
|
...rest
|
|
743
831
|
}: ScreenToolbarProps & { slot: { inset: string | null; maxWidth: number | null } }) {
|
|
744
832
|
const { knobProps } = useResolvedKnobs({ component: "ScreenToolbar" });
|
|
745
|
-
const { scrolled, collapsed, title } = useScreenScroll();
|
|
746
|
-
const
|
|
747
|
-
const measureSide = (side: "leading" | "trailing") => (event: LayoutChangeEvent) => {
|
|
748
|
-
const width = Math.ceil(event.nativeEvent.layout.width);
|
|
749
|
-
setSides((prev) => (prev[side] === width ? prev : { ...prev, [side]: width }));
|
|
750
|
-
};
|
|
833
|
+
const { scrolled, collapsed, title: largeTitle } = useScreenScroll();
|
|
834
|
+
const titleSpan = useBarTitleSpan();
|
|
751
835
|
const motion = transitionProps(knobProps.motion.control);
|
|
752
|
-
const
|
|
836
|
+
const title = inlineTitle ?? largeTitle;
|
|
753
837
|
// LC-153: the pinned bar is a bar, so its neutral buttons paint plain.
|
|
754
838
|
return (
|
|
755
839
|
<Surface bar>
|
|
@@ -772,6 +856,7 @@ function PinnedTopBar({
|
|
|
772
856
|
flexWrap="nowrap"
|
|
773
857
|
// The collapsed title is placed against this row, not the page.
|
|
774
858
|
position="relative"
|
|
859
|
+
onLayout={titleSpan.measureRow}
|
|
775
860
|
paddingHorizontal={(slot.inset ?? knobProps.panelPadding.padding) as "$4"}
|
|
776
861
|
{...knobProps.gap}
|
|
777
862
|
{...rest}
|
|
@@ -781,7 +866,7 @@ function PinnedTopBar({
|
|
|
781
866
|
alignItems="center"
|
|
782
867
|
flexShrink={1000}
|
|
783
868
|
minWidth={0}
|
|
784
|
-
onLayout={
|
|
869
|
+
onLayout={titleSpan.measureLeading}
|
|
785
870
|
>
|
|
786
871
|
{leading}
|
|
787
872
|
</XStack>
|
|
@@ -794,29 +879,18 @@ function PinnedTopBar({
|
|
|
794
879
|
flexShrink={1}
|
|
795
880
|
minWidth={0}
|
|
796
881
|
justifyContent="flex-end"
|
|
797
|
-
onLayout={
|
|
882
|
+
onLayout={titleSpan.measureTrailing}
|
|
798
883
|
>
|
|
799
884
|
{trailing}
|
|
800
885
|
</IconButtonGroup>
|
|
801
886
|
{title ? (
|
|
802
|
-
<
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
right={clearance}
|
|
808
|
-
alignItems="center"
|
|
809
|
-
justifyContent="center"
|
|
810
|
-
pointerEvents="none"
|
|
811
|
-
aria-hidden={!collapsed}
|
|
812
|
-
data-bar-title=""
|
|
813
|
-
opacity={collapsed ? 1 : 0}
|
|
887
|
+
<BarTitle
|
|
888
|
+
title={title}
|
|
889
|
+
span={titleSpan.span}
|
|
890
|
+
onTitleWidth={titleSpan.measureTitle}
|
|
891
|
+
shown={Boolean(inlineTitle) || collapsed}
|
|
814
892
|
{...motion}
|
|
815
|
-
|
|
816
|
-
<SizableText {...knobProps.body} fontWeight="600" color="$color12" numberOfLines={1}>
|
|
817
|
-
{title}
|
|
818
|
-
</SizableText>
|
|
819
|
-
</XStack>
|
|
893
|
+
/>
|
|
820
894
|
) : null}
|
|
821
895
|
</XStack>
|
|
822
896
|
</YStack>
|
|
@@ -841,8 +915,11 @@ function useTopBarControl() {
|
|
|
841
915
|
// Phosphor's caret path starts 72 units into its 256 grid; back pulls by that
|
|
842
916
|
// so the caret's point, not its box, lands on the title's leading edge.
|
|
843
917
|
const CARET_OPTICAL_INSET = 72 / 256;
|
|
918
|
+
// Its ink is centred 8 units toward the point, so on a glass circle it moves
|
|
919
|
+
// back by that to sit at the circle's centre (FZ-8442).
|
|
920
|
+
const CARET_INK_OFFSET = 8 / 256;
|
|
844
921
|
|
|
845
|
-
function backCaret(Caret: typeof CaretLeftIcon) {
|
|
922
|
+
function backCaret(Caret: typeof CaretLeftIcon, inkSign: 1 | -1) {
|
|
846
923
|
return function BackCaret({
|
|
847
924
|
size = 22,
|
|
848
925
|
color,
|
|
@@ -856,16 +933,22 @@ function backCaret(Caret: typeof CaretLeftIcon) {
|
|
|
856
933
|
strokeWidth?: number;
|
|
857
934
|
strokeLinejoin?: "round";
|
|
858
935
|
}) {
|
|
936
|
+
const plated = useGlyphOnPlate();
|
|
859
937
|
return (
|
|
860
|
-
<XStack
|
|
938
|
+
<XStack
|
|
939
|
+
data-back-caret=""
|
|
940
|
+
{...(plated
|
|
941
|
+
? { x: inkSign * size * CARET_INK_OFFSET }
|
|
942
|
+
: { marginInlineStart: -Math.round(size * CARET_OPTICAL_INSET) })}
|
|
943
|
+
>
|
|
861
944
|
<Caret size={size} color={color} weight={weight} {...stroke} />
|
|
862
945
|
</XStack>
|
|
863
946
|
);
|
|
864
947
|
};
|
|
865
948
|
}
|
|
866
949
|
|
|
867
|
-
const BackCaretLeft = backCaret(CaretLeftIcon);
|
|
868
|
-
const BackCaretRight = backCaret(CaretRightIcon);
|
|
950
|
+
const BackCaretLeft = backCaret(CaretLeftIcon, 1);
|
|
951
|
+
const BackCaretRight = backCaret(CaretRightIcon, -1);
|
|
869
952
|
|
|
870
953
|
export interface PageBackButtonProps extends Omit<ButtonProps, "children"> {
|
|
871
954
|
/** Where back goes, shown beside the caret (the list's name). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/appShell/AppShell.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAA+C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAepF,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAIvD,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,uEAAuE;IACvE,UAAU,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC7C,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,iGAAiG;IACjG,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,wEAAwE;AACxE,wBAAgB,aAAa,CAC3B,YAAY,EAAE,SAAS,mBAAmB,EAAE,EAC5C,IAAI,EAAE,MAAM,GACX;IAAE,WAAW,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAQnE;AAiKD,wBAAgB,QAAQ,CAAC,EACvB,YAAY,EACZ,IAAI,EACJ,UAAU,EACV,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,MAAM,EACN,SAAS,EACT,SAAc,EACd,QAAQ,EACR,MAAoB,GACrB,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/appShell/AppShell.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAA+C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAepF,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAIvD,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,uEAAuE;IACvE,UAAU,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC7C,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,iGAAiG;IACjG,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,wEAAwE;AACxE,wBAAgB,aAAa,CAC3B,YAAY,EAAE,SAAS,mBAAmB,EAAE,EAC5C,IAAI,EAAE,MAAM,GACX;IAAE,WAAW,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAQnE;AAiKD,wBAAgB,QAAQ,CAAC,EACvB,YAAY,EACZ,IAAI,EACJ,UAAU,EACV,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,MAAM,EACN,SAAS,EACT,SAAc,EACd,QAAQ,EACR,MAAoB,GACrB,EAAE,aAAa,2CA0Pf"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ends a scroll view's content with the height the bottom chrome covers, so
|
|
3
|
+
* content scrolls under a translucent tab bar and its last row still clears it.
|
|
4
|
+
* Renders nothing when no bar is mounted. One per scroll view: `Screen` ends
|
|
5
|
+
* its own content with one.
|
|
6
|
+
*/
|
|
7
|
+
export declare function BottomChromeClearance({ testID }: {
|
|
8
|
+
testID?: string;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=BottomChromeClearance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BottomChromeClearance.d.ts","sourceRoot":"","sources":["../../src/appShell/BottomChromeClearance.tsx"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,MAA2B,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,kDAKzF"}
|
|
@@ -9,17 +9,7 @@
|
|
|
9
9
|
import { type ReactNode } from "react";
|
|
10
10
|
/** Publish (or with null, withdraw) one floating bar's covered height. */
|
|
11
11
|
export declare function setBottomChromeInset(id: symbol, next: number | null): void;
|
|
12
|
-
/**
|
|
13
|
-
* A region whose frame already ends above the bottom chrome, so nothing in it
|
|
14
|
-
* pads by the inset again.
|
|
15
|
-
*/
|
|
16
|
-
export declare function BottomChromeCleared({ children }: {
|
|
17
|
-
children?: ReactNode;
|
|
18
|
-
}): import("react").FunctionComponentElement<import("react").ProviderProps<boolean>>;
|
|
19
|
-
/**
|
|
20
|
-
* Points of screen the bottom chrome covers; 0 when no floating tab bar is
|
|
21
|
-
* mounted, or inside a `BottomChromeCleared` region.
|
|
22
|
-
*/
|
|
12
|
+
/** Points of screen the bottom chrome covers; 0 when no floating tab bar is mounted. */
|
|
23
13
|
export declare function useBottomChromeInset(): number;
|
|
24
14
|
/** Publish (or with null, withdraw) a page's bottom accessory. The latest wins. */
|
|
25
15
|
export declare function setBottomAccessory(id: symbol, node: ReactNode | null): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottomChrome.d.ts","sourceRoot":"","sources":["../../src/appShell/bottomChrome.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"bottomChrome.d.ts","sourceRoot":"","sources":["../../src/appShell/bottomChrome.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AA4B7D,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,QAEnE;AAED,wFAAwF;AACxF,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAKD,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,QAEpE;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,SAAS,CAE9C;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,QAEjE;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C"}
|
|
@@ -8,4 +8,5 @@ export type { AppEntry } from "./appTile";
|
|
|
8
8
|
export * from "./SettingsSplit";
|
|
9
9
|
export * from "./AppShell";
|
|
10
10
|
export { useBottomChromeInset } from "./bottomChrome";
|
|
11
|
+
export { BottomChromeClearance } from "./BottomChromeClearance";
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/appShell/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/appShell/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACvD,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/code/CodeBlock/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/code/CodeBlock/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA4CH,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sCAAsC;IACtC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,EACxB,QAAa,EACb,QAAgB,EAChB,KAAK,EACL,eAAe,EAAE,mBAAmB,EACpC,cAAmB,EACnB,WAAW,EACX,IAAI,GACL,EAAE,cAAc,2CAgKhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../../src/layouts/EmptyState.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../../src/layouts/EmptyState.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EAGL,KAAK,UAAU,EAKf,KAAK,WAAW,EACjB,MAAM,SAAS,CAAC;AAIjB,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC;IAChE,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,kFAAkF;IAClF,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC7B;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AA+CD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAe,EACf,MAAkB,EAClB,IAAI,EACJ,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,eAAe,2CA2GjB"}
|