@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
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { LinearGradient } from "@tamagui/linear-gradient";
|
|
10
|
-
import {
|
|
10
|
+
import { CopyIcon } from "@phosphor-icons/react";
|
|
11
11
|
import {
|
|
12
12
|
MIN_PRESS_TARGET,
|
|
13
13
|
useTouchSurface,
|
|
@@ -30,6 +30,7 @@ import { Code } from "../Code";
|
|
|
30
30
|
import { HighlightedCode } from "../HighlightedCode";
|
|
31
31
|
import { useShikiTokens } from "../Highlighter";
|
|
32
32
|
import { Pre } from "../Pre";
|
|
33
|
+
import { componentColors } from "../../componentColors";
|
|
33
34
|
|
|
34
35
|
/** Visible lines before the "show more" clamp (content constraint, LC-18). */
|
|
35
36
|
const VISIBLE_LINES = 22;
|
|
@@ -131,7 +132,7 @@ export function CodeBlock({
|
|
|
131
132
|
const capSquare = sizeRecipeForToken(String(codeSize), { touch }).height;
|
|
132
133
|
|
|
133
134
|
return (
|
|
134
|
-
<YStack position="relative"
|
|
135
|
+
<YStack position="relative">
|
|
135
136
|
<ErrorBoundary>
|
|
136
137
|
<YStack
|
|
137
138
|
ref={clampRef}
|
|
@@ -160,9 +161,18 @@ export function CodeBlock({
|
|
|
160
161
|
<Spacer size="$4" />
|
|
161
162
|
</LinearGradient>
|
|
162
163
|
)}
|
|
163
|
-
<Pre
|
|
164
|
+
<Pre
|
|
165
|
+
{...knobProps.borderRadius}
|
|
166
|
+
padding={0}
|
|
167
|
+
marginBottom={0}
|
|
168
|
+
flexDirection="row"
|
|
169
|
+
backgroundColor={componentColors.code.background}
|
|
170
|
+
borderWidth={0}
|
|
171
|
+
data-code-surface=""
|
|
172
|
+
>
|
|
164
173
|
<ScrollView
|
|
165
|
-
|
|
174
|
+
flex={1}
|
|
175
|
+
minWidth={0}
|
|
166
176
|
contentContainerStyle={{ minWidth: "100%" }}
|
|
167
177
|
horizontal
|
|
168
178
|
showsHorizontalScrollIndicator={false}
|
|
@@ -185,33 +195,39 @@ export function CodeBlock({
|
|
|
185
195
|
</HighlightedCode>
|
|
186
196
|
</Code>
|
|
187
197
|
</ScrollView>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
type="button"
|
|
195
|
-
aria-label="Copy to clipboard"
|
|
196
|
-
data-press-min={MIN_PRESS_TARGET}
|
|
197
|
-
hitSlop={capSquare < MIN_PRESS_TARGET ? pressTargetHitSlop(capSquare) : undefined}
|
|
198
|
-
onPress={activateCopy}
|
|
199
|
-
onKeyDown={
|
|
200
|
-
((e: KeyboardEvent) => {
|
|
201
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
202
|
-
e.preventDefault();
|
|
203
|
-
e.stopPropagation();
|
|
204
|
-
activateCopy();
|
|
205
|
-
}
|
|
206
|
-
}) as unknown as () => void
|
|
207
|
-
}
|
|
198
|
+
{showCopy && (
|
|
199
|
+
<XStack
|
|
200
|
+
flexShrink={0}
|
|
201
|
+
alignSelf={lines > 1 ? "flex-start" : "center"}
|
|
202
|
+
zIndex={zIndex.localTop}
|
|
203
|
+
data-code-copy=""
|
|
208
204
|
>
|
|
209
|
-
<Input.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
205
|
+
<Input.Button
|
|
206
|
+
glyphRing
|
|
207
|
+
size={codeSize}
|
|
208
|
+
color={componentColors.text.primary}
|
|
209
|
+
type="button"
|
|
210
|
+
aria-label="Copy to clipboard"
|
|
211
|
+
data-press-min={MIN_PRESS_TARGET}
|
|
212
|
+
hitSlop={capSquare < MIN_PRESS_TARGET ? pressTargetHitSlop(capSquare) : undefined}
|
|
213
|
+
onPress={activateCopy}
|
|
214
|
+
onKeyDown={
|
|
215
|
+
((e: KeyboardEvent) => {
|
|
216
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
activateCopy();
|
|
220
|
+
}
|
|
221
|
+
}) as unknown as () => void
|
|
222
|
+
}
|
|
223
|
+
>
|
|
224
|
+
<Input.Icon aria-hidden color={componentColors.text.primary}>
|
|
225
|
+
<CopyIcon weight="bold" />
|
|
226
|
+
</Input.Icon>
|
|
227
|
+
</Input.Button>
|
|
228
|
+
</XStack>
|
|
229
|
+
)}
|
|
230
|
+
</Pre>
|
|
215
231
|
</YStack>
|
|
216
232
|
</ErrorBoundary>
|
|
217
233
|
<NotifyRegion scope="field" region={region} size="$3" />
|
|
@@ -102,5 +102,7 @@ describe("EmptyState state anatomy (LC-171)", () => {
|
|
|
102
102
|
expect(command.textContent).toContain("shc tenant create <name>");
|
|
103
103
|
expect(screen.getByLabelText("Copy to clipboard")).toBeTruthy();
|
|
104
104
|
expect(document.querySelectorAll("[data-empty-intent] p")).toHaveLength(0);
|
|
105
|
+
const surface = command.querySelector("[data-code-surface]");
|
|
106
|
+
expect(surface?.contains(screen.getByLabelText("Copy to clipboard"))).toBe(true);
|
|
105
107
|
});
|
|
106
108
|
});
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
* not a second description. `intent="error"` is sober failure chrome
|
|
11
11
|
* (DG-ST-01) — never for a successful empty dataset.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import { CopyIcon } from "@phosphor-icons/react";
|
|
13
|
+
import { Spinner } from "@multiplatform.one/forms";
|
|
15
14
|
import {
|
|
16
15
|
IconSlot,
|
|
17
16
|
Intent,
|
|
@@ -26,7 +25,6 @@ import {
|
|
|
26
25
|
H2,
|
|
27
26
|
Paragraph,
|
|
28
27
|
type SizeTokens,
|
|
29
|
-
Text,
|
|
30
28
|
View,
|
|
31
29
|
XStack,
|
|
32
30
|
YStack,
|
|
@@ -34,8 +32,7 @@ import {
|
|
|
34
32
|
type YStackProps,
|
|
35
33
|
} from "tamagui";
|
|
36
34
|
import { componentColors, sectionHeading } from "../componentColors";
|
|
37
|
-
import {
|
|
38
|
-
import { useCopyFeedback } from "../feedback/copy";
|
|
35
|
+
import { CodeBlock } from "../code/CodeBlock";
|
|
39
36
|
|
|
40
37
|
export interface EmptyStateProps extends Omit<YStackProps, "size"> {
|
|
41
38
|
/** One short title: what is true now ("No Pokémon yet"), not an instruction. */
|
|
@@ -102,43 +99,13 @@ function StateSymbol({
|
|
|
102
99
|
}
|
|
103
100
|
|
|
104
101
|
/**
|
|
105
|
-
* The refusal gate's one command
|
|
106
|
-
*
|
|
102
|
+
* The refusal gate's one command, as a code block with its copy inside
|
|
103
|
+
* (LC-267).
|
|
107
104
|
*/
|
|
108
105
|
function CommandLine({ command }: { command: string }) {
|
|
109
|
-
const { knobProps } = useResolvedKnobs();
|
|
110
|
-
const { region, copy } = useCopyFeedback();
|
|
111
106
|
return (
|
|
112
|
-
<YStack data-empty-command=""
|
|
113
|
-
<
|
|
114
|
-
alignItems="center"
|
|
115
|
-
maxWidth="100%"
|
|
116
|
-
backgroundColor="$color3"
|
|
117
|
-
paddingLeft="$3"
|
|
118
|
-
paddingRight="$1"
|
|
119
|
-
paddingVertical="$1"
|
|
120
|
-
{...knobProps.containerRadius}
|
|
121
|
-
{...knobProps.gapSm}
|
|
122
|
-
>
|
|
123
|
-
<Text
|
|
124
|
-
{...knobProps.body}
|
|
125
|
-
fontFamily="$mono"
|
|
126
|
-
color={componentColors.text.primary}
|
|
127
|
-
numberOfLines={1}
|
|
128
|
-
flexShrink={1}
|
|
129
|
-
>
|
|
130
|
-
{command}
|
|
131
|
-
</Text>
|
|
132
|
-
<Button
|
|
133
|
-
nested
|
|
134
|
-
chromeless
|
|
135
|
-
flexShrink={0}
|
|
136
|
-
icon={<CopyIcon />}
|
|
137
|
-
aria-label="Copy to clipboard"
|
|
138
|
-
onPress={() => void copy(command)}
|
|
139
|
-
/>
|
|
140
|
-
</XStack>
|
|
141
|
-
<NotifyRegion scope="field" region={region} size="$3" />
|
|
107
|
+
<YStack data-empty-command="" alignSelf="center" {...readingWidthStyle()}>
|
|
108
|
+
<CodeBlock language="text">{command}</CodeBlock>
|
|
142
109
|
</YStack>
|
|
143
110
|
);
|
|
144
111
|
}
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
2
2
|
// Catalog Button (G-15 story honesty / LC-18): raw tamagui buttons ignored
|
|
3
3
|
// the knobs and pinned a non-house radius (UX-P06).
|
|
4
|
-
import { Button, IconButton
|
|
4
|
+
import { Button, IconButton } from "@multiplatform.one/forms";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
CpuIcon,
|
|
7
|
+
DatabaseIcon,
|
|
8
8
|
MagnifyingGlassIcon,
|
|
9
|
+
MemoryIcon,
|
|
9
10
|
PlusIcon,
|
|
10
11
|
SignOutIcon,
|
|
11
|
-
TrashIcon,
|
|
12
12
|
UserIcon,
|
|
13
13
|
} from "@phosphor-icons/react";
|
|
14
14
|
import { Preset } from "@multiplatform.one/theme";
|
|
15
15
|
import type { ReactNode } from "react";
|
|
16
|
-
import { XStack, YStack } from "tamagui";
|
|
16
|
+
import { Circle, Theme, View, XStack, YStack } from "tamagui";
|
|
17
17
|
import { Avatar } from "../Avatar";
|
|
18
18
|
import { Card } from "../Card";
|
|
19
|
+
import { themed } from "../icons/themed";
|
|
20
|
+
import { Chip } from "../Chip";
|
|
21
|
+
import { CodeBlock } from "../code/CodeBlock";
|
|
19
22
|
import { DropdownMenu } from "../DropdownMenu";
|
|
20
23
|
import { Popover } from "../Popover";
|
|
21
24
|
import { SchemeToggle } from "../SchemeToggle";
|
|
22
25
|
import { PopoverContent } from "../surfaces";
|
|
23
26
|
import { Tabs } from "../Tabs";
|
|
24
|
-
import { PageBackButton, PageHeader, Screen, ScreenToolbar } from "./page";
|
|
27
|
+
import { PageBackButton, PageHeader, PageSection, Screen, ScreenToolbar } from "./page";
|
|
25
28
|
import { Paragraph, SizableText } from "../Text";
|
|
26
29
|
|
|
27
30
|
const meta: Meta<typeof PageHeader> = {
|
|
@@ -174,23 +177,237 @@ export const LongTitleNarrow: Story = {
|
|
|
174
177
|
),
|
|
175
178
|
};
|
|
176
179
|
|
|
177
|
-
// FZ-
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
|
|
180
|
+
// FZ-8442: the SHC console's Nodes screen as it is now (shc
|
|
181
|
+
// features/console/src/manage/app/NodesPane.tsx and ManageApp.tsx's
|
|
182
|
+
// NodesPlace): sites as sections, each node a front panel whose whole face
|
|
183
|
+
// links to its detail, where the verbs live. The "+" opens one panel over
|
|
184
|
+
// itself holding the join command as a code block (LC-112, LC-267). The bar
|
|
185
|
+
// keeps every LC-176 icon-button context.
|
|
186
|
+
const faceplate = {
|
|
187
|
+
ear: 18,
|
|
188
|
+
screw: 9,
|
|
189
|
+
slot: 1.5,
|
|
190
|
+
led: 14,
|
|
191
|
+
ledGap: 2,
|
|
192
|
+
digitWidth: 8,
|
|
193
|
+
digitHeight: 14,
|
|
194
|
+
segment: 2,
|
|
195
|
+
digitGap: 2,
|
|
196
|
+
window: 6,
|
|
197
|
+
} as const;
|
|
198
|
+
const LED_COUNT = 20;
|
|
199
|
+
const bayGlyph = { cpu: themed(CpuIcon), memory: themed(MemoryIcon), disk: themed(DatabaseIcon) };
|
|
200
|
+
|
|
201
|
+
function Screw({ angle }: { angle: number }) {
|
|
202
|
+
const inner = faceplate.screw - 2;
|
|
203
|
+
return (
|
|
204
|
+
<Circle
|
|
205
|
+
size={faceplate.screw}
|
|
206
|
+
borderWidth={1}
|
|
207
|
+
borderColor="$color9"
|
|
208
|
+
backgroundColor="$color6"
|
|
209
|
+
position="relative"
|
|
210
|
+
>
|
|
211
|
+
<View
|
|
212
|
+
position="absolute"
|
|
213
|
+
top={(inner - faceplate.slot) / 2}
|
|
214
|
+
left={0}
|
|
215
|
+
width={inner}
|
|
216
|
+
height={faceplate.slot}
|
|
217
|
+
backgroundColor="$color11"
|
|
218
|
+
rotate={`${angle}deg`}
|
|
219
|
+
/>
|
|
220
|
+
</Circle>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function RackEar({ side, angles }: { side: "left" | "right"; angles: [number, number] }) {
|
|
225
|
+
return (
|
|
226
|
+
<YStack
|
|
227
|
+
width={faceplate.ear}
|
|
228
|
+
alignSelf="stretch"
|
|
229
|
+
alignItems="center"
|
|
230
|
+
justifyContent="space-between"
|
|
231
|
+
paddingVertical="$3"
|
|
232
|
+
backgroundColor="$color4"
|
|
233
|
+
borderColor="$color6"
|
|
234
|
+
borderLeftWidth={side === "right" ? 1 : 0}
|
|
235
|
+
borderRightWidth={side === "left" ? 1 : 0}
|
|
236
|
+
aria-hidden
|
|
237
|
+
>
|
|
238
|
+
<Screw angle={angles[0]} />
|
|
239
|
+
<Screw angle={angles[1]} />
|
|
240
|
+
</YStack>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const segmentsOf: Record<string, string> = {
|
|
245
|
+
"0": "abcdef",
|
|
246
|
+
"1": "bc",
|
|
247
|
+
"2": "abdeg",
|
|
248
|
+
"3": "abcdg",
|
|
249
|
+
"4": "bcfg",
|
|
250
|
+
"5": "acdfg",
|
|
251
|
+
"6": "acdefg",
|
|
252
|
+
"7": "abc",
|
|
253
|
+
"8": "abcdefg",
|
|
254
|
+
"9": "abcdfg",
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
function segmentBox(name: string, width: number) {
|
|
258
|
+
const { digitHeight: h, segment: t } = faceplate;
|
|
259
|
+
const middle = (h - t) / 2;
|
|
260
|
+
const stroke = (h + t) / 2;
|
|
261
|
+
const boxes: Record<string, { top: number; left: number; width: number; height: number }> = {
|
|
262
|
+
a: { top: 0, left: 0, width, height: t },
|
|
263
|
+
b: { top: 0, left: width - t, width: t, height: stroke },
|
|
264
|
+
c: { top: middle, left: width - t, width: t, height: stroke },
|
|
265
|
+
d: { top: h - t, left: 0, width, height: t },
|
|
266
|
+
e: { top: middle, left: 0, width: t, height: stroke },
|
|
267
|
+
f: { top: 0, left: 0, width: t, height: stroke },
|
|
268
|
+
g: { top: middle, left: 0, width, height: t },
|
|
269
|
+
};
|
|
270
|
+
return boxes[name]!;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function AddressDisplay({ address }: { address: string }) {
|
|
274
|
+
return (
|
|
275
|
+
<Theme name="dark">
|
|
276
|
+
<XStack
|
|
277
|
+
role="img"
|
|
278
|
+
aria-label={address}
|
|
279
|
+
alignItems="center"
|
|
280
|
+
gap={faceplate.digitGap}
|
|
281
|
+
backgroundColor="$color1"
|
|
282
|
+
borderColor="$color6"
|
|
283
|
+
borderWidth={1}
|
|
284
|
+
borderRadius="$2"
|
|
285
|
+
padding={faceplate.window}
|
|
286
|
+
>
|
|
287
|
+
{[...address].map((char, index) => {
|
|
288
|
+
if (char === "." || char === ":")
|
|
289
|
+
return (
|
|
290
|
+
<YStack
|
|
291
|
+
key={index}
|
|
292
|
+
width={faceplate.segment}
|
|
293
|
+
height={faceplate.digitHeight}
|
|
294
|
+
justifyContent={char === "." ? "flex-end" : "space-evenly"}
|
|
295
|
+
>
|
|
296
|
+
<View
|
|
297
|
+
width={faceplate.segment}
|
|
298
|
+
height={faceplate.segment}
|
|
299
|
+
backgroundColor="$blue10"
|
|
300
|
+
/>
|
|
301
|
+
{char === ":" ? (
|
|
302
|
+
<View
|
|
303
|
+
width={faceplate.segment}
|
|
304
|
+
height={faceplate.segment}
|
|
305
|
+
backgroundColor="$blue10"
|
|
306
|
+
/>
|
|
307
|
+
) : null}
|
|
308
|
+
</YStack>
|
|
309
|
+
);
|
|
310
|
+
const width = char === "1" ? faceplate.segment : faceplate.digitWidth;
|
|
311
|
+
return (
|
|
312
|
+
<View key={index} width={width} height={faceplate.digitHeight} position="relative">
|
|
313
|
+
{[...(segmentsOf[char] ?? "")].map((name) => (
|
|
314
|
+
<View
|
|
315
|
+
key={name}
|
|
316
|
+
position="absolute"
|
|
317
|
+
{...segmentBox(name, width)}
|
|
318
|
+
backgroundColor="$blue10"
|
|
319
|
+
/>
|
|
320
|
+
))}
|
|
321
|
+
</View>
|
|
322
|
+
);
|
|
323
|
+
})}
|
|
324
|
+
</XStack>
|
|
325
|
+
</Theme>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const ledInk = { lit: "$color11", warning: "$orange9", critical: "$red9" } as const;
|
|
330
|
+
const lampAt = (index: number) =>
|
|
331
|
+
index >= LED_COUNT * 0.9 ? "critical" : index >= LED_COUNT * 0.7 ? "warning" : "lit";
|
|
332
|
+
|
|
333
|
+
function LedBar({
|
|
334
|
+
bay,
|
|
335
|
+
label,
|
|
336
|
+
percent,
|
|
337
|
+
amount,
|
|
338
|
+
}: {
|
|
339
|
+
bay: keyof typeof bayGlyph;
|
|
340
|
+
label: string;
|
|
341
|
+
percent: number;
|
|
342
|
+
amount: string;
|
|
343
|
+
}) {
|
|
344
|
+
const lit = Math.round((percent / 100) * LED_COUNT);
|
|
345
|
+
const Glyph = bayGlyph[bay];
|
|
183
346
|
return (
|
|
184
|
-
<YStack gap="$
|
|
185
|
-
<XStack
|
|
186
|
-
<
|
|
187
|
-
<SizableText
|
|
347
|
+
<YStack gap="$1.5" role="meter" aria-label={label} aria-valuenow={percent}>
|
|
348
|
+
<XStack alignItems="center" gap="$2">
|
|
349
|
+
<Glyph size={16} weight="bold" color="$color11" />
|
|
350
|
+
<SizableText flex={1} size="$2" fontVariant={["tabular-nums"]} numberOfLines={1}>
|
|
351
|
+
{amount}
|
|
352
|
+
</SizableText>
|
|
353
|
+
<SizableText size="$2" fontWeight="700" fontVariant={["tabular-nums"]}>
|
|
354
|
+
{`${percent}%`}
|
|
355
|
+
</SizableText>
|
|
356
|
+
</XStack>
|
|
357
|
+
<XStack gap={faceplate.ledGap} aria-hidden>
|
|
358
|
+
{Array.from({ length: LED_COUNT }, (_, index) => (
|
|
359
|
+
<View
|
|
360
|
+
key={index}
|
|
361
|
+
flex={1}
|
|
362
|
+
height={faceplate.led}
|
|
363
|
+
borderRadius={1}
|
|
364
|
+
backgroundColor={ledInk[lampAt(index)]}
|
|
365
|
+
opacity={index < lit ? 1 : 0.18}
|
|
366
|
+
/>
|
|
367
|
+
))}
|
|
188
368
|
</XStack>
|
|
189
|
-
<Progress value={value} />
|
|
190
369
|
</YStack>
|
|
191
370
|
);
|
|
192
371
|
}
|
|
193
372
|
|
|
373
|
+
function NodeFaceplate() {
|
|
374
|
+
return (
|
|
375
|
+
<Card padding="$0" overflow="hidden" flexDirection="row" alignItems="stretch">
|
|
376
|
+
<RackEar side="left" angles={[38, 121]} />
|
|
377
|
+
<YStack flex={1} gap="$3" padding="$3">
|
|
378
|
+
<XStack alignItems="center" justifyContent="space-between" gap="$2.5">
|
|
379
|
+
<XStack gap="$1.5" alignItems="center" aria-hidden>
|
|
380
|
+
<Circle size="$0.75" backgroundColor="$green9" />
|
|
381
|
+
<Circle size="$0.5" backgroundColor="$green9" />
|
|
382
|
+
</XStack>
|
|
383
|
+
<AddressDisplay address="10.64.96.1:4003" />
|
|
384
|
+
</XStack>
|
|
385
|
+
<XStack alignItems="center" gap="$2.5">
|
|
386
|
+
<SizableText size="$4" fontWeight="700" numberOfLines={1}>
|
|
387
|
+
incus01
|
|
388
|
+
</SizableText>
|
|
389
|
+
<SizableText size="$1" fontWeight="600">
|
|
390
|
+
incus
|
|
391
|
+
</SizableText>
|
|
392
|
+
</XStack>
|
|
393
|
+
<LedBar bay="cpu" label="CPU" percent={12} amount="2.83/24 cores" />
|
|
394
|
+
<LedBar bay="memory" label="Memory" percent={41} amount="25.3/61.9 GiB" />
|
|
395
|
+
<LedBar bay="disk" label="Disk" percent={74} amount="352/476 GiB" />
|
|
396
|
+
<XStack gap="$1.5" flexWrap="wrap">
|
|
397
|
+
<Chip size="$2">AMD Ryzen 9 7900</Chip>
|
|
398
|
+
<Chip size="$2">x86_64</Chip>
|
|
399
|
+
</XStack>
|
|
400
|
+
<Avatar.Group max={7} size={36} circular={false}>
|
|
401
|
+
{["pokemon", "keycloak", "nextcloud", "fizzy", "gitlab", "openobserve"].map((app) => (
|
|
402
|
+
<Avatar key={app} name={app} />
|
|
403
|
+
))}
|
|
404
|
+
</Avatar.Group>
|
|
405
|
+
</YStack>
|
|
406
|
+
<RackEar side="right" angles={[92, 7]} />
|
|
407
|
+
</Card>
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
194
411
|
// LC-112 (FZ-8665 Round 45): each icon trigger's panel covers its button with
|
|
195
412
|
// no X, and a press outside closes it. The join panel's title sits on the
|
|
196
413
|
// panel's padding.
|
|
@@ -209,7 +426,7 @@ function AddNode() {
|
|
|
209
426
|
</Popover.Header>
|
|
210
427
|
<YStack paddingHorizontal="$4" gap="$3">
|
|
211
428
|
<Paragraph color="$color11">Run this on the new machine.</Paragraph>
|
|
212
|
-
<
|
|
429
|
+
<CodeBlock language="text">shc node join</CodeBlock>
|
|
213
430
|
</YStack>
|
|
214
431
|
</YStack>
|
|
215
432
|
</PopoverContent>
|
|
@@ -258,26 +475,9 @@ function NodesScreen() {
|
|
|
258
475
|
}
|
|
259
476
|
>
|
|
260
477
|
<PageHeader title="Nodes" divider={false} actions={<AddNode />} />
|
|
261
|
-
<
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
</SizableText>
|
|
265
|
-
<IconButton inline icon={CopyIcon} aria-label="Copy" />
|
|
266
|
-
</XStack>
|
|
267
|
-
<Card gap="$4">
|
|
268
|
-
<XStack justifyContent="space-between" alignItems="center">
|
|
269
|
-
<SizableText size="$6" fontWeight="600">
|
|
270
|
-
incus01
|
|
271
|
-
</SizableText>
|
|
272
|
-
<XStack alignItems="center">
|
|
273
|
-
<IconButton icon={DotsThreeIcon} aria-label="More for incus01" />
|
|
274
|
-
<IconButton error icon={TrashIcon} aria-label="Remove incus01" />
|
|
275
|
-
</XStack>
|
|
276
|
-
</XStack>
|
|
277
|
-
<SizableText color="$color11">10.64.96.1:4003</SizableText>
|
|
278
|
-
<Meter label="CPU" value={12} detail="2.83/24 cores 12%" />
|
|
279
|
-
<Meter label="Mem" value={41} detail="25.3/61.9 GiB 41%" />
|
|
280
|
-
</Card>
|
|
478
|
+
<PageSection title="Unplaced" description="1 node" headingLevel={3}>
|
|
479
|
+
<NodeFaceplate />
|
|
480
|
+
</PageSection>
|
|
281
481
|
</Screen>
|
|
282
482
|
);
|
|
283
483
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LC-157 (FZ-8442): the bar's inline title is centred on the bar while it
|
|
3
|
+
* fits clear of both side groups, and slides into the span between them when
|
|
4
|
+
* the wider group would touch it (Clay's "Inbox" beside four trailing glyphs).
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { BAR_TITLE_GAP, barTitleSpan, sideExtents } from "./barTitleSpan";
|
|
8
|
+
|
|
9
|
+
describe("bar title span (LC-157, FZ-8442)", () => {
|
|
10
|
+
it("centres on the bar when the title fits clear of both groups", () => {
|
|
11
|
+
const span = barTitleSpan({ row: 358, leftEnd: 44, rightStart: 314, title: 60 });
|
|
12
|
+
expect(span.left).toBe(span.right);
|
|
13
|
+
expect(span.left).toBe(44 + BAR_TITLE_GAP);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("slides between the groups when centring would hide it", () => {
|
|
17
|
+
const span = barTitleSpan({ row: 358, leftEnd: 44, rightStart: 130, title: 45 });
|
|
18
|
+
expect(span).toEqual({ left: 44 + BAR_TITLE_GAP, right: 358 - 130 + BAR_TITLE_GAP });
|
|
19
|
+
expect(358 - span.left - span.right).toBeGreaterThanOrEqual(45);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("reads the groups by side, so a right-to-left bar mirrors", () => {
|
|
23
|
+
const ltr = sideExtents(358, { x: 16, width: 44 }, { x: 250, width: 92 });
|
|
24
|
+
const rtl = sideExtents(358, { x: 298, width: 44 }, { x: 16, width: 92 });
|
|
25
|
+
expect(ltr).toEqual({ leftEnd: 60, rightStart: 250 });
|
|
26
|
+
expect(rtl).toEqual({ leftEnd: 108, rightStart: 298 });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("an empty group takes no room", () => {
|
|
30
|
+
expect(sideExtents(358, { x: 16, width: 44 }, { x: 342, width: 0 })).toEqual({
|
|
31
|
+
leftEnd: 60,
|
|
32
|
+
rightStart: 0,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** The space kept between the bar's inline title and either side group. */
|
|
2
|
+
export const BAR_TITLE_GAP = 8;
|
|
3
|
+
|
|
4
|
+
export interface BarTitleBox {
|
|
5
|
+
/** The bar row's width. */
|
|
6
|
+
row: number;
|
|
7
|
+
/** Where the group on the left ends, from the row's left edge; 0 when empty. */
|
|
8
|
+
leftEnd: number;
|
|
9
|
+
/** Where the group on the right starts, from the row's left edge; 0 when empty. */
|
|
10
|
+
rightStart: number;
|
|
11
|
+
/** The title's natural one-line width. */
|
|
12
|
+
title: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Where the bar's inline title sits. Like UIKit it is centred on the bar
|
|
17
|
+
* while it fits clear of both side groups; when the wider group would touch
|
|
18
|
+
* it, it centres in the span left between the groups instead of vanishing.
|
|
19
|
+
*/
|
|
20
|
+
export function barTitleSpan(box: BarTitleBox): { left: number; right: number } {
|
|
21
|
+
const left = box.leftEnd + BAR_TITLE_GAP;
|
|
22
|
+
const right = (box.rightStart ? box.row - box.rightStart : 0) + BAR_TITLE_GAP;
|
|
23
|
+
const centred = Math.max(left, right);
|
|
24
|
+
return box.row - 2 * centred >= box.title ? { left: centred, right: centred } : { left, right };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Extent = { x: number; width: number };
|
|
28
|
+
|
|
29
|
+
/** The bar's two side groups as physical left and right extents (RTL-safe). */
|
|
30
|
+
export function sideExtents(
|
|
31
|
+
row: number,
|
|
32
|
+
a: Extent,
|
|
33
|
+
b: Extent,
|
|
34
|
+
): Pick<BarTitleBox, "leftEnd" | "rightStart"> {
|
|
35
|
+
const groups = [a, b].filter((group) => group.width > 0);
|
|
36
|
+
let leftEnd = 0;
|
|
37
|
+
let rightStart = 0;
|
|
38
|
+
for (const group of groups) {
|
|
39
|
+
if (group.x + group.width / 2 < row / 2)
|
|
40
|
+
leftEnd = Math.max(leftEnd, Math.ceil(group.x + group.width));
|
|
41
|
+
else rightStart = rightStart ? Math.min(rightStart, Math.floor(group.x)) : Math.floor(group.x);
|
|
42
|
+
}
|
|
43
|
+
return { leftEnd, rightStart };
|
|
44
|
+
}
|