zabi-components 8.1.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1011 -0
- package/README.md +1 -1
- package/THEME.md +18 -5
- package/THEMING.md +308 -74
- package/dist/atoms/ActionPanel.svelte +1 -1
- package/dist/atoms/Avatar.svelte +19 -3
- package/dist/atoms/Avatar.svelte.d.ts +12 -0
- package/dist/atoms/Badge.svelte +37 -8
- package/dist/atoms/Badge.svelte.d.ts +6 -0
- package/dist/atoms/Block.svelte +74 -0
- package/dist/atoms/Block.svelte.d.ts +29 -0
- package/dist/atoms/Button.svelte +52 -15
- package/dist/atoms/Button.svelte.d.ts +3 -2
- package/dist/atoms/Card.svelte +181 -25
- package/dist/atoms/Card.svelte.d.ts +55 -3
- package/dist/atoms/Checkbox.svelte +33 -0
- package/dist/atoms/Checkbox.svelte.d.ts +27 -0
- package/dist/atoms/Chip.svelte +351 -0
- package/dist/atoms/Chip.svelte.d.ts +57 -0
- package/dist/atoms/CodeField.svelte +385 -0
- package/dist/atoms/CodeField.svelte.d.ts +61 -0
- package/dist/atoms/ColorPicker.svelte +1 -0
- package/dist/atoms/DateField.svelte +41 -3
- package/dist/atoms/DateField.svelte.d.ts +31 -3
- package/dist/atoms/FloatingActionButton.svelte +5 -3
- package/dist/atoms/FloatingActionButton.svelte.d.ts +1 -1
- package/dist/atoms/Heading.svelte +27 -1
- package/dist/atoms/Heading.svelte.d.ts +7 -0
- package/dist/atoms/IconButton.svelte +81 -9
- package/dist/atoms/IconButton.svelte.d.ts +13 -0
- package/dist/atoms/Input.svelte +10 -5
- package/dist/atoms/Input.svelte.d.ts +3 -2
- package/dist/atoms/List.svelte +3 -2
- package/dist/atoms/ListItem.svelte +31 -7
- package/dist/atoms/ListItem.svelte.d.ts +6 -0
- package/dist/atoms/PickerField.svelte +239 -0
- package/dist/atoms/PickerField.svelte.d.ts +67 -0
- package/dist/atoms/Progress.svelte +78 -17
- package/dist/atoms/Progress.svelte.d.ts +11 -1
- package/dist/atoms/Radio.svelte +32 -0
- package/dist/atoms/Radio.svelte.d.ts +26 -0
- package/dist/atoms/Rating.svelte +132 -6
- package/dist/atoms/Rating.svelte.d.ts +24 -3
- package/dist/atoms/Select.svelte +24 -26
- package/dist/atoms/Select.svelte.d.ts +12 -0
- package/dist/atoms/SelectionControl.svelte +99 -6
- package/dist/atoms/SelectionControl.svelte.d.ts +25 -0
- package/dist/atoms/Skeleton.svelte +114 -18
- package/dist/atoms/Skeleton.svelte.d.ts +30 -3
- package/dist/atoms/SkeletonGroup.svelte +43 -0
- package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
- package/dist/atoms/Slider.svelte +35 -0
- package/dist/atoms/Sparkline.svelte +109 -0
- package/dist/atoms/Sparkline.svelte.d.ts +27 -0
- package/dist/atoms/SpeechBubble.svelte +187 -0
- package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
- package/dist/atoms/Stat.svelte +72 -0
- package/dist/atoms/Stat.svelte.d.ts +19 -0
- package/dist/atoms/TemporalField.svelte +342 -10
- package/dist/atoms/TemporalField.svelte.d.ts +20 -4
- package/dist/atoms/Textarea.svelte +100 -5
- package/dist/atoms/Textarea.svelte.d.ts +21 -0
- package/dist/atoms/TimeColumn.svelte +150 -0
- package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
- package/dist/atoms/TimeField.svelte +46 -3
- package/dist/atoms/TimeField.svelte.d.ts +34 -3
- package/dist/atoms/Toast.svelte +6 -5
- package/dist/atoms/Toggle.svelte +61 -10
- package/dist/atoms/Toggle.svelte.d.ts +15 -0
- package/dist/atoms/Tooltip.svelte +20 -1
- package/dist/atoms/Tooltip.svelte.d.ts +7 -0
- package/dist/atoms/index.d.ts +21 -0
- package/dist/atoms/index.js +17 -0
- package/dist/atoms/selection-control.styles.d.ts +15 -4
- package/dist/atoms/selection-control.styles.js +16 -5
- package/dist/components/atoms/index.d.ts +21 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/atoms/selection-control.styles.d.ts +15 -4
- package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
- package/dist/components/index.d.ts +27 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/map.d.ts +11 -0
- package/dist/components/map.d.ts.map +1 -0
- package/dist/components/molecules/index.d.ts +7 -2
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/toast-store.d.ts +30 -0
- package/dist/components/molecules/toast-store.d.ts.map +1 -1
- package/dist/components/organisms/index.d.ts +6 -1
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/types/variants.d.ts +12 -3
- package/dist/components/types/variants.d.ts.map +1 -1
- package/dist/components/util/app-shell.d.ts +55 -4
- package/dist/components/util/app-shell.d.ts.map +1 -1
- package/dist/components/util/avatar.d.ts +3 -0
- package/dist/components/util/avatar.d.ts.map +1 -1
- package/dist/components/util/bottom-tab-bar.d.ts +6 -0
- package/dist/components/util/bottom-tab-bar.d.ts.map +1 -1
- package/dist/components/util/chart.d.ts +140 -0
- package/dist/components/util/chart.d.ts.map +1 -0
- package/dist/components/util/chip.d.ts +66 -0
- package/dist/components/util/chip.d.ts.map +1 -0
- package/dist/components/util/cn.d.ts.map +1 -1
- package/dist/components/util/code-field.d.ts +63 -0
- package/dist/components/util/code-field.d.ts.map +1 -0
- package/dist/components/util/collapsible.d.ts +6 -0
- package/dist/components/util/collapsible.d.ts.map +1 -1
- package/dist/components/util/display-voice.d.ts +10 -0
- package/dist/components/util/display-voice.d.ts.map +1 -0
- package/dist/components/util/field-control.d.ts +30 -0
- package/dist/components/util/field-control.d.ts.map +1 -0
- package/dist/components/util/field.d.ts +6 -0
- package/dist/components/util/field.d.ts.map +1 -1
- package/dist/components/util/focus-utils.d.ts +2 -0
- package/dist/components/util/focus-utils.d.ts.map +1 -1
- package/dist/components/util/map-view.d.ts +144 -0
- package/dist/components/util/map-view.d.ts.map +1 -0
- package/dist/components/util/photo.d.ts +19 -0
- package/dist/components/util/photo.d.ts.map +1 -1
- package/dist/components/util/progress.d.ts +19 -0
- package/dist/components/util/progress.d.ts.map +1 -0
- package/dist/components/util/scroll-edge.d.ts +33 -0
- package/dist/components/util/scroll-edge.d.ts.map +1 -0
- package/dist/components/util/sheet-drag.d.ts +25 -1
- package/dist/components/util/sheet-drag.d.ts.map +1 -1
- package/dist/components/util/skeleton.d.ts +15 -0
- package/dist/components/util/skeleton.d.ts.map +1 -0
- package/dist/components/util/sliding-indicator.d.ts +25 -0
- package/dist/components/util/sliding-indicator.d.ts.map +1 -0
- package/dist/components/util/temporal-field.d.ts +89 -0
- package/dist/components/util/temporal-field.d.ts.map +1 -0
- package/dist/components/util/toaster.d.ts +40 -0
- package/dist/components/util/toaster.d.ts.map +1 -1
- package/dist/components/util/tone-surface.d.ts +34 -0
- package/dist/components/util/tone-surface.d.ts.map +1 -0
- package/dist/components/util/top-navbar.d.ts +26 -2
- package/dist/components/util/top-navbar.d.ts.map +1 -1
- package/dist/components/util/zabi-strings.d.ts +9 -0
- package/dist/components/util/zabi-strings.d.ts.map +1 -1
- package/dist/create-theme/cli.js +6 -2
- package/dist/create-theme/index.d.ts +17 -3
- package/dist/create-theme/index.js +252 -35
- package/dist/create-theme/lib/resolve.js +97 -0
- package/dist/create-theme/theme-data.js +1618 -90
- package/dist/index.d.ts +27 -3
- package/dist/index.js +18 -0
- package/dist/map.d.ts +10 -0
- package/dist/map.js +9 -0
- package/dist/molecules/Alert.svelte +31 -12
- package/dist/molecules/Alert.svelte.d.ts +12 -1
- package/dist/molecules/AppBar.svelte +287 -28
- package/dist/molecules/AppBar.svelte.d.ts +62 -0
- package/dist/molecules/AvatarGroup.svelte +1 -1
- package/dist/molecules/BarChart.svelte +381 -0
- package/dist/molecules/BarChart.svelte.d.ts +45 -0
- package/dist/molecules/BottomSheet.svelte +88 -20
- package/dist/molecules/BottomSheet.svelte.d.ts +17 -1
- package/dist/molecules/BottomTabBar.svelte +134 -28
- package/dist/molecules/BottomTabBar.svelte.d.ts +36 -2
- package/dist/molecules/Calendar.svelte.d.ts +1 -1
- package/dist/molecules/ChipGroup.svelte +325 -0
- package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
- package/dist/molecules/Collapsible.svelte +158 -54
- package/dist/molecules/Collapsible.svelte.d.ts +23 -6
- package/dist/molecules/CollapsibleGroup.svelte +16 -6
- package/dist/molecules/ConfirmDialog.svelte +5 -1
- package/dist/molecules/ConfirmDialog.svelte.d.ts +3 -0
- package/dist/molecules/Drawer.svelte +33 -12
- package/dist/molecules/Dropdown.svelte +9 -1
- package/dist/molecules/Dropdown.svelte.d.ts +2 -0
- package/dist/molecules/DropdownItem.svelte +1 -1
- package/dist/molecules/ImageUpload.svelte +230 -120
- package/dist/molecules/ImageUpload.svelte.d.ts +38 -3
- package/dist/molecules/LineChart.svelte +459 -0
- package/dist/molecules/LineChart.svelte.d.ts +53 -0
- package/dist/molecules/MapView.svelte +565 -0
- package/dist/molecules/MapView.svelte.d.ts +88 -0
- package/dist/molecules/Modal.svelte +11 -6
- package/dist/molecules/PhotoGrid.svelte +100 -10
- package/dist/molecules/PhotoGrid.svelte.d.ts +11 -0
- package/dist/molecules/PhotoViewer.svelte +121 -24
- package/dist/molecules/PhotoViewer.svelte.d.ts +18 -0
- package/dist/molecules/PieChart.svelte +421 -0
- package/dist/molecules/PieChart.svelte.d.ts +51 -0
- package/dist/molecules/RadioGroup.svelte +20 -4
- package/dist/molecules/RadioGroup.svelte.d.ts +5 -0
- package/dist/molecules/SegmentedControl.svelte +136 -3
- package/dist/molecules/SegmentedControl.svelte.d.ts +6 -0
- package/dist/molecules/SidebarFooter.svelte +1 -1
- package/dist/molecules/SidebarNavSection.svelte +1 -1
- package/dist/molecules/SortableList.svelte +1 -0
- package/dist/molecules/Stepper.svelte +21 -7
- package/dist/molecules/StickyActionBar.svelte +67 -8
- package/dist/molecules/StickyActionBar.svelte.d.ts +14 -1
- package/dist/molecules/Tabs.svelte +79 -2
- package/dist/molecules/Toaster.svelte +171 -7
- package/dist/molecules/Toaster.svelte.d.ts +60 -2
- package/dist/molecules/ToasterToast.svelte +292 -60
- package/dist/molecules/ToasterToast.svelte.d.ts +6 -0
- package/dist/molecules/UnsavedChangesBar.svelte +13 -2
- package/dist/molecules/index.d.ts +7 -2
- package/dist/molecules/index.js +4 -0
- package/dist/molecules/toast-store.d.ts +30 -0
- package/dist/molecules/toast-store.js +4 -1
- package/dist/organisms/AppNavigation.svelte +440 -0
- package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
- package/dist/organisms/AppShell.svelte +539 -39
- package/dist/organisms/AppShell.svelte.d.ts +102 -6
- package/dist/organisms/AppShellHeaderPart.svelte +163 -0
- package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
- package/dist/organisms/SidebarNavigation.svelte +23 -36
- package/dist/organisms/SidebarPanel.svelte +10 -14
- package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
- package/dist/organisms/SidebarShell.svelte +2 -3
- package/dist/organisms/TopNavbar.svelte +117 -28
- package/dist/organisms/TopNavbar.svelte.d.ts +22 -1
- package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
- package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
- package/dist/organisms/index.d.ts +6 -1
- package/dist/organisms/index.js +4 -0
- package/dist/types/components.d.ts +17 -1
- package/dist/types/components.d.ts.map +1 -1
- package/dist/types/index.d.ts +497 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/variants.ts +14 -3
- package/dist/util/app-shell.d.ts +55 -4
- package/dist/util/app-shell.js +32 -7
- package/dist/util/avatar.d.ts +3 -0
- package/dist/util/bottom-tab-bar.d.ts +6 -0
- package/dist/util/chart.d.ts +139 -0
- package/dist/util/chart.js +305 -0
- package/dist/util/chip.d.ts +65 -0
- package/dist/util/chip.js +58 -0
- package/dist/util/cn.js +28 -2
- package/dist/util/code-field.d.ts +62 -0
- package/dist/util/code-field.js +112 -0
- package/dist/util/collapsible.d.ts +6 -0
- package/dist/util/display-voice.d.ts +9 -0
- package/dist/util/display-voice.js +9 -0
- package/dist/util/field-control.d.ts +29 -0
- package/dist/util/field-control.js +32 -0
- package/dist/util/field.d.ts +6 -0
- package/dist/util/focus-utils.d.ts +2 -0
- package/dist/util/focus-utils.js +4 -0
- package/dist/util/map-view.d.ts +143 -0
- package/dist/util/map-view.js +121 -0
- package/dist/util/photo.d.ts +19 -0
- package/dist/util/photo.js +3 -0
- package/dist/util/progress.d.ts +18 -0
- package/dist/util/progress.js +17 -0
- package/dist/util/scroll-edge.d.ts +32 -0
- package/dist/util/scroll-edge.js +87 -0
- package/dist/util/sheet-drag.d.ts +25 -1
- package/dist/util/sheet-drag.js +71 -1
- package/dist/util/skeleton.d.ts +14 -0
- package/dist/util/skeleton.js +22 -0
- package/dist/util/sliding-indicator.d.ts +24 -0
- package/dist/util/sliding-indicator.js +44 -0
- package/dist/util/temporal-field.d.ts +88 -0
- package/dist/util/temporal-field.js +136 -0
- package/dist/util/toaster.d.ts +40 -0
- package/dist/util/toaster.js +55 -0
- package/dist/util/tone-surface.d.ts +33 -0
- package/dist/util/tone-surface.js +35 -0
- package/dist/util/top-navbar.d.ts +26 -2
- package/dist/util/top-navbar.js +23 -0
- package/dist/util/zabi-strings.d.ts +9 -0
- package/dist/zabi-components-colors.css +9 -3
- package/dist/zabi-components-theme-dark-only.css +474 -164
- package/dist/zabi-components-theme-dark.css +474 -164
- package/dist/zabi-components-theme-only.css +933 -99
- package/dist/zabi-components-theme.css +933 -99
- package/dist/zabi-components.css +2275 -321
- package/package.json +11 -1
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
import { zabiStringsFor } from "../util/zabi-strings.js";
|
|
4
4
|
import ThemeToggle from "../atoms/ThemeToggle.svelte";
|
|
5
5
|
import IconButton from "../atoms/IconButton.svelte";
|
|
6
|
+
import BottomSheet from "../molecules/BottomSheet.svelte";
|
|
7
|
+
import TopNavbarSheetContent from "./TopNavbarSheetContent.svelte";
|
|
6
8
|
import ExternalLink from "@lucide/svelte/icons/external-link";
|
|
7
9
|
import Menu from "@lucide/svelte/icons/menu";
|
|
8
10
|
import X from "@lucide/svelte/icons/x";
|
|
9
11
|
import type { Component, Snippet } from "svelte";
|
|
10
12
|
import { generateId } from "../util/ssr-safe.js";
|
|
13
|
+
import { getAppShell } from "../util/app-shell.js";
|
|
11
14
|
import { cn } from "../util/cn.js";
|
|
15
|
+
import { resolveScrolledUnder, watchScrollEdge, type ScrollEdgeMode } from "../util/scroll-edge.js";
|
|
12
16
|
import type { ThemeToggleLabels } from "../util/theme-mode.js";
|
|
13
17
|
import { DEFAULT_TOP_NAVBAR_STRINGS, type TopNavbarStrings } from "../util/top-navbar.js";
|
|
14
18
|
import { isInsideToastRegion } from "../util/focus-utils.js";
|
|
@@ -24,6 +28,13 @@
|
|
|
24
28
|
|
|
25
29
|
export type TopNavbarCollapseAt = "sm" | "md" | "lg" | "xl";
|
|
26
30
|
|
|
31
|
+
/**
|
|
32
|
+
* How the links open below `collapseAt`: `sheet` slides a modal sheet up
|
|
33
|
+
* from the bottom of the screen; `inline` opens a panel under the bar,
|
|
34
|
+
* which pushes the page down (the menu of 8.1 and earlier).
|
|
35
|
+
*/
|
|
36
|
+
export type TopNavbarMenu = "sheet" | "inline";
|
|
37
|
+
|
|
27
38
|
interface Props {
|
|
28
39
|
brand?: string;
|
|
29
40
|
brandHref?: string;
|
|
@@ -43,7 +54,7 @@
|
|
|
43
54
|
themeStorageKey?: string | null;
|
|
44
55
|
/** The texts of the theme toggle's name, for another language. As `labels` of ThemeToggle. */
|
|
45
56
|
themeLabels?: Partial<ThemeToggleLabels>;
|
|
46
|
-
/** The words the bar says by itself: the menu button's two names and the note on a link that opens a new tab. */
|
|
57
|
+
/** The words the bar says by itself: the menu button's two names, the name of the menu sheet and the note on a link that opens a new tab. */
|
|
47
58
|
strings?: Partial<TopNavbarStrings>;
|
|
48
59
|
class?: string;
|
|
49
60
|
/** @deprecated use `class`. */
|
|
@@ -59,8 +70,22 @@
|
|
|
59
70
|
* more items than fit at 768px.
|
|
60
71
|
*/
|
|
61
72
|
collapseAt?: TopNavbarCollapseAt;
|
|
73
|
+
/**
|
|
74
|
+
* How the links open below `collapseAt`. `sheet` (the default) is a
|
|
75
|
+
* bottom sheet: a modal dialog that is as tall as its content, up to
|
|
76
|
+
* the full screen below the status bar, and scrolls inside. `inline` is a panel under the
|
|
77
|
+
* bar, a disclosure that pushes the page down and traps nothing.
|
|
78
|
+
*/
|
|
79
|
+
menu?: TopNavbarMenu;
|
|
62
80
|
preventNavigation?: boolean;
|
|
63
81
|
onclick?: (event: Event) => void;
|
|
82
|
+
/**
|
|
83
|
+
* When the bar shows the glass and the hairline. `auto` follows the
|
|
84
|
+
* scroll position: flush at the top, glass once content is under it
|
|
85
|
+
* (and while the inline phone menu is open). `always` is glass all the time,
|
|
86
|
+
* `never` flush all the time.
|
|
87
|
+
*/
|
|
88
|
+
scrollEdge?: ScrollEdgeMode;
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
let {
|
|
@@ -79,8 +104,10 @@
|
|
|
79
104
|
navVariant = "header",
|
|
80
105
|
currentPath = "",
|
|
81
106
|
collapseAt = "md",
|
|
107
|
+
menu = "sheet",
|
|
82
108
|
preventNavigation = false,
|
|
83
109
|
onclick,
|
|
110
|
+
scrollEdge = "auto",
|
|
84
111
|
nav,
|
|
85
112
|
actions,
|
|
86
113
|
...restProps
|
|
@@ -94,9 +121,28 @@
|
|
|
94
121
|
|
|
95
122
|
/** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
|
|
96
123
|
const provided = zabiStringsFor("topNavbar");
|
|
97
|
-
const text = $derived(mergeStrings(DEFAULT_TOP_NAVBAR_STRINGS, provided(), strings));
|
|
124
|
+
const text = $derived(mergeStrings<Required<TopNavbarStrings>>(DEFAULT_TOP_NAVBAR_STRINGS, provided(), strings));
|
|
98
125
|
|
|
99
126
|
let isMenuOpen = $state(false);
|
|
127
|
+
const sheet = $derived(menu !== "inline");
|
|
128
|
+
/** The inline panel is open: only then do the window listeners below have anything to close. */
|
|
129
|
+
const inlineOpen = $derived(isMenuOpen && !sheet);
|
|
130
|
+
|
|
131
|
+
/** Content is under the bar: from the shell if there is one, else from the window. */
|
|
132
|
+
const shell = getAppShell();
|
|
133
|
+
let ownScrolled = $state(false);
|
|
134
|
+
$effect(() => {
|
|
135
|
+
if (shell || scrollEdge !== "auto" || embedded) return;
|
|
136
|
+
return watchScrollEdge(null, (edge) => (ownScrolled = edge.top));
|
|
137
|
+
});
|
|
138
|
+
// An open inline menu sits on the material, whatever the scroll position.
|
|
139
|
+
// The sheet covers the page and the bar under its backdrop: the bar stays as it was.
|
|
140
|
+
const scrolledUnder = $derived(
|
|
141
|
+
resolveScrolledUnder(
|
|
142
|
+
scrollEdge,
|
|
143
|
+
inlineOpen || (shell ? shell.scrolledTop : ownScrolled),
|
|
144
|
+
),
|
|
145
|
+
);
|
|
100
146
|
let navElement = $state<HTMLElement | null>(null);
|
|
101
147
|
let panelElement = $state<HTMLElement | null>(null);
|
|
102
148
|
let menuButtonHolder = $state<HTMLElement | null>(null);
|
|
@@ -127,12 +173,13 @@
|
|
|
127
173
|
}
|
|
128
174
|
|
|
129
175
|
/**
|
|
130
|
-
* The menu is a disclosure, not a dialog: nothing is trapped or
|
|
131
|
-
* it has to get out of the way by itself whenever the user has
|
|
176
|
+
* The inline menu is a disclosure, not a dialog: nothing is trapped or
|
|
177
|
+
* locked, so it has to get out of the way by itself whenever the user has
|
|
178
|
+
* moved on. The sheet is a dialog and does all of this itself.
|
|
132
179
|
*/
|
|
133
180
|
function handleWindowKeydown(event: KeyboardEvent) {
|
|
134
181
|
// A menu inside the panel (a Dropdown, say) closes on Escape first.
|
|
135
|
-
if (event.key !== "Escape" || !
|
|
182
|
+
if (event.key !== "Escape" || !inlineOpen || event.defaultPrevented) return;
|
|
136
183
|
const focusInside = !!panelElement?.contains(document.activeElement);
|
|
137
184
|
isMenuOpen = false;
|
|
138
185
|
// The link that had focus is gone; without this, focus falls to <body>.
|
|
@@ -154,7 +201,7 @@
|
|
|
154
201
|
let pressing = false;
|
|
155
202
|
|
|
156
203
|
function handleOutsideClick(event: MouseEvent) {
|
|
157
|
-
if (
|
|
204
|
+
if (inlineOpen && isOutside(event)) isMenuOpen = false;
|
|
158
205
|
}
|
|
159
206
|
|
|
160
207
|
/**
|
|
@@ -163,10 +210,10 @@
|
|
|
163
210
|
* finger has lifted, on a touch screen); that one is left to the click.
|
|
164
211
|
*/
|
|
165
212
|
function handleOutsideFocus(event: FocusEvent) {
|
|
166
|
-
if (
|
|
213
|
+
if (inlineOpen && !pressing && isOutside(event)) isMenuOpen = false;
|
|
167
214
|
}
|
|
168
215
|
|
|
169
|
-
/** Following a link in the menu: the page under it is about to change. */
|
|
216
|
+
/** Following a link in the menu or the sheet: the page under it is about to change. */
|
|
170
217
|
function handlePanelClick(event: MouseEvent) {
|
|
171
218
|
if (event.target instanceof Element && event.target.closest("a[href]")) {
|
|
172
219
|
isMenuOpen = false;
|
|
@@ -181,7 +228,8 @@
|
|
|
181
228
|
});
|
|
182
229
|
|
|
183
230
|
// Past the breakpoint the menu button is gone, so a menu left open could
|
|
184
|
-
// not be closed, and it would still be open on the way back down.
|
|
231
|
+
// not be closed, and it would still be open on the way back down. For the
|
|
232
|
+
// sheet, closing also lets go of the scroll lock and the inert page.
|
|
185
233
|
$effect(() => {
|
|
186
234
|
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
187
235
|
const wide = window.matchMedia(collapse.query);
|
|
@@ -254,7 +302,7 @@
|
|
|
254
302
|
});
|
|
255
303
|
|
|
256
304
|
/** Where a list of links is drawn: in the bar, in the phone menu, or alone (`embedded`). */
|
|
257
|
-
type NavListPlace = "row" | "menu" | "embedded";
|
|
305
|
+
type NavListPlace = "row" | "menu" | "sheet" | "embedded";
|
|
258
306
|
|
|
259
307
|
function getListClasses(place: NavListPlace): string {
|
|
260
308
|
if (place === "embedded" || navVariant === "sidebar") return ulClasses;
|
|
@@ -269,7 +317,7 @@
|
|
|
269
317
|
const base =
|
|
270
318
|
"flex flex-col gap-1 grow h-full items-center justify-center min-h-0 min-w-0 relative shrink-0 cursor-pointer";
|
|
271
319
|
if (place === "row") return `${base} w-auto`;
|
|
272
|
-
if (place === "menu") return `${base} w-full`;
|
|
320
|
+
if (place === "menu" || place === "sheet") return `${base} w-full`;
|
|
273
321
|
return `${base} w-full md:w-auto`;
|
|
274
322
|
}
|
|
275
323
|
|
|
@@ -290,28 +338,30 @@
|
|
|
290
338
|
*/
|
|
291
339
|
function getIconContainerClasses(isActive: boolean): string {
|
|
292
340
|
const base =
|
|
293
|
-
"focus-ring focus-ring--nav group/nav-item flex flex-col items-center justify-center overflow-clip relative rounded-pill shrink-0 transition-colors duration-
|
|
341
|
+
"focus-ring focus-ring--nav group/nav-item flex flex-col items-center justify-center overflow-clip relative rounded-pill shrink-0 transition-colors duration-(--duration-moderate) ease-standard outline-none";
|
|
294
342
|
return isActive
|
|
295
343
|
? // Forced colours drop the fill and recolour the label: the
|
|
296
344
|
// current item keeps an inset outline there, which is drawn.
|
|
297
345
|
cn(
|
|
298
346
|
base,
|
|
299
|
-
"bg-nav-menu-active text-nav-menu-item-active forced-colors:outline-solid forced-colors:outline-2 forced-colors:-outline-offset-2",
|
|
347
|
+
"bg-nav-menu-active font-semibold text-nav-menu-item-active forced-colors:outline-solid forced-colors:outline-2 forced-colors:-outline-offset-2",
|
|
300
348
|
)
|
|
301
349
|
: cn(
|
|
302
350
|
base,
|
|
303
|
-
"text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-nav-menu-active",
|
|
351
|
+
"font-medium text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-nav-menu-active",
|
|
304
352
|
);
|
|
305
353
|
}
|
|
306
354
|
|
|
307
|
-
function getStateLayerClasses(): string {
|
|
308
|
-
|
|
355
|
+
function getStateLayerClasses(place: NavListPlace): string {
|
|
356
|
+
// A row of the sheet is a touch target on every pointer: 44px.
|
|
357
|
+
const height = place === "sheet" ? "h-11" : "h-10 pointer-coarse:h-11";
|
|
358
|
+
return `box-border flex gap-1 ${height} items-center px-4 py-2 relative shrink-0`;
|
|
309
359
|
}
|
|
310
360
|
|
|
311
361
|
function getLabelClasses(): string {
|
|
312
362
|
// `whitespace-pre` used to sit beside `text-nowrap` — same property,
|
|
313
363
|
// and it preserved literal whitespace as a side effect.
|
|
314
|
-
return "
|
|
364
|
+
return "leading-5 relative shrink-0 text-center text-nowrap text-sm text-inherit";
|
|
315
365
|
}
|
|
316
366
|
|
|
317
367
|
function getIconWrapperClasses(): string {
|
|
@@ -325,16 +375,16 @@
|
|
|
325
375
|
{@const isActive = isNavItemActive(item.href)}
|
|
326
376
|
{@const external = isExternal(item)}
|
|
327
377
|
<li class={getNavItemClasses(place)}>
|
|
328
|
-
<!-- In the menu the link is the whole row, not a pill as wide as its label. -->
|
|
378
|
+
<!-- In the menu and the sheet the link is the whole row, not a pill as wide as its label. -->
|
|
329
379
|
<a
|
|
330
380
|
href={item.href}
|
|
331
|
-
class={cn(getIconContainerClasses(isActive), place === "menu" && "w-full")}
|
|
381
|
+
class={cn(getIconContainerClasses(isActive), (place === "menu" || place === "sheet") && "w-full")}
|
|
332
382
|
onclick={handleNavLinkClick}
|
|
333
383
|
aria-current={isActive ? "page" : undefined}
|
|
334
384
|
target={external ? "_blank" : undefined}
|
|
335
385
|
rel={external ? "noopener noreferrer" : undefined}
|
|
336
386
|
>
|
|
337
|
-
<div class={getStateLayerClasses()}>
|
|
387
|
+
<div class={getStateLayerClasses(place)}>
|
|
338
388
|
{#if item.iconFilled && isActive}
|
|
339
389
|
{@const Icon = item.iconFilled}
|
|
340
390
|
<div class={getIconWrapperClasses()}>
|
|
@@ -364,6 +414,14 @@
|
|
|
364
414
|
</ul>
|
|
365
415
|
{/snippet}
|
|
366
416
|
|
|
417
|
+
{#snippet sheetLinks()}
|
|
418
|
+
{#if nav}
|
|
419
|
+
{@render nav()}
|
|
420
|
+
{:else}
|
|
421
|
+
{@render defaultNavList("sheet")}
|
|
422
|
+
{/if}
|
|
423
|
+
{/snippet}
|
|
424
|
+
|
|
367
425
|
<svelte:window
|
|
368
426
|
onkeydown={handleWindowKeydown}
|
|
369
427
|
onmousedowncapture={() => (pressing = true)}
|
|
@@ -384,7 +442,8 @@
|
|
|
384
442
|
{:else}
|
|
385
443
|
<nav
|
|
386
444
|
bind:this={navElement}
|
|
387
|
-
|
|
445
|
+
data-scrolled-under={String(scrolledUnder)}
|
|
446
|
+
class={cn("material-bar sticky top-0 z-sticky", className)}
|
|
388
447
|
aria-label={ariaLabel}
|
|
389
448
|
{...restProps}
|
|
390
449
|
>
|
|
@@ -431,14 +490,17 @@
|
|
|
431
490
|
off the icon scale, and with no disclosure semantics, so
|
|
432
491
|
a screen reader could not tell the menu was open.
|
|
433
492
|
-->
|
|
493
|
+
<!-- The sheet is a dialog the button opens; the page under its
|
|
494
|
+
backdrop is inert, so this button is never the one that closes it. -->
|
|
434
495
|
<IconButton
|
|
435
496
|
variant="ghost"
|
|
436
|
-
label={
|
|
497
|
+
label={inlineOpen ? text.closeMenu : text.openMenu}
|
|
437
498
|
onclick={toggleMenu}
|
|
499
|
+
aria-haspopup={sheet ? "dialog" : undefined}
|
|
438
500
|
aria-expanded={isMenuOpen}
|
|
439
501
|
aria-controls={isMenuOpen ? mobileMenuId : undefined}
|
|
440
502
|
>
|
|
441
|
-
{#if
|
|
503
|
+
{#if inlineOpen}
|
|
442
504
|
<X size={20} />
|
|
443
505
|
{:else}
|
|
444
506
|
<Menu size={20} />
|
|
@@ -451,7 +513,7 @@
|
|
|
451
513
|
Keeping it in the DOM, hidden, would render `nav` and `actions` a
|
|
452
514
|
second time on every page for every desktop visitor: duplicate ids
|
|
453
515
|
from the caller's snippets, and a second ThemeToggle mounted. -->
|
|
454
|
-
{#if
|
|
516
|
+
{#if inlineOpen}
|
|
455
517
|
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
|
|
456
518
|
<div
|
|
457
519
|
bind:this={panelElement}
|
|
@@ -461,7 +523,7 @@
|
|
|
461
523
|
onclick={handlePanelClick}
|
|
462
524
|
>
|
|
463
525
|
<div
|
|
464
|
-
class="px-2 pt-2 pb-3 space-y-1 sm:px-3 border-t border-border"
|
|
526
|
+
class="px-2 pt-2 pb-3 space-y-1 sm:px-3 border-t border-border-weak"
|
|
465
527
|
>
|
|
466
528
|
{#if nav}
|
|
467
529
|
{@render nav()}
|
|
@@ -481,6 +543,33 @@
|
|
|
481
543
|
{/if}
|
|
482
544
|
</div>
|
|
483
545
|
</nav>
|
|
546
|
+
|
|
547
|
+
<!-- Below `collapseAt` the links open in a sheet from the bottom. It is
|
|
548
|
+
portalled to the body: the bar is sticky and glass, and a fixed child of a
|
|
549
|
+
filtered box is placed against that box. It renders nothing until
|
|
550
|
+
it is open, so `nav` and `actions` are not drawn a second time. -->
|
|
551
|
+
{#if sheet}
|
|
552
|
+
<BottomSheet
|
|
553
|
+
bind:isOpen={isMenuOpen}
|
|
554
|
+
fit
|
|
555
|
+
fitMax="full"
|
|
556
|
+
title={text.menuTitle}
|
|
557
|
+
id={mobileMenuId}
|
|
558
|
+
data-topnavbar-sheet=""
|
|
559
|
+
>
|
|
560
|
+
<TopNavbarSheetContent
|
|
561
|
+
links={nav || items.length > 0 ? sheetLinks : undefined}
|
|
562
|
+
navLabel={ariaLabel ?? text.menuTitle}
|
|
563
|
+
{actions}
|
|
564
|
+
{showThemeToggle}
|
|
565
|
+
{themeModes}
|
|
566
|
+
{themeStorageKey}
|
|
567
|
+
{themeLabels}
|
|
568
|
+
themeHeading={text.themeHeading}
|
|
569
|
+
onclick={handlePanelClick}
|
|
570
|
+
/>
|
|
571
|
+
</BottomSheet>
|
|
572
|
+
{/if}
|
|
484
573
|
{/if}
|
|
485
574
|
|
|
486
575
|
<style>
|
|
@@ -488,7 +577,7 @@
|
|
|
488
577
|
* The bar is sticky, so an open menu taller than the screen could never be
|
|
489
578
|
* scrolled into view: the page moved under it and its last control stayed
|
|
490
579
|
* below the fold. It takes what is left of the screen under the 4rem bar
|
|
491
|
-
*
|
|
580
|
+
* and scrolls on its own, without handing the scroll
|
|
492
581
|
* on to the page when it reaches an end. `dvh` follows a phone's
|
|
493
582
|
* collapsing address bar; `vh` is for browsers without it. In a web app
|
|
494
583
|
* installed to the home screen the bar starts below the status bar, so
|
|
@@ -499,8 +588,8 @@
|
|
|
499
588
|
* script): a scrolling box would clip a Dropdown that opens out of it.
|
|
500
589
|
*/
|
|
501
590
|
.topnavbar-menu {
|
|
502
|
-
max-height: calc(100vh - 4rem -
|
|
503
|
-
max-height: calc(100dvh - 4rem -
|
|
591
|
+
max-height: calc(100vh - 4rem - env(safe-area-inset-top, 0px));
|
|
592
|
+
max-height: calc(100dvh - 4rem - env(safe-area-inset-top, 0px));
|
|
504
593
|
}
|
|
505
594
|
|
|
506
595
|
.topnavbar-menu[data-scrolls] {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Component, Snippet } from "svelte";
|
|
2
|
+
import { type ScrollEdgeMode } from "../util/scroll-edge.js";
|
|
2
3
|
import type { ThemeToggleLabels } from "../util/theme-mode.js";
|
|
3
4
|
import { type TopNavbarStrings } from "../util/top-navbar.js";
|
|
4
5
|
export interface TopNavbarNavItem {
|
|
@@ -16,6 +17,12 @@ export interface TopNavbarNavItem {
|
|
|
16
17
|
external?: boolean;
|
|
17
18
|
}
|
|
18
19
|
export type TopNavbarCollapseAt = "sm" | "md" | "lg" | "xl";
|
|
20
|
+
/**
|
|
21
|
+
* How the links open below `collapseAt`: `sheet` slides a modal sheet up
|
|
22
|
+
* from the bottom of the screen; `inline` opens a panel under the bar,
|
|
23
|
+
* which pushes the page down (the menu of 8.1 and earlier).
|
|
24
|
+
*/
|
|
25
|
+
export type TopNavbarMenu = "sheet" | "inline";
|
|
19
26
|
interface Props {
|
|
20
27
|
brand?: string;
|
|
21
28
|
brandHref?: string;
|
|
@@ -35,7 +42,7 @@ interface Props {
|
|
|
35
42
|
themeStorageKey?: string | null;
|
|
36
43
|
/** The texts of the theme toggle's name, for another language. As `labels` of ThemeToggle. */
|
|
37
44
|
themeLabels?: Partial<ThemeToggleLabels>;
|
|
38
|
-
/** The words the bar says by itself: the menu button's two names and the note on a link that opens a new tab. */
|
|
45
|
+
/** The words the bar says by itself: the menu button's two names, the name of the menu sheet and the note on a link that opens a new tab. */
|
|
39
46
|
strings?: Partial<TopNavbarStrings>;
|
|
40
47
|
class?: string;
|
|
41
48
|
/** @deprecated use `class`. */
|
|
@@ -51,8 +58,22 @@ interface Props {
|
|
|
51
58
|
* more items than fit at 768px.
|
|
52
59
|
*/
|
|
53
60
|
collapseAt?: TopNavbarCollapseAt;
|
|
61
|
+
/**
|
|
62
|
+
* How the links open below `collapseAt`. `sheet` (the default) is a
|
|
63
|
+
* bottom sheet: a modal dialog that is as tall as its content, up to
|
|
64
|
+
* the full screen below the status bar, and scrolls inside. `inline` is a panel under the
|
|
65
|
+
* bar, a disclosure that pushes the page down and traps nothing.
|
|
66
|
+
*/
|
|
67
|
+
menu?: TopNavbarMenu;
|
|
54
68
|
preventNavigation?: boolean;
|
|
55
69
|
onclick?: (event: Event) => void;
|
|
70
|
+
/**
|
|
71
|
+
* When the bar shows the glass and the hairline. `auto` follows the
|
|
72
|
+
* scroll position: flush at the top, glass once content is under it
|
|
73
|
+
* (and while the inline phone menu is open). `always` is glass all the time,
|
|
74
|
+
* `never` flush all the time.
|
|
75
|
+
*/
|
|
76
|
+
scrollEdge?: ScrollEdgeMode;
|
|
56
77
|
}
|
|
57
78
|
type $$ComponentProps = Props & {
|
|
58
79
|
nav?: Snippet;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, type Snippet } from "svelte";
|
|
3
|
+
import Monitor from "@lucide/svelte/icons/monitor";
|
|
4
|
+
import Moon from "@lucide/svelte/icons/moon";
|
|
5
|
+
import Sun from "@lucide/svelte/icons/sun";
|
|
6
|
+
import SegmentedControl from "../molecules/SegmentedControl.svelte";
|
|
7
|
+
import { mergeStrings } from "../util/ready-made-strings.js";
|
|
8
|
+
import { generateId } from "../util/ssr-safe.js";
|
|
9
|
+
import { zabiStringsFor } from "../util/zabi-strings.js";
|
|
10
|
+
import { markTopNavbarPlace } from "../util/top-navbar.js";
|
|
11
|
+
import {
|
|
12
|
+
getThemeMode,
|
|
13
|
+
isThemeDark,
|
|
14
|
+
setThemeMode,
|
|
15
|
+
storeThemeMode,
|
|
16
|
+
type ThemeMode,
|
|
17
|
+
type ThemeToggleLabels,
|
|
18
|
+
} from "../util/theme-mode.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* What TopNavbar's phone sheet holds: the links, then a section of its own
|
|
22
|
+
* with the caller's `actions` and the theme choice, at the full width of
|
|
23
|
+
* the sheet. Internal to TopNavbar.
|
|
24
|
+
*/
|
|
25
|
+
interface Props {
|
|
26
|
+
/** The links: the `nav` snippet or the built-in list. Omitted when there are none. */
|
|
27
|
+
links?: Snippet;
|
|
28
|
+
/** Name of the `nav` around the links. */
|
|
29
|
+
navLabel: string;
|
|
30
|
+
actions?: Snippet;
|
|
31
|
+
showThemeToggle: boolean;
|
|
32
|
+
themeModes: "two" | "three";
|
|
33
|
+
themeStorageKey?: string | null;
|
|
34
|
+
themeLabels?: Partial<ThemeToggleLabels>;
|
|
35
|
+
themeHeading: string;
|
|
36
|
+
/** Every click inside, for the sheet to close on a link. */
|
|
37
|
+
onclick: (event: MouseEvent) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let {
|
|
41
|
+
links,
|
|
42
|
+
navLabel,
|
|
43
|
+
actions,
|
|
44
|
+
showThemeToggle,
|
|
45
|
+
themeModes,
|
|
46
|
+
themeStorageKey,
|
|
47
|
+
themeLabels,
|
|
48
|
+
themeHeading,
|
|
49
|
+
onclick,
|
|
50
|
+
}: Props = $props();
|
|
51
|
+
|
|
52
|
+
// Everything drawn in here, the caller's `actions` too, reads "menu".
|
|
53
|
+
markTopNavbarPlace("menu");
|
|
54
|
+
|
|
55
|
+
/** The words of ThemeToggle's name, which are also the segments' labels here. */
|
|
56
|
+
const DEFAULT_WORDS = { auto: "system", light: "light", dark: "dark" };
|
|
57
|
+
const provided = zabiStringsFor("themeToggle");
|
|
58
|
+
const words = $derived(mergeStrings(DEFAULT_WORDS, provided(), themeLabels));
|
|
59
|
+
const sentence = (word: string) => word.charAt(0).toLocaleUpperCase() + word.slice(1);
|
|
60
|
+
|
|
61
|
+
const options = $derived(
|
|
62
|
+
themeModes === "three"
|
|
63
|
+
? [
|
|
64
|
+
{ value: "auto", label: sentence(words.auto), icon: Monitor },
|
|
65
|
+
{ value: "light", label: sentence(words.light), icon: Sun },
|
|
66
|
+
{ value: "dark", label: sentence(words.dark), icon: Moon },
|
|
67
|
+
]
|
|
68
|
+
: [
|
|
69
|
+
{ value: "light", label: sentence(words.light), icon: Sun },
|
|
70
|
+
{ value: "dark", label: sentence(words.dark), icon: Moon },
|
|
71
|
+
],
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// The bar's own ThemeToggle stays mounted (hidden by CSS) and restores the
|
|
75
|
+
// stored choice; this control reads and writes the same `<html>` attribute,
|
|
76
|
+
// and follows whatever else changes it.
|
|
77
|
+
let mode = $state<ThemeMode>("light");
|
|
78
|
+
let dark = $state(false);
|
|
79
|
+
const value = $derived(themeModes === "three" ? mode : dark ? "dark" : "light");
|
|
80
|
+
|
|
81
|
+
function read() {
|
|
82
|
+
mode = getThemeMode();
|
|
83
|
+
dark = isThemeDark();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onMount(() => {
|
|
87
|
+
read();
|
|
88
|
+
const root = document.documentElement;
|
|
89
|
+
const observer = new MutationObserver(read);
|
|
90
|
+
observer.observe(root, { attributes: true, attributeFilter: ["class", "data-theme"] });
|
|
91
|
+
const media = window.matchMedia?.("(prefers-color-scheme: dark)");
|
|
92
|
+
media?.addEventListener("change", read);
|
|
93
|
+
return () => {
|
|
94
|
+
observer.disconnect();
|
|
95
|
+
media?.removeEventListener("change", read);
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* As ThemeToggle writes it: `data-theme` with three modes, for "auto" and
|
|
101
|
+
* on a page that has the attribute; the `dark` class on a page that
|
|
102
|
+
* switches by class.
|
|
103
|
+
*/
|
|
104
|
+
function choose(next: string) {
|
|
105
|
+
if (next !== "auto" && next !== "light" && next !== "dark") return;
|
|
106
|
+
const root = document.documentElement;
|
|
107
|
+
if (themeModes === "three" || next === "auto" || root.hasAttribute("data-theme")) {
|
|
108
|
+
setThemeMode(next, { storageKey: themeStorageKey });
|
|
109
|
+
} else {
|
|
110
|
+
root.classList.toggle("dark", next === "dark");
|
|
111
|
+
storeThemeMode(next, themeStorageKey);
|
|
112
|
+
}
|
|
113
|
+
read();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const headingId = generateId("topnavbar-theme");
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
|
|
120
|
+
<div {onclick}>
|
|
121
|
+
{#if links}
|
|
122
|
+
<nav aria-label={navLabel}>
|
|
123
|
+
{@render links()}
|
|
124
|
+
</nav>
|
|
125
|
+
{/if}
|
|
126
|
+
{#if actions || showThemeToggle}
|
|
127
|
+
<div class="mt-4 flex flex-col gap-4 border-t border-border-weak pt-4" data-topnavbar-sheet-section>
|
|
128
|
+
{#if actions}
|
|
129
|
+
<div class="flex flex-col items-stretch gap-2 empty:hidden" data-topnavbar-actions>{@render actions()}</div>
|
|
130
|
+
{/if}
|
|
131
|
+
{#if showThemeToggle}
|
|
132
|
+
<div class="flex flex-col gap-2">
|
|
133
|
+
<p id={headingId} class="text-sm font-medium text-headline">{themeHeading}</p>
|
|
134
|
+
<SegmentedControl
|
|
135
|
+
{options}
|
|
136
|
+
{value}
|
|
137
|
+
size="lg"
|
|
138
|
+
aria-labelledby={headingId}
|
|
139
|
+
onchange={choose}
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
{/if}
|
|
143
|
+
</div>
|
|
144
|
+
{/if}
|
|
145
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import { type ThemeToggleLabels } from "../util/theme-mode.js";
|
|
3
|
+
/**
|
|
4
|
+
* What TopNavbar's phone sheet holds: the links, then a section of its own
|
|
5
|
+
* with the caller's `actions` and the theme choice, at the full width of
|
|
6
|
+
* the sheet. Internal to TopNavbar.
|
|
7
|
+
*/
|
|
8
|
+
interface Props {
|
|
9
|
+
/** The links: the `nav` snippet or the built-in list. Omitted when there are none. */
|
|
10
|
+
links?: Snippet;
|
|
11
|
+
/** Name of the `nav` around the links. */
|
|
12
|
+
navLabel: string;
|
|
13
|
+
actions?: Snippet;
|
|
14
|
+
showThemeToggle: boolean;
|
|
15
|
+
themeModes: "two" | "three";
|
|
16
|
+
themeStorageKey?: string | null;
|
|
17
|
+
themeLabels?: Partial<ThemeToggleLabels>;
|
|
18
|
+
themeHeading: string;
|
|
19
|
+
/** Every click inside, for the sheet to close on a link. */
|
|
20
|
+
onclick: (event: MouseEvent) => void;
|
|
21
|
+
}
|
|
22
|
+
declare const TopNavbarSheetContent: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type TopNavbarSheetContent = ReturnType<typeof TopNavbarSheetContent>;
|
|
24
|
+
export default TopNavbarSheetContent;
|
|
@@ -4,7 +4,12 @@ export { default as SidebarShell } from './SidebarShell.svelte';
|
|
|
4
4
|
export { default as SidebarAccountPanel } from './SidebarAccountPanel.svelte';
|
|
5
5
|
export { default as SidebarPanel } from './SidebarPanel.svelte';
|
|
6
6
|
export { default as AppShell } from './AppShell.svelte';
|
|
7
|
+
export { default as AppShellHeaderPart } from './AppShellHeaderPart.svelte';
|
|
8
|
+
export { default as AppNavigation } from './AppNavigation.svelte';
|
|
9
|
+
export { APP_SHELL_RAIL_MIN_WIDTH, APP_SHELL_SIDEBAR_MIN_WIDTH, } from '../util/app-shell.js';
|
|
10
|
+
export type { AppShellNavigationContext, AppShellNavigationMode, AppShellNavigationPlacement, } from '../util/app-shell.js';
|
|
7
11
|
export { DEFAULT_TOP_NAVBAR_STRINGS } from '../util/top-navbar.js';
|
|
8
|
-
export type { TopNavbarStrings } from '../util/top-navbar.js';
|
|
12
|
+
export type { TopNavbarStrings, TopNavbarPlace } from '../util/top-navbar.js';
|
|
13
|
+
export { getTopNavbarPlace } from '../util/top-navbar.js';
|
|
9
14
|
export { DEFAULT_SIDEBAR_ACCOUNT_PANEL_STRINGS, DEFAULT_SIDEBAR_NAVIGATION_STRINGS, } from '../util/sidebar.js';
|
|
10
15
|
export type { SidebarAccountPanelStrings, SidebarNavigationStrings } from '../util/sidebar.js';
|
package/dist/organisms/index.js
CHANGED
|
@@ -4,5 +4,9 @@ export { default as SidebarShell } from './SidebarShell.svelte';
|
|
|
4
4
|
export { default as SidebarAccountPanel } from './SidebarAccountPanel.svelte';
|
|
5
5
|
export { default as SidebarPanel } from './SidebarPanel.svelte';
|
|
6
6
|
export { default as AppShell } from './AppShell.svelte';
|
|
7
|
+
export { default as AppShellHeaderPart } from './AppShellHeaderPart.svelte';
|
|
8
|
+
export { default as AppNavigation } from './AppNavigation.svelte';
|
|
9
|
+
export { APP_SHELL_RAIL_MIN_WIDTH, APP_SHELL_SIDEBAR_MIN_WIDTH, } from '../util/app-shell.js';
|
|
7
10
|
export { DEFAULT_TOP_NAVBAR_STRINGS } from '../util/top-navbar.js';
|
|
11
|
+
export { getTopNavbarPlace } from '../util/top-navbar.js';
|
|
8
12
|
export { DEFAULT_SIDEBAR_ACCOUNT_PANEL_STRINGS, DEFAULT_SIDEBAR_NAVIGATION_STRINGS, } from '../util/sidebar.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Component } from 'svelte';
|
|
1
|
+
import type { Component, Snippet } from 'svelte';
|
|
2
|
+
import type { SkeletonStrings } from '../components/util/skeleton.js';
|
|
2
3
|
export type ZabiComponent<T extends Record<string, any> = Record<string, any>, E extends Record<string, any> = Record<string, any>> = Component<T, E>;
|
|
3
4
|
export interface ButtonProps {
|
|
4
5
|
variant?: 'primary' | 'secondary' | 'danger' | 'success' | 'warning' | 'info' | 'ghost' | 'link' | 'neutral';
|
|
@@ -138,6 +139,20 @@ export interface SkeletonProps {
|
|
|
138
139
|
height?: string | number;
|
|
139
140
|
class?: string;
|
|
140
141
|
'aria-label'?: string;
|
|
142
|
+
/** Overrides for the built-in strings. */
|
|
143
|
+
strings?: Partial<SkeletonStrings>;
|
|
144
|
+
/** No role, no label, hidden from assistive tech. */
|
|
145
|
+
decorative?: boolean;
|
|
146
|
+
/** "on-color" is for a brand, accent or fill block. */
|
|
147
|
+
tone?: 'neutral' | 'on-color';
|
|
148
|
+
/** Text variant only: this many lines (1 to 20), the last one shorter. */
|
|
149
|
+
lines?: number;
|
|
150
|
+
}
|
|
151
|
+
export interface SkeletonGroupProps {
|
|
152
|
+
'aria-label'?: string;
|
|
153
|
+
strings?: Partial<SkeletonStrings>;
|
|
154
|
+
class?: string;
|
|
155
|
+
children?: Snippet;
|
|
141
156
|
}
|
|
142
157
|
export interface ToggleProps {
|
|
143
158
|
checked?: boolean;
|
|
@@ -186,6 +201,7 @@ export type Alert = ZabiComponent<AlertProps>;
|
|
|
186
201
|
export type Badge = ZabiComponent<BadgeProps>;
|
|
187
202
|
export type Progress = ZabiComponent<ProgressProps>;
|
|
188
203
|
export type Skeleton = ZabiComponent<SkeletonProps>;
|
|
204
|
+
export type SkeletonGroup = ZabiComponent<SkeletonGroupProps>;
|
|
189
205
|
export type Toggle = ZabiComponent<ToggleProps, ToggleEvents>;
|
|
190
206
|
export type Tooltip = ZabiComponent<TooltipProps>;
|
|
191
207
|
//# sourceMappingURL=components.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/types/components.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/types/components.ts"],"names":[],"mappings":"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"}
|