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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onMount } from 'svelte';
|
|
2
|
+
import { onMount, untrack } from 'svelte';
|
|
3
3
|
import { cubicOut } from 'svelte/easing';
|
|
4
4
|
import { slide, fade, fly } from 'svelte/transition';
|
|
5
5
|
import AlertCircle from '@lucide/svelte/icons/circle-alert';
|
|
@@ -11,10 +11,15 @@
|
|
|
11
11
|
import Button from '../atoms/Button.svelte';
|
|
12
12
|
import { toastStore, type ToastItem } from './toast-store.js';
|
|
13
13
|
import { cn } from "../util/cn.js";
|
|
14
|
+
import { createVelocityTracker, DRAG_SLOP, FLICK_VELOCITY, prefersReducedMotion, timeOf } from "../util/sheet-drag.js";
|
|
14
15
|
import {
|
|
15
16
|
DEFAULT_TOASTER_STRINGS,
|
|
17
|
+
easeStandard,
|
|
16
18
|
resolveToastDuration,
|
|
19
|
+
toastsReplaced,
|
|
20
|
+
toastTexts,
|
|
17
21
|
TOAST_SECONDS_AFTER_HOLD,
|
|
22
|
+
TOAST_SWIPE_DISMISS_FRACTION,
|
|
18
23
|
type ToastDuration,
|
|
19
24
|
type ToasterStrings,
|
|
20
25
|
type ToastPauseChange,
|
|
@@ -32,6 +37,12 @@
|
|
|
32
37
|
defaultDuration?: ToastDuration;
|
|
33
38
|
/** Called when a pointer, a finger or focus starts or stops holding the timer. */
|
|
34
39
|
onpausechange?: (detail: ToastPauseChange) => void;
|
|
40
|
+
/** The compact form for a phone: one row, docked, sliding out from the edge it sits on. */
|
|
41
|
+
snackbar?: boolean;
|
|
42
|
+
/** A touch or a pen drags the toast away, sideways (and down, in the snackbar). */
|
|
43
|
+
swipeToDismiss?: boolean;
|
|
44
|
+
/** Draws the time bar (`true`/`false`); unset, it is left out of a snackbar toast with nothing to press. */
|
|
45
|
+
timeLine?: boolean;
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
let {
|
|
@@ -41,6 +52,9 @@
|
|
|
41
52
|
showCountdown = false,
|
|
42
53
|
defaultDuration,
|
|
43
54
|
onpausechange,
|
|
55
|
+
snackbar = false,
|
|
56
|
+
swipeToDismiss = true,
|
|
57
|
+
timeLine,
|
|
44
58
|
}: Props = $props();
|
|
45
59
|
|
|
46
60
|
let count = $state(0);
|
|
@@ -57,27 +71,11 @@
|
|
|
57
71
|
|
|
58
72
|
let intervalRef: ReturnType<typeof setInterval> | undefined;
|
|
59
73
|
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* for its type. A toast with a message says the message.
|
|
66
|
-
*/
|
|
67
|
-
const fallbackTitle = $derived(
|
|
68
|
-
toast.type === 'success'
|
|
69
|
-
? strings.successTitle
|
|
70
|
-
: toast.type === 'error'
|
|
71
|
-
? strings.errorTitle
|
|
72
|
-
: toast.type === 'warning'
|
|
73
|
-
? strings.warningTitle
|
|
74
|
-
: strings.infoTitle,
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
/** The first line: the title, or the message when there is no title. */
|
|
78
|
-
const headerTitle = $derived(title || message || fallbackTitle);
|
|
79
|
-
/** The second line: the message, under a title that says something else. */
|
|
80
|
-
const secondLine = $derived(title && message && message !== title ? message : "");
|
|
74
|
+
const texts = $derived(toastTexts(toast, strings));
|
|
75
|
+
const title = $derived(texts.title);
|
|
76
|
+
const message = $derived(texts.message);
|
|
77
|
+
const headerTitle = $derived(texts.headerTitle);
|
|
78
|
+
const secondLine = $derived(texts.secondLine);
|
|
81
79
|
|
|
82
80
|
/**
|
|
83
81
|
* The part that opens and closes is `detail`, when the toast was given
|
|
@@ -105,6 +103,39 @@
|
|
|
105
103
|
return ms <= 0 ? 0 : Math.max(1, Math.ceil(ms / 1000));
|
|
106
104
|
});
|
|
107
105
|
|
|
106
|
+
/**
|
|
107
|
+
* A short timed toast pushed with `closable: false` has no control at
|
|
108
|
+
* all. A toast that stays, or has a button of any kind to press, keeps
|
|
109
|
+
* its close button: there must always be a way to dismiss that is not a
|
|
110
|
+
* gesture.
|
|
111
|
+
*/
|
|
112
|
+
const bare = $derived(
|
|
113
|
+
toast.closable === false &&
|
|
114
|
+
maxSeconds > 0 &&
|
|
115
|
+
!toast.action &&
|
|
116
|
+
!hasExpandable &&
|
|
117
|
+
!showCountdown &&
|
|
118
|
+
toast.type !== 'error',
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The bar says "you have this long to press". A confirmation with nothing
|
|
123
|
+
* to press, in the snackbar, has no use for it (and WCAG 2.2.1 is about
|
|
124
|
+
* time to act, of which there is none), so it is left out there unless
|
|
125
|
+
* asked for. The stack keeps it. The toast's own setting wins over the Toaster's.
|
|
126
|
+
*/
|
|
127
|
+
const drawTimeLine = $derived(toast.timeLine ?? timeLine ?? !(snackbar && bare));
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* What a screen reader hears is a live region that is in the page
|
|
131
|
+
* empty and gets its words afterwards: one that is inserted already
|
|
132
|
+
* filled is the pattern screen readers are least dependable on. So the
|
|
133
|
+
* words are put in a moment after the toast is mounted (the toast is
|
|
134
|
+
* still coming in then). The server's markup has them at once.
|
|
135
|
+
*/
|
|
136
|
+
let spoken = $state(typeof document === 'undefined');
|
|
137
|
+
const SPEAK_AFTER_MS = 100;
|
|
138
|
+
|
|
108
139
|
/** The time left as words: for the sentence, or for a screen reader to find. */
|
|
109
140
|
const countdownText = $derived(
|
|
110
141
|
paused ? strings.pausedClosesIn(count) : strings.closesIn(count),
|
|
@@ -147,11 +178,11 @@
|
|
|
147
178
|
timerActive = false;
|
|
148
179
|
}
|
|
149
180
|
|
|
150
|
-
|
|
181
|
+
/** (Re)starts the timer from the full time; a toast that stays has none. */
|
|
182
|
+
function startTimer() {
|
|
183
|
+
stopTimer();
|
|
151
184
|
const max = maxSeconds;
|
|
152
|
-
if (max <= 0)
|
|
153
|
-
return () => {};
|
|
154
|
-
}
|
|
185
|
+
if (max <= 0) return;
|
|
155
186
|
count = max;
|
|
156
187
|
timerActive = true;
|
|
157
188
|
|
|
@@ -166,8 +197,15 @@
|
|
|
166
197
|
toastStore.dismiss(toast.id);
|
|
167
198
|
}
|
|
168
199
|
}, 1000);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
onMount(() => {
|
|
203
|
+
startTimer();
|
|
204
|
+
const speak = setTimeout(() => (spoken = true), SPEAK_AFTER_MS);
|
|
169
205
|
|
|
170
206
|
return () => {
|
|
207
|
+
clearTimeout(speak);
|
|
208
|
+
clearTimeout(settleTimer);
|
|
171
209
|
if (intervalRef !== undefined) {
|
|
172
210
|
clearInterval(intervalRef);
|
|
173
211
|
intervalRef = undefined;
|
|
@@ -175,6 +213,22 @@
|
|
|
175
213
|
};
|
|
176
214
|
});
|
|
177
215
|
|
|
216
|
+
// The same id pushed again replaces the toast in place: new words, the full time again.
|
|
217
|
+
let shownToast: ToastItem | undefined;
|
|
218
|
+
$effect(() => {
|
|
219
|
+
const current = toast;
|
|
220
|
+
if (shownToast === undefined) {
|
|
221
|
+
shownToast = current;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (current === shownToast) return;
|
|
225
|
+
shownToast = current;
|
|
226
|
+
untrack(() => {
|
|
227
|
+
isExpanded = false;
|
|
228
|
+
startTimer();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
178
232
|
function handleDismiss() {
|
|
179
233
|
toastStore.dismiss(toast.id);
|
|
180
234
|
}
|
|
@@ -213,29 +267,141 @@
|
|
|
213
267
|
}
|
|
214
268
|
|
|
215
269
|
/**
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
270
|
+
* A mouse or a stylus holds the timer once it has moved over the toast,
|
|
271
|
+
* not when the toast arrives under it: a browser sends `pointerenter` to
|
|
272
|
+
* an element that appears under a pointer at rest, and a toast shown
|
|
273
|
+
* where the last click was would otherwise be held until the mouse is
|
|
274
|
+
* moved away. A finger holds it while it is on the toast.
|
|
219
275
|
*/
|
|
220
|
-
let lastPointer: string | undefined;
|
|
221
|
-
|
|
222
|
-
function handlePointerEnter(event: PointerEvent) {
|
|
223
|
-
lastPointer = event.pointerType;
|
|
224
|
-
// A mouse, or a stylus in range. A finger is over the toast only while it is on it.
|
|
225
|
-
if (event.pointerType !== "touch") hovered = true;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
276
|
function handlePointerLeave(event: PointerEvent) {
|
|
229
277
|
if (event.pointerType !== "touch") hovered = false;
|
|
230
278
|
}
|
|
231
279
|
|
|
232
|
-
function
|
|
233
|
-
if (
|
|
280
|
+
function handlePointerMove(event: PointerEvent) {
|
|
281
|
+
if (dragging) {
|
|
282
|
+
drag(event);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (tracked) maybeStartDrag(event);
|
|
286
|
+
if (event.pointerType === "touch" || hovered) return;
|
|
287
|
+
// A move of no distance is not the pointer moving (a browser repeating
|
|
288
|
+
// its position after the layout changed); one with no such field is.
|
|
289
|
+
const moved =
|
|
290
|
+
typeof event.movementX !== "number" ||
|
|
291
|
+
Number.isNaN(event.movementX) ||
|
|
292
|
+
event.movementX !== 0 ||
|
|
293
|
+
event.movementY !== 0;
|
|
294
|
+
if (moved) hovered = true;
|
|
234
295
|
}
|
|
235
296
|
|
|
297
|
+
// Swiping away: a touch or a pen drags the toast sideways, or down in the
|
|
298
|
+
// snackbar (it is docked at the bottom); past 40% of its width or a flick it
|
|
299
|
+
// goes, otherwise it settles back. Reuses the sheets' drag constants.
|
|
300
|
+
let root = $state<HTMLDivElement>();
|
|
301
|
+
let dragX = $state(0);
|
|
302
|
+
let dragY = $state(0);
|
|
303
|
+
let dragging = $state(false);
|
|
304
|
+
let settling = $state(false);
|
|
305
|
+
let tracked = false;
|
|
306
|
+
let pointerId = -1;
|
|
307
|
+
let startX = 0;
|
|
308
|
+
let startY = 0;
|
|
309
|
+
let axis: "x" | "y" | null = null;
|
|
310
|
+
let swallowClick = false;
|
|
311
|
+
let settleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
312
|
+
const trackX = createVelocityTracker();
|
|
313
|
+
const trackY = createVelocityTracker();
|
|
314
|
+
|
|
236
315
|
function handlePointerDown(event: PointerEvent) {
|
|
237
|
-
lastPointer = event.pointerType;
|
|
238
316
|
if (event.pointerType === "touch") held = true;
|
|
317
|
+
if (!swipeToDismiss || bare || event.pointerType === "mouse" || !event.isPrimary) return;
|
|
318
|
+
// A button keeps its own press.
|
|
319
|
+
if ((event.target as Element | null)?.closest?.("button, a[href], input, select, textarea")) return;
|
|
320
|
+
clearTimeout(settleTimer);
|
|
321
|
+
settling = false;
|
|
322
|
+
tracked = true;
|
|
323
|
+
pointerId = event.pointerId;
|
|
324
|
+
startX = event.clientX;
|
|
325
|
+
startY = event.clientY;
|
|
326
|
+
axis = null;
|
|
327
|
+
const time = timeOf(event);
|
|
328
|
+
trackX.reset(event.clientX, time);
|
|
329
|
+
trackY.reset(event.clientY, time);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function drag(event: PointerEvent) {
|
|
333
|
+
if (event.pointerId !== pointerId) return;
|
|
334
|
+
const dx = event.clientX - startX;
|
|
335
|
+
const dy = event.clientY - startY;
|
|
336
|
+
const time = timeOf(event);
|
|
337
|
+
trackX.add(event.clientX, time);
|
|
338
|
+
trackY.add(event.clientY, time);
|
|
339
|
+
dragX = axis === "x" ? dx : 0;
|
|
340
|
+
dragY = axis === "y" ? Math.max(0, dy) : 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Called for every move of a pointer that is down on the toast, before it is a drag. */
|
|
344
|
+
function maybeStartDrag(event: PointerEvent) {
|
|
345
|
+
if (!tracked || event.pointerId !== pointerId) return;
|
|
346
|
+
const dx = event.clientX - startX;
|
|
347
|
+
const dy = event.clientY - startY;
|
|
348
|
+
const time = timeOf(event);
|
|
349
|
+
trackX.add(event.clientX, time);
|
|
350
|
+
trackY.add(event.clientY, time);
|
|
351
|
+
if (Math.hypot(dx, dy) < DRAG_SLOP) return;
|
|
352
|
+
const sideways = Math.abs(dx) > Math.abs(dy);
|
|
353
|
+
// Up is the way a page scrolls, and in the stack there is no edge to go down to.
|
|
354
|
+
if (!sideways && (dy < 0 || !snackbar)) {
|
|
355
|
+
tracked = false;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
axis = sideways ? "x" : "y";
|
|
359
|
+
dragging = true;
|
|
360
|
+
try {
|
|
361
|
+
root?.setPointerCapture(event.pointerId);
|
|
362
|
+
} catch {
|
|
363
|
+
// A pointer that is gone cannot be captured; the drag simply ends.
|
|
364
|
+
}
|
|
365
|
+
drag(event);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function finishDrag(event: PointerEvent, cancelled: boolean) {
|
|
369
|
+
if (!tracked || event.pointerId !== pointerId) return;
|
|
370
|
+
tracked = false;
|
|
371
|
+
if (!dragging) return;
|
|
372
|
+
dragging = false;
|
|
373
|
+
swallowClick = true;
|
|
374
|
+
setTimeout(() => (swallowClick = false), 0);
|
|
375
|
+
const width = root?.offsetWidth || 1;
|
|
376
|
+
const height = root?.offsetHeight || 1;
|
|
377
|
+
const sideways = axis === "x";
|
|
378
|
+
const distance = sideways ? dragX : dragY;
|
|
379
|
+
const velocity = sideways ? trackX.velocity() : trackY.velocity();
|
|
380
|
+
const flicked = Math.abs(velocity) > FLICK_VELOCITY && Math.sign(velocity) === Math.sign(distance);
|
|
381
|
+
const far = Math.abs(distance) > (sideways ? width : height) * TOAST_SWIPE_DISMISS_FRACTION;
|
|
382
|
+
if (!cancelled && (far || flicked)) {
|
|
383
|
+
if (prefersReducedMotion()) {
|
|
384
|
+
toastStore.dismiss(toast.id);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
// Out the way it was going, then gone.
|
|
388
|
+
settling = true;
|
|
389
|
+
if (sideways) dragX = Math.sign(distance || 1) * (width + 32);
|
|
390
|
+
else dragY = height + 32;
|
|
391
|
+
settleTimer = setTimeout(() => toastStore.dismiss(toast.id), 200);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
// Back where it was.
|
|
395
|
+
settling = !prefersReducedMotion();
|
|
396
|
+
dragX = 0;
|
|
397
|
+
dragY = 0;
|
|
398
|
+
settleTimer = setTimeout(() => (settling = false), 220);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function handleClickCapture(event: MouseEvent) {
|
|
402
|
+
if (!swallowClick) return;
|
|
403
|
+
event.preventDefault();
|
|
404
|
+
event.stopPropagation();
|
|
239
405
|
}
|
|
240
406
|
|
|
241
407
|
/**
|
|
@@ -244,6 +410,7 @@
|
|
|
244
410
|
* was held because someone was reading it.
|
|
245
411
|
*/
|
|
246
412
|
function handlePointerRelease(event: PointerEvent) {
|
|
413
|
+
finishDrag(event, event.type === "pointercancel");
|
|
247
414
|
if (event.pointerType !== "touch" || !held) return;
|
|
248
415
|
held = false;
|
|
249
416
|
if (timerActive) count = Math.max(count, Math.min(TOAST_SECONDS_AFTER_HOLD, maxSeconds));
|
|
@@ -257,26 +424,78 @@
|
|
|
257
424
|
}
|
|
258
425
|
|
|
259
426
|
/** No movement where motion is unwanted: the toast appears and goes at once. */
|
|
260
|
-
const reducedMotion =
|
|
261
|
-
typeof window !== 'undefined' &&
|
|
262
|
-
typeof window.matchMedia === 'function' &&
|
|
263
|
-
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
427
|
+
const reducedMotion = prefersReducedMotion();
|
|
264
428
|
const motion = (duration: number) => (reducedMotion ? 0 : duration);
|
|
265
429
|
|
|
266
430
|
const toastEnter = { y: 18, duration: motion(260), opacity: 0, easing: cubicOut };
|
|
267
431
|
const toastLeave = { y: 14, duration: motion(300), opacity: 0, easing: cubicOut };
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* The snackbar comes out from behind the edge it is docked on and goes
|
|
435
|
+
* back into it: it slides down by its own height and the gap, on the
|
|
436
|
+
* standard curve, with no overshoot. The region clips it at that edge.
|
|
437
|
+
*/
|
|
438
|
+
function dock(node: HTMLElement, leaving: boolean) {
|
|
439
|
+
const distance = node.offsetHeight + 24;
|
|
440
|
+
return {
|
|
441
|
+
duration: motion(leaving ? 200 : 280),
|
|
442
|
+
easing: easeStandard,
|
|
443
|
+
css: (t: number, u: number) => `transform: translateY(${u * distance}px); opacity: ${Math.min(1, t * 3)}`,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function enter(node: HTMLElement) {
|
|
448
|
+
return snackbar ? dock(node, false) : fly(node, toastEnter);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function leave(node: HTMLElement) {
|
|
452
|
+
// Dropped for a newer toast that is coming in where it is: gone at once.
|
|
453
|
+
if (toastsReplaced.delete(toast.id)) return { duration: 0 };
|
|
454
|
+
return snackbar ? dock(node, true) : fly(node, toastLeave);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** One line of text is centred on the buttons beside it; text that wraps stays at the top. */
|
|
458
|
+
let textBox = $state<HTMLDivElement>();
|
|
459
|
+
let oneLine = $state(false);
|
|
460
|
+
$effect(() => {
|
|
461
|
+
const box = textBox;
|
|
462
|
+
void headerTitle;
|
|
463
|
+
void secondLine;
|
|
464
|
+
if (!box || typeof getComputedStyle !== 'function') return;
|
|
465
|
+
const measure = () => {
|
|
466
|
+
const line = parseFloat(getComputedStyle(box).lineHeight) || 24;
|
|
467
|
+
oneLine = box.scrollHeight <= line * 1.5;
|
|
468
|
+
};
|
|
469
|
+
measure();
|
|
470
|
+
if (typeof ResizeObserver !== 'function') return;
|
|
471
|
+
const observer = new ResizeObserver(measure);
|
|
472
|
+
observer.observe(box);
|
|
473
|
+
return () => observer.disconnect();
|
|
474
|
+
});
|
|
268
475
|
</script>
|
|
269
476
|
|
|
477
|
+
<!-- Radius: the snackbar's 4px of padding round a control of 8px make 12px (outer = inner + padding). -->
|
|
270
478
|
<div
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
479
|
+
bind:this={root}
|
|
480
|
+
class={cn(
|
|
481
|
+
"relative w-full min-w-[min(18rem,100%)] shrink-0 overflow-hidden material-thick",
|
|
482
|
+
snackbar ? "rounded-container" : "rounded-overlay",
|
|
483
|
+
// No control in it: a tap goes through to the page.
|
|
484
|
+
bare ? "pointer-events-none" : "pointer-events-auto",
|
|
485
|
+
className,
|
|
486
|
+
)}
|
|
487
|
+
in:enter
|
|
488
|
+
out:leave
|
|
489
|
+
style:transform={dragX !== 0 || dragY !== 0 ? `translate3d(${dragX}px, ${dragY}px, 0)` : undefined}
|
|
490
|
+
style:opacity={dragging || settling ? Math.max(0.2, 1 - Math.hypot(dragX, dragY) / (root?.offsetWidth || 300)) : undefined}
|
|
491
|
+
style:transition={settling ? "transform 200ms var(--ease-standard), opacity 200ms var(--ease-standard)" : undefined}
|
|
492
|
+
style:touch-action={swipeToDismiss && !bare ? (snackbar ? "none" : "pan-y") : undefined}
|
|
275
493
|
onpointerleave={handlePointerLeave}
|
|
276
494
|
onpointerdown={handlePointerDown}
|
|
495
|
+
onpointermove={handlePointerMove}
|
|
277
496
|
onpointerup={handlePointerRelease}
|
|
278
497
|
onpointercancel={handlePointerRelease}
|
|
279
|
-
|
|
498
|
+
onclickcapture={handleClickCapture}
|
|
280
499
|
onmouseleave={() => (hovered = false)}
|
|
281
500
|
onfocusin={() => (focusWithin = true)}
|
|
282
501
|
onfocusout={handleFocusOut}
|
|
@@ -287,11 +506,11 @@
|
|
|
287
506
|
>
|
|
288
507
|
<!-- Padding and gaps in px: at 200% text on a phone, room that grew with
|
|
289
508
|
the text left the text itself a column too narrow for one long word. -->
|
|
290
|
-
<div class="p-[16px]">
|
|
509
|
+
<div class={snackbar ? "p-[4px] ps-[12px]" : "p-[16px]"}>
|
|
291
510
|
<!-- The title keeps 8rem at least, icon included. When the toast has
|
|
292
511
|
less beside its buttons (320px with enlarged text), the buttons go to a
|
|
293
512
|
line of their own under it, at the end. -->
|
|
294
|
-
<div class="flex flex-wrap items-start gap-x-[12px] gap-y-[4px]">
|
|
513
|
+
<div class="flex flex-wrap {oneLine ? 'items-center' : 'items-start'} gap-x-[12px] gap-y-[4px] {snackbar ? 'min-h-[44px]' : ''}">
|
|
295
514
|
<!-- Live region holds only title + message, so the countdown and buttons are never re-announced. -->
|
|
296
515
|
<div
|
|
297
516
|
class="flex min-w-0 flex-[1_1_8rem] items-start gap-[12px]"
|
|
@@ -312,30 +531,41 @@
|
|
|
312
531
|
<Info class="size-[20px] {statusIconClass}" aria-hidden="true" />
|
|
313
532
|
{/if}
|
|
314
533
|
</div>
|
|
315
|
-
<div class="min-w-0 flex-1">
|
|
534
|
+
<div class="min-w-0 flex-1" bind:this={textBox}>
|
|
316
535
|
<!-- A heading when the app gave the toast a title (or nothing
|
|
317
536
|
at all); a message on its own is a sentence, not a heading. -->
|
|
318
537
|
{#if title || !message}
|
|
319
538
|
<h4 class="text-base font-semibold text-headline [overflow-wrap:anywhere]">
|
|
320
|
-
{headerTitle}
|
|
539
|
+
{spoken ? headerTitle : "\u00a0"}
|
|
321
540
|
</h4>
|
|
322
541
|
{:else}
|
|
323
542
|
<p class="text-base font-medium text-headline [overflow-wrap:anywhere]" data-toast-message>
|
|
324
|
-
{headerTitle}
|
|
543
|
+
{spoken ? headerTitle : "\u00a0"}
|
|
325
544
|
</p>
|
|
326
545
|
{/if}
|
|
327
546
|
{#if secondLine}
|
|
328
547
|
<p class="mt-1 text-sm text-description [overflow-wrap:anywhere]" data-toast-message>
|
|
329
|
-
{secondLine}
|
|
548
|
+
{spoken ? secondLine : "\u00a0"}
|
|
330
549
|
</p>
|
|
331
550
|
{/if}
|
|
332
|
-
{#if toast.action}
|
|
551
|
+
{#if toast.action && spoken}
|
|
333
552
|
<!-- The button sits outside the live region, so this is how a screen reader user hears that there is one. -->
|
|
334
553
|
<span class="sr-only">{strings.actionAvailable(toast.action.label)}</span>
|
|
335
554
|
{/if}
|
|
336
555
|
</div>
|
|
337
556
|
</div>
|
|
557
|
+
{#if !bare}
|
|
338
558
|
<div class="ms-auto flex shrink-0 items-center gap-1">
|
|
559
|
+
{#if snackbar && toast.action}
|
|
560
|
+
<Button
|
|
561
|
+
variant="text"
|
|
562
|
+
size="sm"
|
|
563
|
+
text={toast.action.label}
|
|
564
|
+
type="button"
|
|
565
|
+
data-toast-action
|
|
566
|
+
onclick={handleAction}
|
|
567
|
+
/>
|
|
568
|
+
{/if}
|
|
339
569
|
{#if hasExpandable}
|
|
340
570
|
<button
|
|
341
571
|
type="button"
|
|
@@ -364,9 +594,10 @@
|
|
|
364
594
|
<X class="size-5" aria-hidden="true" />
|
|
365
595
|
</button>
|
|
366
596
|
</div>
|
|
597
|
+
{/if}
|
|
367
598
|
</div>
|
|
368
599
|
|
|
369
|
-
{#if toast.action}
|
|
600
|
+
{#if toast.action && !snackbar}
|
|
370
601
|
<!-- Indented to the title: past the 20px status icon and its 12px gap. -->
|
|
371
602
|
<div class="mt-3 pl-[32px]">
|
|
372
603
|
<Button
|
|
@@ -419,14 +650,15 @@
|
|
|
419
650
|
<p class="sr-only" data-toast-countdown>{countdownText}</p>
|
|
420
651
|
{/if}
|
|
421
652
|
|
|
422
|
-
{#if timerActive}
|
|
653
|
+
{#if timerActive && drawTimeLine}
|
|
423
654
|
<div
|
|
424
|
-
class="pointer-events-none absolute bottom-0 left-0 right-0 h-1 bg-border/70"
|
|
655
|
+
class="pointer-events-none absolute bottom-0 left-0 right-0 h-1 bg-border/70 forced-colors:outline-1 forced-colors:-outline-offset-1 forced-colors:outline-[color:CanvasText]"
|
|
656
|
+
data-toast-timeline
|
|
425
657
|
transition:fade={{ duration: motion(180) }}
|
|
426
658
|
aria-hidden="true"
|
|
427
659
|
>
|
|
428
660
|
<div
|
|
429
|
-
class="h-full {progressFillClass} transition-all duration-1000 ease-linear"
|
|
661
|
+
class="h-full {progressFillClass} forced-colors:bg-[color:Highlight]! transition-all duration-1000 ease-linear"
|
|
430
662
|
style="width: {(count / maxSeconds) * 100}%"
|
|
431
663
|
></div>
|
|
432
664
|
</div>
|
|
@@ -12,6 +12,12 @@ interface Props {
|
|
|
12
12
|
defaultDuration?: ToastDuration;
|
|
13
13
|
/** Called when a pointer, a finger or focus starts or stops holding the timer. */
|
|
14
14
|
onpausechange?: (detail: ToastPauseChange) => void;
|
|
15
|
+
/** The compact form for a phone: one row, docked, sliding out from the edge it sits on. */
|
|
16
|
+
snackbar?: boolean;
|
|
17
|
+
/** A touch or a pen drags the toast away, sideways (and down, in the snackbar). */
|
|
18
|
+
swipeToDismiss?: boolean;
|
|
19
|
+
/** Draws the time bar (`true`/`false`); unset, it is left out of a snackbar toast with nothing to press. */
|
|
20
|
+
timeLine?: boolean;
|
|
15
21
|
}
|
|
16
22
|
declare const ToasterToast: import("svelte").Component<Props, {}, "">;
|
|
17
23
|
type ToasterToast = ReturnType<typeof ToasterToast>;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { tick, untrack, type Snippet } from "svelte";
|
|
4
4
|
import type { HTMLAttributes } from "svelte/elements";
|
|
5
5
|
import Button from "../atoms/Button.svelte";
|
|
6
|
+
import { getAppShell } from "../util/app-shell.js";
|
|
6
7
|
import { cn } from "../util/cn.js";
|
|
7
8
|
|
|
8
9
|
type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "role"> & {
|
|
@@ -67,6 +68,10 @@
|
|
|
67
68
|
const saveLabel = $derived(saveLabelGiven ?? provided()?.saveLabel ?? DEFAULT_UNSAVED_CHANGES_BAR_TEXTS.saveLabel);
|
|
68
69
|
const discardLabel = $derived(discardLabelGiven ?? provided()?.discardLabel ?? DEFAULT_UNSAVED_CHANGES_BAR_TEXTS.discardLabel);
|
|
69
70
|
|
|
71
|
+
/** In a shell whose footer lies over the scroller the bar floats above the footer, which already clears the home indicator. */
|
|
72
|
+
const shell = getAppShell();
|
|
73
|
+
const aboveFooter = $derived(shell?.footerOverlays === true);
|
|
74
|
+
|
|
70
75
|
/** True while a promise returned by `onsave` is pending. */
|
|
71
76
|
let pending = $state(false);
|
|
72
77
|
const busy = $derived(saving || pending);
|
|
@@ -184,12 +189,18 @@
|
|
|
184
189
|
aria-label={dirty ? label : undefined}
|
|
185
190
|
aria-busy={dirty && busy ? "true" : undefined}
|
|
186
191
|
data-position={position}
|
|
192
|
+
data-toast-clear={dirty ? "" : undefined}
|
|
187
193
|
class={cn(
|
|
188
194
|
"focus-ring",
|
|
189
195
|
dirty &&
|
|
190
|
-
"sticky z-sticky flex flex-wrap items-center justify-between gap-x-4 gap-y-2 rounded-container
|
|
196
|
+
"material-layer-regular sticky z-sticky flex flex-wrap items-center justify-between gap-x-4 gap-y-2 rounded-container px-4 py-3",
|
|
191
197
|
// Clear of the home indicator on a phone.
|
|
192
|
-
dirty &&
|
|
198
|
+
dirty &&
|
|
199
|
+
(position === "top"
|
|
200
|
+
? "top-2"
|
|
201
|
+
: aboveFooter
|
|
202
|
+
? "bottom-[calc(var(--app-shell-footer-overlay,0px)+0.5rem)]"
|
|
203
|
+
: "bottom-[max(0.5rem,env(safe-area-inset-bottom))]"),
|
|
193
204
|
dirty && className,
|
|
194
205
|
)}
|
|
195
206
|
onfocusin={handleFocusIn}
|
|
@@ -27,6 +27,7 @@ export { default as Header } from './Header.svelte';
|
|
|
27
27
|
export { default as EmptyState } from './EmptyState.svelte';
|
|
28
28
|
export { default as Toaster } from './Toaster.svelte';
|
|
29
29
|
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
30
|
+
export { default as ChipGroup } from './ChipGroup.svelte';
|
|
30
31
|
export { default as SegmentedControl } from './SegmentedControl.svelte';
|
|
31
32
|
export type { SegmentedControlIcon, SegmentedControlOption, } from '../util/segmented-control.js';
|
|
32
33
|
export { default as SortableList } from './SortableList.svelte';
|
|
@@ -55,15 +56,19 @@ export { default as Calendar } from './Calendar.svelte';
|
|
|
55
56
|
export type { CalendarEvent, CalendarStrings, CalendarTone, } from '../util/calendar.js';
|
|
56
57
|
export { default as PhotoGrid } from './PhotoGrid.svelte';
|
|
57
58
|
export { default as PhotoViewer } from './PhotoViewer.svelte';
|
|
58
|
-
export type { Photo, PhotoGridSelectDetail, PhotoGridStrings, PhotoKey, PhotoViewerAction, PhotoViewerCloseReason, PhotoViewerStrings, } from '../util/photo.js';
|
|
59
|
+
export type { Photo, PhotoGridSelectDetail, PhotoGridStrings, PhotoKey, PhotoStatus, PhotoViewerAction, PhotoViewerCloseReason, PhotoViewerStrings, } from '../util/photo.js';
|
|
59
60
|
export { default as AvatarGroup } from './AvatarGroup.svelte';
|
|
60
61
|
export { DEFAULT_AVATAR_GROUP_STRINGS } from '../util/avatar.js';
|
|
61
62
|
export type { AvatarGroupStrings, AvatarPerson } from '../util/avatar.js';
|
|
63
|
+
export { default as LineChart } from './LineChart.svelte';
|
|
64
|
+
export { default as BarChart } from './BarChart.svelte';
|
|
65
|
+
export { default as PieChart } from './PieChart.svelte';
|
|
66
|
+
export type { ChartColor, ChartColorName, ChartDatum, ChartFormat, ChartSeries, } from '../util/chart.js';
|
|
62
67
|
export { default as Stepper } from './Stepper.svelte';
|
|
63
68
|
export type { StepperItem, StepperLayout, StepperStep, StepperStepState, StepperStrings, } from '../util/stepper.js';
|
|
64
69
|
export * from './toast-store.js';
|
|
65
70
|
export { DEFAULT_TOASTER_STRINGS, TOAST_DURATIONS } from '../util/toaster.js';
|
|
66
|
-
export type { ToasterStrings, ToastPauseChange, ToastDuration } from '../util/toaster.js';
|
|
71
|
+
export type { ToasterStrings, ToasterMode, ToastPauseChange, ToastDuration } from '../util/toaster.js';
|
|
67
72
|
export { DEFAULT_SELECT_STRINGS } from '../util/select.js';
|
|
68
73
|
export type { SelectStrings, SelectPresentation } from '../util/select.js';
|
|
69
74
|
export { DEFAULT_SIDEBAR_BRAND_HEADER_STRINGS, DEFAULT_SIDEBAR_FOOTER_STRINGS, } from '../util/sidebar.js';
|
package/dist/molecules/index.js
CHANGED
|
@@ -25,6 +25,7 @@ export { default as Header } from './Header.svelte';
|
|
|
25
25
|
export { default as EmptyState } from './EmptyState.svelte';
|
|
26
26
|
export { default as Toaster } from './Toaster.svelte';
|
|
27
27
|
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
28
|
+
export { default as ChipGroup } from './ChipGroup.svelte';
|
|
28
29
|
export { default as SegmentedControl } from './SegmentedControl.svelte';
|
|
29
30
|
export { default as SortableList } from './SortableList.svelte';
|
|
30
31
|
export { default as Collapsible } from './Collapsible.svelte';
|
|
@@ -45,6 +46,9 @@ export { default as PhotoGrid } from './PhotoGrid.svelte';
|
|
|
45
46
|
export { default as PhotoViewer } from './PhotoViewer.svelte';
|
|
46
47
|
export { default as AvatarGroup } from './AvatarGroup.svelte';
|
|
47
48
|
export { DEFAULT_AVATAR_GROUP_STRINGS } from '../util/avatar.js';
|
|
49
|
+
export { default as LineChart } from './LineChart.svelte';
|
|
50
|
+
export { default as BarChart } from './BarChart.svelte';
|
|
51
|
+
export { default as PieChart } from './PieChart.svelte';
|
|
48
52
|
export { default as Stepper } from './Stepper.svelte';
|
|
49
53
|
export * from './toast-store.js';
|
|
50
54
|
export { DEFAULT_TOASTER_STRINGS, TOAST_DURATIONS } from '../util/toaster.js';
|
|
@@ -47,6 +47,24 @@ export interface ToastItem {
|
|
|
47
47
|
duration?: ToastDuration;
|
|
48
48
|
/** An action button in the toast, such as Undo. */
|
|
49
49
|
action?: ToastAction;
|
|
50
|
+
/**
|
|
51
|
+
* `false` leaves the close button out, for a short timed confirmation
|
|
52
|
+
* that nobody has to act on: the toast then has no control, takes no
|
|
53
|
+
* taps (they reach the page under it) and cannot be held. Ignored, and
|
|
54
|
+
* the button shown, for a toast that stays (an error, one with an
|
|
55
|
+
* action, `"persistent"`), one with `detail`, and under a Toaster with
|
|
56
|
+
* `showCountdown`. Defaults to `true`.
|
|
57
|
+
*/
|
|
58
|
+
closable?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the thin bar along the bottom, which empties over the toast's
|
|
61
|
+
* time, is drawn. Unset, the Toaster decides: it is drawn, except under
|
|
62
|
+
* a confirmation in the snackbar that has nothing to press (the case
|
|
63
|
+
* `closable: false` makes), where it is left out. `true` or `false`
|
|
64
|
+
* here wins over the Toaster's `timeLine`. The sentence a screen reader
|
|
65
|
+
* finds for the time left stays either way.
|
|
66
|
+
*/
|
|
67
|
+
timeLine?: boolean;
|
|
50
68
|
}
|
|
51
69
|
export declare const toastStore: {
|
|
52
70
|
subscribe: (this: void, run: import("svelte/store").Subscriber<ToastItem[]>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
@@ -59,12 +77,20 @@ export declare const toastStore: {
|
|
|
59
77
|
title?: string;
|
|
60
78
|
detail?: string;
|
|
61
79
|
action?: ToastAction;
|
|
80
|
+
closable?: boolean;
|
|
81
|
+
timeLine?: boolean;
|
|
62
82
|
}): string;
|
|
63
83
|
dismiss: (id: string) => void;
|
|
64
84
|
clear(): void;
|
|
65
85
|
};
|
|
66
86
|
export declare function pushToast(options: {
|
|
67
87
|
message: string;
|
|
88
|
+
/**
|
|
89
|
+
* Gives the toast a name of your own. Pushing with the id of a toast that
|
|
90
|
+
* is showing replaces it in place, with its time started again, so
|
|
91
|
+
* "Saved" pushed three times is one toast.
|
|
92
|
+
*/
|
|
93
|
+
id?: string;
|
|
68
94
|
type?: ToastLevel;
|
|
69
95
|
/** Same semantics as {@link ToastItem.duration}. */
|
|
70
96
|
duration?: ToastDuration;
|
|
@@ -72,6 +98,10 @@ export declare function pushToast(options: {
|
|
|
72
98
|
detail?: string;
|
|
73
99
|
/** An action button in the toast, such as Undo. See {@link ToastAction}. */
|
|
74
100
|
action?: ToastAction;
|
|
101
|
+
/** `false` leaves the close button out of a short timed toast. See {@link ToastItem.closable}. */
|
|
102
|
+
closable?: boolean;
|
|
103
|
+
/** `false` leaves the time bar out, `true` forces it. See {@link ToastItem.timeLine}. */
|
|
104
|
+
timeLine?: boolean;
|
|
75
105
|
}): string;
|
|
76
106
|
export declare function dismissToast(id: string): void;
|
|
77
107
|
/**
|