@sovereignfs/ui 0.54.0 → 0.83.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/Accordion.module.css +1 -1
- package/dist/Alert.module.css +1 -1
- package/dist/AppsLauncher.module.css +96 -0
- package/dist/Avatar.module.css +1 -1
- package/dist/Badge.module.css +30 -7
- package/dist/BalanceChip.module.css +1 -1
- package/dist/Button.module.css +18 -2
- package/dist/Card.module.css +1 -1
- package/dist/CardTile.module.css +181 -0
- package/dist/CodeTextarea.module.css +1 -1
- package/dist/Collapsible.module.css +1 -1
- package/dist/ColorPicker.module.css +132 -0
- package/dist/Combobox.module.css +2 -2
- package/dist/Command.module.css +1 -1
- package/dist/ConfirmDialog.module.css +2 -2
- package/dist/ConsentPrompt.module.css +21 -0
- package/dist/DatePicker.module.css +1 -1
- package/dist/Dialog.module.css +130 -21
- package/dist/Drawer.module.css +36 -7
- package/dist/Header.module.css +62 -0
- package/dist/HeaderFooterLayout.module.css +21 -0
- package/dist/IconPicker.module.css +1 -1
- package/dist/Input.module.css +1 -1
- package/dist/Kbd.module.css +1 -1
- package/dist/Markdown.module.css +24 -0
- package/dist/Menu.module.css +1 -0
- package/dist/Message.module.css +1 -1
- package/dist/MessageScroller.module.css +12 -1
- package/dist/MobileAppsDrawer.module.css +10 -9
- package/dist/MobileFooter.module.css +1 -1
- package/dist/MobileHeader.module.css +14 -1
- package/dist/NavList.module.css +130 -0
- package/dist/NavTabs.module.css +1 -1
- package/dist/NotificationsPanel.module.css +258 -0
- package/dist/OverlayHeader.module.css +1 -1
- package/dist/PageContainer.module.css +62 -0
- package/dist/PageHeader.module.css +54 -0
- package/dist/PageLayout.module.css +51 -0
- package/dist/Popover.module.css +1 -1
- package/dist/QuantityStepper.module.css +1 -1
- package/dist/Resizable.module.css +3 -3
- package/dist/RootLayout.module.css +12 -0
- package/dist/SegmentedControl.module.css +1 -1
- package/dist/Select.module.css +1 -1
- package/dist/Sheet.module.css +4 -1
- package/dist/SplitPane.module.css +4 -4
- package/dist/StatusBadge.module.css +1 -1
- package/dist/StepStrip.module.css +53 -0
- package/dist/SuggestionInput.module.css +2 -2
- package/dist/SwipableMobileCarousel.module.css +12 -3
- package/dist/SwipableMobileCarouselDots.module.css +15 -0
- package/dist/SwipeStack.module.css +101 -0
- package/dist/SystemBanner.module.css +10 -0
- package/dist/Table.module.css +2 -2
- package/dist/Tabs.module.css +1 -1
- package/dist/TagInput.module.css +2 -2
- package/dist/Textarea.module.css +9 -1
- package/dist/ThreeColumnLayout.module.css +35 -0
- package/dist/Toast.module.css +62 -3
- package/dist/Tooltip.module.css +1 -1
- package/dist/UserMenu.module.css +90 -0
- package/dist/index.d.ts +1047 -45
- package/dist/index.js +3908 -1456
- package/dist/tokens/primitives.css +11 -0
- package/dist/tokens/semantic.css +32 -0
- package/dist/tokens/theme-presets.css +33 -0
- package/package.json +25 -9
- package/src/__tests__/package-json.test.ts +8 -0
- package/src/components/Accordion/Accordion.module.css +1 -1
- package/src/components/Alert/Alert.module.css +1 -1
- package/src/components/AppsLauncher/AppsLauncher.module.css +96 -0
- package/src/components/AppsLauncher/AppsLauncher.stories.tsx +58 -0
- package/src/components/AppsLauncher/AppsLauncher.tsx +117 -0
- package/src/components/AppsLauncher/__tests__/AppsLauncher.test.tsx +52 -0
- package/src/components/Avatar/Avatar.module.css +1 -1
- package/src/components/Badge/Badge.module.css +30 -7
- package/src/components/Badge/Badge.stories.tsx +16 -4
- package/src/components/Badge/Badge.tsx +7 -4
- package/src/components/Badge/__tests__/Badge.test.tsx +4 -2
- package/src/components/BalanceChip/BalanceChip.module.css +1 -1
- package/src/components/Button/Button.module.css +18 -2
- package/src/components/Card/Card.module.css +1 -1
- package/src/components/CardTile/CardTile.module.css +181 -0
- package/src/components/CardTile/CardTile.stories.tsx +86 -0
- package/src/components/CardTile/CardTile.tsx +128 -0
- package/src/components/CardTile/__tests__/CardTile.test.tsx +56 -0
- package/src/components/CodeTextarea/CodeTextarea.module.css +1 -1
- package/src/components/Collapsible/Collapsible.module.css +1 -1
- package/src/components/ColorPicker/ColorPicker.module.css +132 -0
- package/src/components/ColorPicker/ColorPicker.stories.tsx +80 -0
- package/src/components/ColorPicker/ColorPicker.tsx +157 -0
- package/src/components/ColorPicker/__tests__/ColorPicker.test.tsx +117 -0
- package/src/components/Combobox/Combobox.module.css +2 -2
- package/src/components/Command/Command.module.css +1 -1
- package/src/components/ConfirmDialog/ConfirmDialog.module.css +2 -2
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +28 -2
- package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +71 -3
- package/src/components/ConsentPrompt/ConsentPrompt.module.css +21 -0
- package/src/components/ConsentPrompt/ConsentPrompt.stories.tsx +31 -0
- package/src/components/ConsentPrompt/ConsentPrompt.tsx +66 -0
- package/src/components/ContextMenu/ContextMenu.tsx +1 -1
- package/src/components/DatePicker/DatePicker.module.css +1 -1
- package/src/components/Dialog/Dialog.module.css +130 -21
- package/src/components/Dialog/Dialog.stories.tsx +179 -2
- package/src/components/Dialog/Dialog.tsx +129 -35
- package/src/components/Dialog/__tests__/Dialog.test.tsx +190 -9
- package/src/components/DragHandleRow/DragHandleRow.tsx +2 -21
- package/src/components/Drawer/Drawer.module.css +36 -7
- package/src/components/Drawer/Drawer.tsx +9 -10
- package/src/components/Drawer/__tests__/Drawer.test.tsx +33 -6
- package/src/components/GripIcon/GripIcon.tsx +27 -0
- package/src/components/Header/Header.module.css +62 -0
- package/src/components/Header/Header.stories.tsx +452 -0
- package/src/components/Header/Header.tsx +102 -0
- package/src/components/Header/__tests__/Header.test.tsx +104 -0
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.module.css +21 -0
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.stories.tsx +111 -0
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.tsx +54 -0
- package/src/components/HeaderFooterLayout/__tests__/HeaderFooterLayout.test.tsx +58 -0
- package/src/components/Icon/icons/arrow-left-right.tsx +23 -0
- package/src/components/Icon/icons/bold.tsx +20 -0
- package/src/components/Icon/icons/calendar.tsx +4 -4
- package/src/components/Icon/icons/carrot.tsx +3 -3
- package/src/components/Icon/icons/circle-chevron-left.tsx +21 -0
- package/src/components/Icon/icons/code.tsx +21 -0
- package/src/components/Icon/icons/copy.tsx +21 -0
- package/src/components/Icon/icons/credit-card.tsx +21 -0
- package/src/components/Icon/icons/database.tsx +22 -0
- package/src/components/Icon/icons/download.tsx +22 -0
- package/src/components/Icon/icons/file-text.tsx +24 -0
- package/src/components/Icon/icons/folder-closed.tsx +21 -0
- package/src/components/Icon/icons/folder-open.tsx +20 -0
- package/src/components/Icon/icons/folder.tsx +20 -0
- package/src/components/Icon/icons/folders.tsx +21 -0
- package/src/components/Icon/icons/hat-glasses.tsx +24 -0
- package/src/components/Icon/icons/history.tsx +22 -0
- package/src/components/Icon/icons/inbox.tsx +21 -0
- package/src/components/Icon/icons/index.ts +161 -1
- package/src/components/Icon/icons/italic.tsx +22 -0
- package/src/components/Icon/icons/layers.tsx +22 -0
- package/src/components/Icon/icons/layout-dashboard.tsx +23 -0
- package/src/components/Icon/icons/layout-grid.tsx +23 -0
- package/src/components/Icon/icons/link.tsx +21 -0
- package/src/components/Icon/icons/list-ordered.tsx +25 -0
- package/src/components/Icon/icons/list.tsx +25 -0
- package/src/components/Icon/icons/luggage.tsx +24 -0
- package/src/components/Icon/icons/map-pin.tsx +21 -0
- package/src/components/Icon/icons/minus.tsx +20 -0
- package/src/components/Icon/icons/paint-bucket.tsx +23 -0
- package/src/components/Icon/icons/panel-left.tsx +21 -0
- package/src/components/Icon/icons/pin.tsx +21 -0
- package/src/components/Icon/icons/rectangle-ellipsis.tsx +23 -0
- package/src/components/Icon/icons/refresh-cw.tsx +23 -0
- package/src/components/Icon/icons/rotate-cw.tsx +21 -0
- package/src/components/Icon/icons/route.tsx +22 -0
- package/src/components/Icon/icons/send.tsx +21 -0
- package/src/components/Icon/icons/share-2.tsx +24 -0
- package/src/components/Icon/icons/sheet.tsx +24 -0
- package/src/components/Icon/icons/square-kanban.tsx +23 -0
- package/src/components/Icon/icons/table.tsx +23 -0
- package/src/components/Icon/icons/user-round-plus.tsx +23 -0
- package/src/components/Icon/icons/users.tsx +23 -0
- package/src/components/IconPicker/IconPicker.module.css +1 -1
- package/src/components/Input/Input.module.css +1 -1
- package/src/components/Input/Input.tsx +8 -2
- package/src/components/Kbd/Kbd.module.css +1 -1
- package/src/components/Markdown/Markdown.module.css +24 -0
- package/src/components/Markdown/Markdown.stories.tsx +10 -0
- package/src/components/Markdown/Markdown.tsx +166 -29
- package/src/components/Markdown/__tests__/Markdown.test.tsx +144 -0
- package/src/components/Menu/Menu.module.css +1 -0
- package/src/components/Menu/Menu.tsx +72 -22
- package/src/components/Menu/__tests__/Menu.test.tsx +30 -0
- package/src/components/Menubar/Menubar.tsx +1 -1
- package/src/components/Message/Message.module.css +1 -1
- package/src/components/MessageScroller/MessageScroller.module.css +12 -1
- package/src/components/MessageScroller/MessageScroller.tsx +12 -0
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +10 -9
- package/src/components/MobileFooter/MobileFooter.module.css +1 -1
- package/src/components/MobileFooter/MobileFooter.tsx +15 -2
- package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +58 -0
- package/src/components/MobileHeader/MobileHeader.module.css +14 -1
- package/src/components/MobileHeader/MobileHeader.tsx +15 -2
- package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +56 -1
- package/src/components/NavList/NavList.module.css +130 -0
- package/src/components/NavList/NavList.tsx +136 -0
- package/src/components/NavList/__tests__/NavList.test.tsx +109 -0
- package/src/components/NavTabs/NavTabs.module.css +1 -1
- package/src/components/NotificationsPanel/NotificationsPanel.module.css +258 -0
- package/src/components/NotificationsPanel/NotificationsPanel.stories.tsx +61 -0
- package/src/components/NotificationsPanel/NotificationsPanel.tsx +187 -0
- package/src/components/NotificationsPanel/__tests__/NotificationsPanel.test.tsx +78 -0
- package/src/components/OverlayHeader/OverlayHeader.module.css +1 -1
- package/src/components/OverlayHeader/OverlayHeader.tsx +19 -4
- package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +18 -0
- package/src/components/PageContainer/PageContainer.module.css +62 -0
- package/src/components/PageContainer/PageContainer.tsx +37 -4
- package/src/components/PageContainer/__tests__/PageContainer.test.tsx +41 -8
- package/src/components/PageHeader/PageHeader.module.css +54 -0
- package/src/components/PageHeader/PageHeader.tsx +146 -5
- package/src/components/PageHeader/__tests__/PageHeader.test.tsx +103 -0
- package/src/components/PageLayout/PageLayout.module.css +51 -0
- package/src/components/PageLayout/PageLayout.stories.tsx +73 -0
- package/src/components/PageLayout/PageLayout.tsx +51 -0
- package/src/components/PageLayout/__tests__/PageLayout.test.tsx +34 -0
- package/src/components/Popover/Popover.module.css +1 -1
- package/src/components/Popover/Popover.tsx +86 -4
- package/src/components/Popover/__tests__/Popover.test.tsx +165 -0
- package/src/components/QuantityStepper/QuantityStepper.module.css +1 -1
- package/src/components/Resizable/Resizable.module.css +3 -3
- package/src/components/RootLayout/RootLayout.module.css +12 -0
- package/src/components/RootLayout/RootLayout.stories.tsx +125 -0
- package/src/components/RootLayout/RootLayout.tsx +185 -0
- package/src/components/RootLayout/__tests__/RootLayout.test.tsx +161 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +1 -1
- package/src/components/Select/Select.module.css +1 -1
- package/src/components/Sheet/Sheet.module.css +4 -1
- package/src/components/Sheet/Sheet.tsx +5 -5
- package/src/components/SplitPane/SplitPane.module.css +4 -4
- package/src/components/StatusBadge/StatusBadge.module.css +1 -1
- package/src/components/StepStrip/StepStrip.module.css +53 -0
- package/src/components/StepStrip/StepStrip.tsx +74 -0
- package/src/components/SuggestionInput/SuggestionInput.module.css +2 -2
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +12 -3
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +25 -9
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +81 -0
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +15 -0
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +12 -1
- package/src/components/SwipeStack/SwipeStack.module.css +101 -0
- package/src/components/SwipeStack/SwipeStack.tsx +199 -0
- package/src/components/SwipeStack/SwipeStackCard.tsx +22 -0
- package/src/components/SwipeStack/__tests__/SwipeStack.test.tsx +169 -0
- package/src/components/SystemBanner/SystemBanner.module.css +10 -0
- package/src/components/Table/Table.module.css +2 -2
- package/src/components/Tabs/Tabs.module.css +1 -1
- package/src/components/TagInput/TagInput.module.css +2 -2
- package/src/components/Textarea/Textarea.module.css +9 -1
- package/src/components/Textarea/Textarea.tsx +28 -5
- package/src/components/Textarea/__tests__/Textarea.test.tsx +23 -1
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.module.css +35 -0
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.stories.tsx +152 -0
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.tsx +87 -0
- package/src/components/ThreeColumnLayout/__tests__/ThreeColumnLayout.test.tsx +78 -0
- package/src/components/Toast/Toast.module.css +62 -3
- package/src/components/Toast/Toast.stories.tsx +1 -1
- package/src/components/Tooltip/Tooltip.module.css +1 -1
- package/src/components/UserMenu/UserMenu.module.css +90 -0
- package/src/components/UserMenu/UserMenu.stories.tsx +49 -0
- package/src/components/UserMenu/UserMenu.tsx +132 -0
- package/src/components/UserMenu/__tests__/UserMenu.test.tsx +77 -0
- package/src/hooks/__tests__/usePublishShellChromeHeight.test.tsx +118 -0
- package/src/hooks/__tests__/useReorderSensors.test.ts +26 -0
- package/src/hooks/__tests__/useSnapCarousel.test.tsx +82 -0
- package/src/hooks/__tests__/useSwipeStack.test.tsx +155 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/usePublishShellChromeHeight.ts +96 -0
- package/src/hooks/useReorderSensors.ts +88 -0
- package/src/hooks/useSnapCarousel.ts +211 -6
- package/src/hooks/useSwipeStack.ts +235 -0
- package/src/index.ts +61 -1
- package/src/overlay-shell.ts +65 -0
- package/src/stories/DesignSystemOverview.stories.tsx +617 -11
- package/src/stories/GripIcon.stories.tsx +29 -0
- package/src/stories/LayoutSystem.stories.tsx +639 -0
- package/src/stories/MenuEntries.stories.tsx +86 -0
- package/src/stories/NavList.stories.tsx +147 -0
- package/src/stories/PageContainer.stories.tsx +52 -7
- package/src/stories/PageHeader.stories.tsx +54 -1
- package/src/stories/StepStrip.stories.tsx +76 -0
- package/src/stories/SwipableMobileCarousel.stories.tsx +58 -0
- package/src/stories/SwipeStack.stories.tsx +200 -0
- package/src/stories/Textarea.stories.tsx +12 -0
- package/src/stories/TokenGallery.stories.tsx +50 -0
- package/src/tokens/primitives.css +11 -0
- package/src/tokens/semantic.css +32 -0
- package/src/tokens/theme-presets.css +33 -0
- package/src/tokens/theme-presets.ts +72 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Children } from 'react';
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
import { HeaderFooterLayout } from '../HeaderFooterLayout/HeaderFooterLayout';
|
|
6
|
+
import { ResponsiveSurface } from '../ResponsiveSurface/ResponsiveSurface';
|
|
7
|
+
import { ThreeColumnLayout } from '../ThreeColumnLayout/ThreeColumnLayout';
|
|
8
|
+
import styles from './RootLayout.module.css';
|
|
9
|
+
|
|
10
|
+
export type RootLayoutVariant = 'plain' | 'sidebar' | 'header' | 'shell';
|
|
11
|
+
|
|
12
|
+
export interface RootLayoutProps {
|
|
13
|
+
/**
|
|
14
|
+
* Which chrome shape this page uses. Children are positional and their
|
|
15
|
+
* count is fixed per variant:
|
|
16
|
+
* - `'plain'` — 1 child: `[main]`. No chrome on either breakpoint.
|
|
17
|
+
* - `'sidebar'` — 2 children: `[sidebar, main]`. Sidebar + main on web
|
|
18
|
+
* only — on mobile, sidebar is dropped and only main renders.
|
|
19
|
+
* - `'header'` — 2 children: `[header, main]`. Header + main on both
|
|
20
|
+
* breakpoints — same structure, but the header's own height differs
|
|
21
|
+
* per breakpoint (`headerHeight` on web, `mobileHeaderHeight` on
|
|
22
|
+
* mobile), matching how real `shell: minimal` plugins actually do
|
|
23
|
+
* this (a compact web top bar vs. the platform's mobile chrome
|
|
24
|
+
* height).
|
|
25
|
+
* - `'shell'` — 3 children: `[header, main, footer]`. Header + main +
|
|
26
|
+
* footer on mobile only — on web, header/footer are dropped and only
|
|
27
|
+
* main renders.
|
|
28
|
+
* Default `'plain'`.
|
|
29
|
+
*/
|
|
30
|
+
variant?: RootLayoutVariant;
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
/** px. Default 64 — matches the platform's own persistent nav rail
|
|
33
|
+
* (`--sv-shell-sidebar-width`, runtime/app/(platform)/shell.module.css).
|
|
34
|
+
* Only used by `'sidebar'`. */
|
|
35
|
+
sidebarWidth?: number;
|
|
36
|
+
/** px. Default 48 — matches a real `shell: minimal` plugin's own compact
|
|
37
|
+
* web header (e.g. Kanban's `KanbanHeader`, deliberately not the
|
|
38
|
+
* platform's 60px shell header since a `shell: minimal` plugin owns its
|
|
39
|
+
* whole viewport). Web only — used by `'header'` on web. */
|
|
40
|
+
headerHeight?: number;
|
|
41
|
+
/** px. Default 60 — matches the platform's own mobile chrome
|
|
42
|
+
* (`--sv-shell-header-height`, `MobileHeader`'s default). Mobile only —
|
|
43
|
+
* used by `'header'` and `'shell'` on mobile. */
|
|
44
|
+
mobileHeaderHeight?: number;
|
|
45
|
+
/** px. Default 60 — matches the platform's own mobile chrome
|
|
46
|
+
* (`--sv-shell-footer-height`, `MobileFooter`'s default). Mobile only —
|
|
47
|
+
* used by `'shell'`. */
|
|
48
|
+
footerHeight?: number;
|
|
49
|
+
className?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const EXPECTED_CHILD_COUNT: Record<RootLayoutVariant, number> = {
|
|
53
|
+
plain: 1,
|
|
54
|
+
sidebar: 2,
|
|
55
|
+
header: 2,
|
|
56
|
+
shell: 3,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
interface RootLayoutWebProps {
|
|
60
|
+
variant: RootLayoutVariant;
|
|
61
|
+
items: ReactNode[];
|
|
62
|
+
sidebarWidth: number;
|
|
63
|
+
headerHeight: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// internal — not exported
|
|
67
|
+
function RootLayoutWeb({ variant, items, sidebarWidth, headerHeight }: RootLayoutWebProps) {
|
|
68
|
+
if (variant === 'sidebar') {
|
|
69
|
+
const [sidebar, main] = items;
|
|
70
|
+
return (
|
|
71
|
+
<ThreeColumnLayout sidebarWidth={sidebarWidth}>
|
|
72
|
+
{sidebar}
|
|
73
|
+
{main}
|
|
74
|
+
</ThreeColumnLayout>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (variant === 'header') {
|
|
78
|
+
const [header, main] = items;
|
|
79
|
+
return (
|
|
80
|
+
<HeaderFooterLayout header={header} headerHeight={headerHeight}>
|
|
81
|
+
{main}
|
|
82
|
+
</HeaderFooterLayout>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
// 'plain' and 'shell' both render plain on web — shell's chrome is mobile-only.
|
|
86
|
+
const main = variant === 'shell' ? items[1] : items[0];
|
|
87
|
+
return <>{main}</>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface RootLayoutMobileProps {
|
|
91
|
+
variant: RootLayoutVariant;
|
|
92
|
+
items: ReactNode[];
|
|
93
|
+
mobileHeaderHeight: number;
|
|
94
|
+
footerHeight: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// internal — not exported
|
|
98
|
+
function RootLayoutMobile({
|
|
99
|
+
variant,
|
|
100
|
+
items,
|
|
101
|
+
mobileHeaderHeight,
|
|
102
|
+
footerHeight,
|
|
103
|
+
}: RootLayoutMobileProps) {
|
|
104
|
+
if (variant === 'header') {
|
|
105
|
+
const [header, main] = items;
|
|
106
|
+
return (
|
|
107
|
+
<HeaderFooterLayout header={header} headerHeight={mobileHeaderHeight}>
|
|
108
|
+
{main}
|
|
109
|
+
</HeaderFooterLayout>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if (variant === 'shell') {
|
|
113
|
+
const [header, main, footer] = items;
|
|
114
|
+
return (
|
|
115
|
+
<HeaderFooterLayout
|
|
116
|
+
header={header}
|
|
117
|
+
footer={footer}
|
|
118
|
+
headerHeight={mobileHeaderHeight}
|
|
119
|
+
footerHeight={footerHeight}
|
|
120
|
+
>
|
|
121
|
+
{main}
|
|
122
|
+
</HeaderFooterLayout>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
// 'plain' and 'sidebar' both render plain on mobile — sidebar's chrome is web-only.
|
|
126
|
+
const main = variant === 'sidebar' ? items[1] : items[0];
|
|
127
|
+
return <>{main}</>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* RootLayout — the root-level layout a plugin's page tree sits in. Enforces
|
|
132
|
+
* only structure and dimensions (`width: 100%; height: 100%`, filling
|
|
133
|
+
* whatever real height the platform shell's own ancestor chain already
|
|
134
|
+
* establishes — not viewport units, since a plugin isn't alone on the
|
|
135
|
+
* page). A definite height, not min-height: ThreeColumnLayout/
|
|
136
|
+
* HeaderFooterLayout's own .shell need one to resolve their own height:
|
|
137
|
+
* 100% against (see RootLayout.module.css's comment). All content comes
|
|
138
|
+
* from children; RootLayout has no opinion on what's inside any region.
|
|
139
|
+
*
|
|
140
|
+
* The web/mobile fork is `ResponsiveSurface` — only one of
|
|
141
|
+
* `RootLayoutWeb`/`RootLayoutMobile` is ever mounted, never a CSS squeeze of
|
|
142
|
+
* the same tree. Each variant's actual shape is delegated entirely to
|
|
143
|
+
* `ThreeColumnLayout`/`HeaderFooterLayout`; RootLayout itself contains no
|
|
144
|
+
* media queries.
|
|
145
|
+
*/
|
|
146
|
+
export function RootLayout({
|
|
147
|
+
variant = 'plain',
|
|
148
|
+
children,
|
|
149
|
+
sidebarWidth = 64,
|
|
150
|
+
headerHeight = 48,
|
|
151
|
+
mobileHeaderHeight = 60,
|
|
152
|
+
footerHeight = 60,
|
|
153
|
+
className,
|
|
154
|
+
}: RootLayoutProps) {
|
|
155
|
+
const items = Children.toArray(children);
|
|
156
|
+
|
|
157
|
+
if (process.env.NODE_ENV !== 'production' && items.length !== EXPECTED_CHILD_COUNT[variant]) {
|
|
158
|
+
console.warn(
|
|
159
|
+
`[RootLayout] variant="${variant}" expects ${EXPECTED_CHILD_COUNT[variant]} children; received ${items.length}.`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<div className={[styles.root, className].filter(Boolean).join(' ')}>
|
|
165
|
+
<ResponsiveSurface
|
|
166
|
+
web={
|
|
167
|
+
<RootLayoutWeb
|
|
168
|
+
variant={variant}
|
|
169
|
+
items={items}
|
|
170
|
+
sidebarWidth={sidebarWidth}
|
|
171
|
+
headerHeight={headerHeight}
|
|
172
|
+
/>
|
|
173
|
+
}
|
|
174
|
+
mobile={
|
|
175
|
+
<RootLayoutMobile
|
|
176
|
+
variant={variant}
|
|
177
|
+
items={items}
|
|
178
|
+
mobileHeaderHeight={mobileHeaderHeight}
|
|
179
|
+
footerHeight={footerHeight}
|
|
180
|
+
/>
|
|
181
|
+
}
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { cleanup, render, screen } from '@testing-library/react';
|
|
4
|
+
import { RootLayout } from '../RootLayout';
|
|
5
|
+
|
|
6
|
+
let matches = false;
|
|
7
|
+
|
|
8
|
+
function installMatchMedia() {
|
|
9
|
+
vi.stubGlobal(
|
|
10
|
+
'matchMedia',
|
|
11
|
+
vi.fn().mockImplementation((query: string) => ({
|
|
12
|
+
matches,
|
|
13
|
+
media: query,
|
|
14
|
+
addEventListener: vi.fn(),
|
|
15
|
+
removeEventListener: vi.fn(),
|
|
16
|
+
addListener: vi.fn(),
|
|
17
|
+
removeListener: vi.fn(),
|
|
18
|
+
dispatchEvent: vi.fn(),
|
|
19
|
+
})),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('RootLayout', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
matches = false;
|
|
26
|
+
installMatchMedia();
|
|
27
|
+
});
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
vi.unstubAllGlobals();
|
|
30
|
+
cleanup();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('plain: renders the single child on web and mobile', () => {
|
|
34
|
+
render(
|
|
35
|
+
<RootLayout variant="plain">
|
|
36
|
+
<span>Main</span>
|
|
37
|
+
</RootLayout>,
|
|
38
|
+
);
|
|
39
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
40
|
+
cleanup();
|
|
41
|
+
|
|
42
|
+
matches = true;
|
|
43
|
+
installMatchMedia();
|
|
44
|
+
render(
|
|
45
|
+
<RootLayout variant="plain">
|
|
46
|
+
<span>Main</span>
|
|
47
|
+
</RootLayout>,
|
|
48
|
+
);
|
|
49
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('sidebar: renders sidebar + main on web, main only on mobile', () => {
|
|
53
|
+
render(
|
|
54
|
+
<RootLayout variant="sidebar">
|
|
55
|
+
<nav>Sidebar</nav>
|
|
56
|
+
<main>Main</main>
|
|
57
|
+
</RootLayout>,
|
|
58
|
+
);
|
|
59
|
+
expect(screen.getByText('Sidebar')).toBeDefined();
|
|
60
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
61
|
+
cleanup();
|
|
62
|
+
|
|
63
|
+
matches = true;
|
|
64
|
+
installMatchMedia();
|
|
65
|
+
render(
|
|
66
|
+
<RootLayout variant="sidebar">
|
|
67
|
+
<nav>Sidebar</nav>
|
|
68
|
+
<main>Main</main>
|
|
69
|
+
</RootLayout>,
|
|
70
|
+
);
|
|
71
|
+
expect(screen.queryByText('Sidebar')).toBeNull();
|
|
72
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('header: renders header + main identically on web and mobile', () => {
|
|
76
|
+
render(
|
|
77
|
+
<RootLayout variant="header">
|
|
78
|
+
<header>Header</header>
|
|
79
|
+
<main>Main</main>
|
|
80
|
+
</RootLayout>,
|
|
81
|
+
);
|
|
82
|
+
expect(screen.getByText('Header')).toBeDefined();
|
|
83
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
84
|
+
cleanup();
|
|
85
|
+
|
|
86
|
+
matches = true;
|
|
87
|
+
installMatchMedia();
|
|
88
|
+
render(
|
|
89
|
+
<RootLayout variant="header">
|
|
90
|
+
<header>Header</header>
|
|
91
|
+
<main>Main</main>
|
|
92
|
+
</RootLayout>,
|
|
93
|
+
);
|
|
94
|
+
expect(screen.getByText('Header')).toBeDefined();
|
|
95
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('header: applies headerHeight on web and mobileHeaderHeight on mobile', () => {
|
|
99
|
+
render(
|
|
100
|
+
<RootLayout variant="header">
|
|
101
|
+
<header>Header</header>
|
|
102
|
+
<main>Main</main>
|
|
103
|
+
</RootLayout>,
|
|
104
|
+
);
|
|
105
|
+
expect(screen.getByText('Header').parentElement?.style.height).toBe('48px');
|
|
106
|
+
cleanup();
|
|
107
|
+
|
|
108
|
+
matches = true;
|
|
109
|
+
installMatchMedia();
|
|
110
|
+
render(
|
|
111
|
+
<RootLayout variant="header">
|
|
112
|
+
<header>Header</header>
|
|
113
|
+
<main>Main</main>
|
|
114
|
+
</RootLayout>,
|
|
115
|
+
);
|
|
116
|
+
expect(screen.getByText('Header').parentElement?.style.height).toBe('60px');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('shell: renders main only on web, header + main + footer on mobile', () => {
|
|
120
|
+
render(
|
|
121
|
+
<RootLayout variant="shell">
|
|
122
|
+
<header>Header</header>
|
|
123
|
+
<main>Main</main>
|
|
124
|
+
<footer>Footer</footer>
|
|
125
|
+
</RootLayout>,
|
|
126
|
+
);
|
|
127
|
+
expect(screen.queryByText('Header')).toBeNull();
|
|
128
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
129
|
+
expect(screen.queryByText('Footer')).toBeNull();
|
|
130
|
+
cleanup();
|
|
131
|
+
|
|
132
|
+
matches = true;
|
|
133
|
+
installMatchMedia();
|
|
134
|
+
render(
|
|
135
|
+
<RootLayout variant="shell">
|
|
136
|
+
<header>Header</header>
|
|
137
|
+
<main>Main</main>
|
|
138
|
+
<footer>Footer</footer>
|
|
139
|
+
</RootLayout>,
|
|
140
|
+
);
|
|
141
|
+
expect(screen.getByText('Header')).toBeDefined();
|
|
142
|
+
expect(screen.getByText('Main')).toBeDefined();
|
|
143
|
+
expect(screen.getByText('Footer')).toBeDefined();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('warns in development when the child count does not match the variant', () => {
|
|
147
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
148
|
+
|
|
149
|
+
render(
|
|
150
|
+
<RootLayout variant="sidebar">
|
|
151
|
+
<span>Only one child</span>
|
|
152
|
+
</RootLayout>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('variant="sidebar"'));
|
|
156
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('expects 2'));
|
|
157
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('received 1'));
|
|
158
|
+
|
|
159
|
+
warn.mockRestore();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
line-height: 1.5;
|
|
19
19
|
color: var(--sv-color-text-primary);
|
|
20
20
|
background-color: var(--sv-color-surface);
|
|
21
|
-
border:
|
|
21
|
+
border: var(--sv-border-width-hairline) solid var(--sv-color-border-strong);
|
|
22
22
|
border-radius: var(--sv-radius-md);
|
|
23
23
|
cursor: pointer;
|
|
24
24
|
/* Remove native OS chevron */
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/* Sheet — fills the same rect as a plugin's content area (between the shell's
|
|
2
2
|
* sticky header and footer nav — see --sv-shell-header-height /
|
|
3
|
-
* --sv-shell-footer-height, set by the shell
|
|
3
|
+
* --sv-shell-footer-height, set by the platform shell or, for a plugin that
|
|
4
|
+
* self-renders MobileHeader/MobileFooter instead (shellConfig.mobileHeader/
|
|
5
|
+
* mobileFooter: false), by that component itself — see
|
|
6
|
+
* usePublishShellChromeHeight), sliding in from an edge
|
|
4
7
|
* instead of Dialog's centered box or Drawer's partial-height sheet-over-
|
|
5
8
|
* scrim. z-index matches Dialog/Drawer's scrim so it layers consistently
|
|
6
9
|
* with any other overlay. No scrim of its own — see the component doc
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { type ReactNode, useRef } from 'react';
|
|
4
4
|
import { useMountTransition, usePrefersReducedMotion } from '../../motion';
|
|
5
5
|
import {
|
|
6
|
+
OVERLAY_MOTION_DURATION_MS,
|
|
6
7
|
useOverlayFocusCapture,
|
|
7
8
|
useOverlayKeyboardTrap,
|
|
8
9
|
useOverlayScrollLock,
|
|
@@ -40,10 +41,6 @@ export interface SheetProps {
|
|
|
40
41
|
children: ReactNode;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
// Matches --sv-motion-duration-base (Sheet.module.css) — see Dialog.tsx's
|
|
44
|
-
// identical constant for why this stays a plain JS number.
|
|
45
|
-
const MOTION_DURATION_MS = 250;
|
|
46
|
-
|
|
47
44
|
/**
|
|
48
45
|
* Sheet — a full-page overlay that replaces a plugin's content area between
|
|
49
46
|
* the shell's fixed header and footer (RFC 0013's mobile chrome stays visible
|
|
@@ -73,7 +70,10 @@ export function Sheet({
|
|
|
73
70
|
}: SheetProps) {
|
|
74
71
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
75
72
|
const reducedMotion = usePrefersReducedMotion();
|
|
76
|
-
const { mounted, phase } = useMountTransition(
|
|
73
|
+
const { mounted, phase } = useMountTransition(
|
|
74
|
+
open,
|
|
75
|
+
reducedMotion ? 0 : OVERLAY_MOTION_DURATION_MS,
|
|
76
|
+
);
|
|
77
77
|
|
|
78
78
|
useOverlayScrollLock(mounted);
|
|
79
79
|
useOverlayFocusCapture(panelRef, mounted);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
minmax(0, calc(100% - var(--split-pane-primary-size, 50%)));
|
|
6
6
|
width: 100%;
|
|
7
7
|
min-height: 320px;
|
|
8
|
-
border:
|
|
8
|
+
border: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
9
9
|
border-radius: var(--sv-radius-lg);
|
|
10
10
|
overflow: hidden;
|
|
11
11
|
background: var(--sv-color-surface);
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
.pane + .pane {
|
|
28
|
-
border-left:
|
|
28
|
+
border-left: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
.handle {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
min-width: 12px;
|
|
35
35
|
padding: 0;
|
|
36
36
|
border: 0;
|
|
37
|
-
border-inline:
|
|
37
|
+
border-inline: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
38
38
|
cursor: col-resize;
|
|
39
39
|
background: var(--sv-color-surface-sunken);
|
|
40
40
|
}
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
.pane + .pane {
|
|
82
|
-
border-top:
|
|
82
|
+
border-top: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
83
83
|
border-left: 0;
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
min-height: 24px;
|
|
6
6
|
gap: var(--sv-space-1);
|
|
7
7
|
padding: 2px var(--sv-space-2);
|
|
8
|
-
border:
|
|
8
|
+
border: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
9
9
|
border-radius: var(--sv-radius-full);
|
|
10
10
|
font-family: var(--sv-font-family);
|
|
11
11
|
font-size: var(--sv-font-size-xs);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.strip {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: stretch;
|
|
4
|
+
overflow-x: auto;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.step {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
flex-shrink: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.connector {
|
|
14
|
+
width: var(--sv-space-4);
|
|
15
|
+
height: 2px;
|
|
16
|
+
flex-shrink: 0;
|
|
17
|
+
background: var(--sv-color-border);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.connectorDashed {
|
|
21
|
+
background-image: repeating-linear-gradient(
|
|
22
|
+
to right,
|
|
23
|
+
var(--sv-color-border) 0,
|
|
24
|
+
var(--sv-color-border) 4px,
|
|
25
|
+
transparent 4px,
|
|
26
|
+
transparent 7px
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.addChip {
|
|
31
|
+
all: unset;
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: var(--sv-space-1);
|
|
37
|
+
padding: var(--sv-space-3) var(--sv-space-4);
|
|
38
|
+
border: var(--sv-border-width-hairline) dashed var(--sv-color-border);
|
|
39
|
+
border-radius: var(--sv-radius-lg);
|
|
40
|
+
color: var(--sv-color-text-muted);
|
|
41
|
+
font-size: var(--sv-font-size-sm);
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.addChip:hover {
|
|
46
|
+
background: var(--sv-color-surface-sunken);
|
|
47
|
+
color: var(--sv-color-text-primary);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.addChip:focus-visible {
|
|
51
|
+
outline: 2px solid var(--sv-color-focus-ring);
|
|
52
|
+
outline-offset: 2px;
|
|
53
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { Icon } from '../Icon/Icon';
|
|
3
|
+
import styles from './StepStrip.module.css';
|
|
4
|
+
|
|
5
|
+
export interface StepStripItem {
|
|
6
|
+
id: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface StepStripProps<T extends StepStripItem> {
|
|
10
|
+
items: T[];
|
|
11
|
+
activeId: string | null;
|
|
12
|
+
/** Full control over each chip's content, click handling, and (if needed) drag-reorder wiring. */
|
|
13
|
+
renderItem: (item: T, state: { isActive: boolean; index: number }) => ReactNode;
|
|
14
|
+
/** Trailing dashed "add" chip — omit to render the strip with no add affordance. */
|
|
15
|
+
onAdd?: () => void;
|
|
16
|
+
addLabel?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
'aria-label'?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* StepStrip — a horizontal, connected sequence of steps (a route or
|
|
23
|
+
* itinerary strip): ordered chips joined by a thin connector line, with an
|
|
24
|
+
* optional trailing dashed "add" chip. Purely presentational — it owns
|
|
25
|
+
* layout and the connector lines, nothing else.
|
|
26
|
+
*
|
|
27
|
+
* Each chip's content, active styling, and click handling are entirely up
|
|
28
|
+
* to `renderItem`; if a consumer needs drag-reorder, `renderItem` attaches
|
|
29
|
+
* its own dnd-kit `useSortable` ref/listeners to whatever it returns. This
|
|
30
|
+
* mirrors `DragHandleRow`'s existing split (DS owns chrome, consumer wires
|
|
31
|
+
* dnd-kit via passed-through props) generalized for a horizontal,
|
|
32
|
+
* handle-less strip where the whole rendered item — not a separate handle
|
|
33
|
+
* icon — is both the click target and the drag surface.
|
|
34
|
+
*/
|
|
35
|
+
export function StepStrip<T extends StepStripItem>({
|
|
36
|
+
items,
|
|
37
|
+
activeId,
|
|
38
|
+
renderItem,
|
|
39
|
+
onAdd,
|
|
40
|
+
addLabel = 'Add',
|
|
41
|
+
className,
|
|
42
|
+
'aria-label': ariaLabel,
|
|
43
|
+
}: StepStripProps<T>) {
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
className={[styles.strip, className].filter(Boolean).join(' ')}
|
|
47
|
+
role="list"
|
|
48
|
+
aria-label={ariaLabel}
|
|
49
|
+
>
|
|
50
|
+
{items.map((item, index) => (
|
|
51
|
+
<div key={item.id} className={styles.step} role="listitem">
|
|
52
|
+
{renderItem(item, { isActive: item.id === activeId, index })}
|
|
53
|
+
{(index < items.length - 1 || onAdd) && (
|
|
54
|
+
<div
|
|
55
|
+
className={[
|
|
56
|
+
styles.connector,
|
|
57
|
+
index === items.length - 1 ? styles.connectorDashed : '',
|
|
58
|
+
]
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join(' ')}
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
))}
|
|
66
|
+
{onAdd && (
|
|
67
|
+
<button type="button" className={styles.addChip} onClick={onAdd}>
|
|
68
|
+
<Icon name="plus" size="sm" aria-hidden={true} />
|
|
69
|
+
{addLabel}
|
|
70
|
+
</button>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
line-height: 1.5;
|
|
8
8
|
color: var(--sv-color-text-primary);
|
|
9
9
|
background-color: var(--sv-color-surface);
|
|
10
|
-
border:
|
|
10
|
+
border: var(--sv-border-width-hairline) solid var(--sv-color-border-strong);
|
|
11
11
|
border-radius: var(--sv-radius-md);
|
|
12
12
|
transition:
|
|
13
13
|
border-color 0.15s ease,
|
|
@@ -99,5 +99,5 @@
|
|
|
99
99
|
|
|
100
100
|
.divider {
|
|
101
101
|
margin: var(--sv-space-1) var(--sv-space-2);
|
|
102
|
-
border-top:
|
|
102
|
+
border-top: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
103
103
|
}
|
|
@@ -39,10 +39,19 @@
|
|
|
39
39
|
flex-direction: column;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
.
|
|
42
|
+
.indicatorSlot {
|
|
43
43
|
/* Overlays the bottom of .wrap rather than taking its own layout row —
|
|
44
44
|
matches both sovereign-tasks' and sovereign-shopper's existing dots
|
|
45
|
-
placement.
|
|
45
|
+
placement. Applied by the carousel itself around WHATEVER
|
|
46
|
+
renderIndicator returns (the default SwipableMobileCarouselDots or a
|
|
47
|
+
caller-supplied override) — a custom renderIndicator has no way to
|
|
48
|
+
import this module's locally-scoped class itself (CSS Modules classnames
|
|
49
|
+
aren't cross-file importable), so positioning must live here, not be
|
|
50
|
+
something each indicator implementation opts into. Previously this class
|
|
51
|
+
was only ever applied to the *default* indicator branch, which silently
|
|
52
|
+
left any custom renderIndicator unpositioned — a static, in-flow sibling
|
|
53
|
+
pushed below the full-height .scroller and clipped out of view instead
|
|
54
|
+
of overlaid on top of it. */
|
|
46
55
|
position: absolute;
|
|
47
56
|
left: 0;
|
|
48
57
|
right: 0;
|
|
@@ -50,6 +59,6 @@
|
|
|
50
59
|
pointer-events: none;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
.
|
|
62
|
+
.indicatorSlot > * {
|
|
54
63
|
pointer-events: auto;
|
|
55
64
|
}
|