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
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
import { topOverlayFooter, topOverlayHeader, watchOverlayFooters } from "../util/overlay.js";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_TOASTER_STRINGS,
|
|
11
|
+
toastsReplaced,
|
|
12
|
+
toastStays,
|
|
11
13
|
type ToastDuration,
|
|
14
|
+
type ToasterMode,
|
|
12
15
|
type ToasterStrings,
|
|
13
16
|
type ToastPauseChange,
|
|
14
17
|
} from "../util/toaster.js";
|
|
@@ -28,7 +31,7 @@
|
|
|
28
31
|
* `--toaster-bottom-offset` on the Toaster or any ancestor:
|
|
29
32
|
*
|
|
30
33
|
* ```svelte
|
|
31
|
-
* <Toaster style="--toaster-bottom-offset:
|
|
34
|
+
* <Toaster style="--toaster-bottom-offset: 64px" />
|
|
32
35
|
* ```
|
|
33
36
|
*
|
|
34
37
|
* A `FloatingActionButton` is 56px with 16px under it: `72px` puts the
|
|
@@ -63,6 +66,29 @@
|
|
|
63
66
|
* own time (WCAG 2.2.1). A finger that lifts leaves it at least three
|
|
64
67
|
* seconds, so holding a short toast starts it again.
|
|
65
68
|
* `data-paused` on the toast says so, and `onpausechange` is called.
|
|
69
|
+
* A mouse holds the timer once it has moved over the toast, not when the
|
|
70
|
+
* toast comes up under a pointer that is at rest. A touch or a pen drags
|
|
71
|
+
* a toast away sideways (`swipeToDismiss`, on by default), and the close
|
|
72
|
+
* button stays as the way for a keyboard and a screen reader. A toast
|
|
73
|
+
* pushed with the id of one that is showing replaces it in place, and
|
|
74
|
+
* `max` limits how many show at once: the oldest toast that is timed
|
|
75
|
+
* (not an error, not one with an action) is the one that leaves.
|
|
76
|
+
*
|
|
77
|
+
* For a phone, `mode="auto"` makes the toast a snackbar below 640px: one
|
|
78
|
+
* row (two lines at most), one at a time, with its action beside its text
|
|
79
|
+
* and no stack. It sits directly above what is docked at the bottom, which
|
|
80
|
+
* it finds by itself: a `BottomTabBar`, a `StickyActionBar`, a
|
|
81
|
+
* `FloatingActionButton` and an `UnsavedChangesBar` that lie under it, and
|
|
82
|
+
* anything you name in `avoid` (the button a screen ends in). It comes out
|
|
83
|
+
* from behind that edge and goes back into it, on `--ease-standard`, with
|
|
84
|
+
* no movement under reduced motion, and lies over the page without moving
|
|
85
|
+
* it. Push a short confirmation with `closable: false` and it has no
|
|
86
|
+
* control and takes no taps.
|
|
87
|
+
*
|
|
88
|
+
* ```svelte
|
|
89
|
+
* <Toaster mode="auto" avoid="[data-primary-action]" />
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
66
92
|
* Every word the toaster says by itself is in `strings`:
|
|
67
93
|
*
|
|
68
94
|
* ```svelte
|
|
@@ -100,6 +126,41 @@
|
|
|
100
126
|
* on the toast wins.
|
|
101
127
|
*/
|
|
102
128
|
defaultDuration?: ToastDuration;
|
|
129
|
+
/**
|
|
130
|
+
* `"stack"` (default): the stack of toasts. `"snackbar"`: the compact
|
|
131
|
+
* form for a phone, at every width. `"auto"`: the snackbar below
|
|
132
|
+
* 640px, the stack from there up.
|
|
133
|
+
*/
|
|
134
|
+
mode?: ToasterMode;
|
|
135
|
+
/**
|
|
136
|
+
* How many toasts show at once. The oldest one that is timed leaves
|
|
137
|
+
* when a newer one arrives; a toast that has to stay (an error, one
|
|
138
|
+
* with an action, `"persistent"`) is not dropped, so there can be more
|
|
139
|
+
* than this while they are showing. Default: no limit, and 1 in the
|
|
140
|
+
* snackbar.
|
|
141
|
+
*/
|
|
142
|
+
max?: number;
|
|
143
|
+
/**
|
|
144
|
+
* A CSS selector for elements the stack must not lie on, in addition
|
|
145
|
+
* to the docked bars it finds by itself: the button a screen ends in.
|
|
146
|
+
* When one lies under the stack, the stack sits above it.
|
|
147
|
+
*/
|
|
148
|
+
avoid?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Draws the thin bar along the bottom of a toast that empties over its
|
|
151
|
+
* time. Unset: drawn, except under a confirmation in the snackbar that
|
|
152
|
+
* has nothing to press (pushed with `closable: false`), where it is
|
|
153
|
+
* left out. `false` leaves it out everywhere, `true` draws it
|
|
154
|
+
* everywhere; a toast's own `timeLine` wins. The sentence for a screen
|
|
155
|
+
* reader about the time left stays.
|
|
156
|
+
*/
|
|
157
|
+
timeLine?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Touch and pen drag a toast away sideways (and down, in the
|
|
160
|
+
* snackbar). Default `true`. A mouse does not drag; the close button
|
|
161
|
+
* is the way for a keyboard and a screen reader.
|
|
162
|
+
*/
|
|
163
|
+
swipeToDismiss?: boolean;
|
|
103
164
|
/**
|
|
104
165
|
* Called when a pointer, a finger or focus starts or stops holding a
|
|
105
166
|
* toast's timer, with the toast's id. The same state is `data-paused`
|
|
@@ -114,6 +175,11 @@
|
|
|
114
175
|
"aria-label": ariaLabel,
|
|
115
176
|
showCountdown = false,
|
|
116
177
|
defaultDuration,
|
|
178
|
+
mode = "stack",
|
|
179
|
+
max,
|
|
180
|
+
avoid,
|
|
181
|
+
swipeToDismiss = true,
|
|
182
|
+
timeLine,
|
|
117
183
|
onpausechange,
|
|
118
184
|
class: className = '',
|
|
119
185
|
...restProps
|
|
@@ -143,8 +209,77 @@
|
|
|
143
209
|
|
|
144
210
|
let region = $state<HTMLDivElement>();
|
|
145
211
|
|
|
212
|
+
/** Below 640px (`sm`), for `mode="auto"`. */
|
|
213
|
+
let narrow = $state(false);
|
|
214
|
+
$effect(() => {
|
|
215
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
216
|
+
const query = window.matchMedia("(max-width: 39.999rem)");
|
|
217
|
+
const update = () => (narrow = query.matches);
|
|
218
|
+
update();
|
|
219
|
+
query.addEventListener?.("change", update);
|
|
220
|
+
return () => query.removeEventListener?.("change", update);
|
|
221
|
+
});
|
|
222
|
+
const snackbar = $derived(mode === "snackbar" || (mode === "auto" && narrow));
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Cuts at the edge the toast is docked on, so it comes out from behind it (sides and top stay open).
|
|
226
|
+
* Each class is written out whole: Tailwind only generates what it finds as text in the source, so a
|
|
227
|
+
* class put together from a prefix and a value at run time is in no stylesheet.
|
|
228
|
+
*/
|
|
229
|
+
const clipClass = $derived(
|
|
230
|
+
mode === "snackbar"
|
|
231
|
+
? "[clip-path:inset(-100vh_-100vw_0_-100vw)]"
|
|
232
|
+
: mode === "auto"
|
|
233
|
+
? "max-sm:[clip-path:inset(-100vh_-100vw_0_-100vw)]"
|
|
234
|
+
: "",
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
/** The oldest toast that is timed leaves for a newer one; one that has to stay is never dropped. */
|
|
238
|
+
const limit = $derived(max ?? (snackbar ? 1 : Infinity));
|
|
239
|
+
$effect(() => {
|
|
240
|
+
const list = $toastStore;
|
|
241
|
+
const allowed = Math.floor(limit);
|
|
242
|
+
let over = list.length - allowed;
|
|
243
|
+
if (!(allowed >= 1) || over <= 0) return;
|
|
244
|
+
for (const toast of list.slice(0, -1)) {
|
|
245
|
+
if (over <= 0) break;
|
|
246
|
+
if (toastStays(toast, defaultDuration)) continue;
|
|
247
|
+
toastsReplaced.add(toast.id);
|
|
248
|
+
toastStore.dismiss(toast.id);
|
|
249
|
+
over -= 1;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
146
253
|
/** The highest the stack may reach: below the status bar or an AppShell's top bar. */
|
|
147
254
|
const TOP = "calc(max(var(--app-shell-top-inset, 0px), env(safe-area-inset-top, 0px)) + var(--toaster-edge))";
|
|
255
|
+
/** What is docked at the bottom, and so may lie under the stack. */
|
|
256
|
+
const DOCKED = '[data-bar-edge="bottom"], [data-toast-clear]';
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The top edge of the highest docked element (or one named in `avoid`)
|
|
260
|
+
* that the stack at rest would lie on, or null when it lies on none.
|
|
261
|
+
*/
|
|
262
|
+
function dockedTop(rest: DOMRect): number | null {
|
|
263
|
+
let nodes: NodeListOf<HTMLElement>;
|
|
264
|
+
try {
|
|
265
|
+
nodes = document.querySelectorAll<HTMLElement>(avoid ? `${DOCKED}, ${avoid}` : DOCKED);
|
|
266
|
+
} catch {
|
|
267
|
+
// A selector that does not parse is not a reason to lose the toast.
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
let top: number | null = null;
|
|
271
|
+
for (const node of nodes) {
|
|
272
|
+
if (node.closest("[data-zabi-toaster]")) continue;
|
|
273
|
+
const box = node.getBoundingClientRect();
|
|
274
|
+
if (box.width === 0 || box.height === 0) continue;
|
|
275
|
+
// Only what is in the same columns, and under the stack at rest.
|
|
276
|
+
if (box.left >= rest.right || box.right <= rest.left) continue;
|
|
277
|
+
if (box.top >= rest.bottom || box.bottom <= rest.top) continue;
|
|
278
|
+
top = top === null ? box.top : Math.min(top, box.top);
|
|
279
|
+
}
|
|
280
|
+
return top;
|
|
281
|
+
}
|
|
282
|
+
|
|
148
283
|
/**
|
|
149
284
|
* Places the stack for what is on screen now. At rest it is where the
|
|
150
285
|
* classes put it, raised above the on-screen keyboard where that covers
|
|
@@ -175,10 +310,17 @@
|
|
|
175
310
|
const keyboard = readKeyboardInset();
|
|
176
311
|
const raise = (px: number) => stack.style.setProperty("--toaster-overlay-inset", `${px}px`);
|
|
177
312
|
if (keyboard > 0) raise(keyboard);
|
|
178
|
-
|
|
313
|
+
let rest = stack.getBoundingClientRect();
|
|
179
314
|
// No layout (a test DOM): nothing to place.
|
|
180
315
|
if (rest.width === 0 && rest.height === 0) return;
|
|
181
316
|
|
|
317
|
+
// Docked bars and named elements that lie under the stack: it sits above the highest.
|
|
318
|
+
const dockTop = dockedTop(rest);
|
|
319
|
+
if (dockTop !== null) {
|
|
320
|
+
raise(Math.max(keyboard, Math.ceil(viewportHeight - dockTop)));
|
|
321
|
+
rest = stack.getBoundingClientRect();
|
|
322
|
+
}
|
|
323
|
+
|
|
182
324
|
stack.style.top = TOP;
|
|
183
325
|
stack.style.bottom = "auto";
|
|
184
326
|
const topLimit = stack.getBoundingClientRect().top;
|
|
@@ -222,12 +364,21 @@
|
|
|
222
364
|
// over the header after all.
|
|
223
365
|
const padding = parseFloat(getComputedStyle(stack).paddingTop) || 0;
|
|
224
366
|
if (room < 2 * padding + 44) stack.style.paddingBlock = "0px";
|
|
225
|
-
// Where it was, or at its end when it has only now begun to scroll
|
|
226
|
-
// the newest toast is the last one.
|
|
227
|
-
|
|
367
|
+
// Where it was, or at its end when it has only now begun to scroll or was at its
|
|
368
|
+
// end before: the newest toast is the last one.
|
|
369
|
+
const atEnd = scrolledTo !== null && scrolledTo >= endAt - 1;
|
|
370
|
+
stack.scrollTop = scrolledTo === null || atEnd ? stack.scrollHeight : scrolledTo;
|
|
371
|
+
endAt = stack.scrollHeight - stack.clientHeight;
|
|
228
372
|
}
|
|
229
373
|
}
|
|
230
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Where the end of a stack that scrolls was, at the last layout. A toast's words arrive a
|
|
377
|
+
* moment after it does and make it taller: a stack that was at its end stays there, so the
|
|
378
|
+
* newest toast stays in view. One the reader has scrolled back stays where they put it.
|
|
379
|
+
*/
|
|
380
|
+
let endAt = 0;
|
|
381
|
+
|
|
231
382
|
/** How many toasts were shown at the last layout: a new one is scrolled to. */
|
|
232
383
|
let shownCount = 0;
|
|
233
384
|
|
|
@@ -238,6 +389,7 @@
|
|
|
238
389
|
// The newest is at the end: in a stack that scrolls, that is what to show.
|
|
239
390
|
if (stack && count > shownCount && stack.style.overflowY === "auto") {
|
|
240
391
|
stack.scrollTop = stack.scrollHeight;
|
|
392
|
+
endAt = stack.scrollHeight - stack.clientHeight;
|
|
241
393
|
}
|
|
242
394
|
shownCount = count;
|
|
243
395
|
});
|
|
@@ -256,10 +408,22 @@
|
|
|
256
408
|
for (const child of stack.children) observer?.observe(child);
|
|
257
409
|
});
|
|
258
410
|
children.observe(stack, { childList: true });
|
|
411
|
+
// A named element moves with the page: follow it, once a frame.
|
|
412
|
+
let frame = 0;
|
|
413
|
+
const onScroll = () => {
|
|
414
|
+
if (frame || stack.childElementCount === 0) return;
|
|
415
|
+
frame = requestAnimationFrame(() => {
|
|
416
|
+
frame = 0;
|
|
417
|
+
layout();
|
|
418
|
+
});
|
|
419
|
+
};
|
|
420
|
+
document.addEventListener("scroll", onScroll, { capture: true, passive: true });
|
|
259
421
|
window.addEventListener("resize", again);
|
|
260
422
|
window.visualViewport?.addEventListener("resize", again);
|
|
261
423
|
window.visualViewport?.addEventListener("scroll", again);
|
|
262
424
|
return () => {
|
|
425
|
+
document.removeEventListener("scroll", onScroll, { capture: true });
|
|
426
|
+
if (frame) cancelAnimationFrame(frame);
|
|
263
427
|
window.visualViewport?.removeEventListener("scroll", again);
|
|
264
428
|
stopFooters();
|
|
265
429
|
observer?.disconnect();
|
|
@@ -352,7 +516,7 @@
|
|
|
352
516
|
|
|
353
517
|
<div
|
|
354
518
|
{...restProps}
|
|
355
|
-
class={cn("pointer-events-none fixed z-toast flex min-w-0 flex-col gap-2 overscroll-contain", placement, className)}
|
|
519
|
+
class={cn("pointer-events-none fixed z-toast flex min-w-0 flex-col gap-2 overscroll-contain", placement, clipClass, className)}
|
|
356
520
|
role="region"
|
|
357
521
|
aria-label={ariaLabel ?? words.regionLabel}
|
|
358
522
|
data-zabi-toaster
|
|
@@ -362,6 +526,6 @@
|
|
|
362
526
|
>
|
|
363
527
|
<!-- Each toast owns its role="status"/"alert"; a region-level aria-live would announce twice. -->
|
|
364
528
|
{#each $toastStore as toast (toast.id)}
|
|
365
|
-
<ToasterToast {toast} strings={words} {showCountdown} {defaultDuration} {onpausechange} />
|
|
529
|
+
<ToasterToast {toast} strings={words} {showCountdown} {defaultDuration} {onpausechange} {snackbar} {swipeToDismiss} {timeLine} />
|
|
366
530
|
{/each}
|
|
367
531
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ToastDuration, type ToasterStrings, type ToastPauseChange } from "../util/toaster.js";
|
|
1
|
+
import { type ToastDuration, type ToasterMode, type ToasterStrings, type ToastPauseChange } from "../util/toaster.js";
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
/**
|
|
4
4
|
* The stack of toasts, fixed to the bottom of the screen: in the corner
|
|
@@ -14,7 +14,7 @@ import type { HTMLAttributes } from "svelte/elements";
|
|
|
14
14
|
* `--toaster-bottom-offset` on the Toaster or any ancestor:
|
|
15
15
|
*
|
|
16
16
|
* ```svelte
|
|
17
|
-
* <Toaster style="--toaster-bottom-offset:
|
|
17
|
+
* <Toaster style="--toaster-bottom-offset: 64px" />
|
|
18
18
|
* ```
|
|
19
19
|
*
|
|
20
20
|
* A `FloatingActionButton` is 56px with 16px under it: `72px` puts the
|
|
@@ -49,6 +49,29 @@ import type { HTMLAttributes } from "svelte/elements";
|
|
|
49
49
|
* own time (WCAG 2.2.1). A finger that lifts leaves it at least three
|
|
50
50
|
* seconds, so holding a short toast starts it again.
|
|
51
51
|
* `data-paused` on the toast says so, and `onpausechange` is called.
|
|
52
|
+
* A mouse holds the timer once it has moved over the toast, not when the
|
|
53
|
+
* toast comes up under a pointer that is at rest. A touch or a pen drags
|
|
54
|
+
* a toast away sideways (`swipeToDismiss`, on by default), and the close
|
|
55
|
+
* button stays as the way for a keyboard and a screen reader. A toast
|
|
56
|
+
* pushed with the id of one that is showing replaces it in place, and
|
|
57
|
+
* `max` limits how many show at once: the oldest toast that is timed
|
|
58
|
+
* (not an error, not one with an action) is the one that leaves.
|
|
59
|
+
*
|
|
60
|
+
* For a phone, `mode="auto"` makes the toast a snackbar below 640px: one
|
|
61
|
+
* row (two lines at most), one at a time, with its action beside its text
|
|
62
|
+
* and no stack. It sits directly above what is docked at the bottom, which
|
|
63
|
+
* it finds by itself: a `BottomTabBar`, a `StickyActionBar`, a
|
|
64
|
+
* `FloatingActionButton` and an `UnsavedChangesBar` that lie under it, and
|
|
65
|
+
* anything you name in `avoid` (the button a screen ends in). It comes out
|
|
66
|
+
* from behind that edge and goes back into it, on `--ease-standard`, with
|
|
67
|
+
* no movement under reduced motion, and lies over the page without moving
|
|
68
|
+
* it. Push a short confirmation with `closable: false` and it has no
|
|
69
|
+
* control and takes no taps.
|
|
70
|
+
*
|
|
71
|
+
* ```svelte
|
|
72
|
+
* <Toaster mode="auto" avoid="[data-primary-action]" />
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
52
75
|
* Every word the toaster says by itself is in `strings`:
|
|
53
76
|
*
|
|
54
77
|
* ```svelte
|
|
@@ -86,6 +109,41 @@ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "role" | "aria-label
|
|
|
86
109
|
* on the toast wins.
|
|
87
110
|
*/
|
|
88
111
|
defaultDuration?: ToastDuration;
|
|
112
|
+
/**
|
|
113
|
+
* `"stack"` (default): the stack of toasts. `"snackbar"`: the compact
|
|
114
|
+
* form for a phone, at every width. `"auto"`: the snackbar below
|
|
115
|
+
* 640px, the stack from there up.
|
|
116
|
+
*/
|
|
117
|
+
mode?: ToasterMode;
|
|
118
|
+
/**
|
|
119
|
+
* How many toasts show at once. The oldest one that is timed leaves
|
|
120
|
+
* when a newer one arrives; a toast that has to stay (an error, one
|
|
121
|
+
* with an action, `"persistent"`) is not dropped, so there can be more
|
|
122
|
+
* than this while they are showing. Default: no limit, and 1 in the
|
|
123
|
+
* snackbar.
|
|
124
|
+
*/
|
|
125
|
+
max?: number;
|
|
126
|
+
/**
|
|
127
|
+
* A CSS selector for elements the stack must not lie on, in addition
|
|
128
|
+
* to the docked bars it finds by itself: the button a screen ends in.
|
|
129
|
+
* When one lies under the stack, the stack sits above it.
|
|
130
|
+
*/
|
|
131
|
+
avoid?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Draws the thin bar along the bottom of a toast that empties over its
|
|
134
|
+
* time. Unset: drawn, except under a confirmation in the snackbar that
|
|
135
|
+
* has nothing to press (pushed with `closable: false`), where it is
|
|
136
|
+
* left out. `false` leaves it out everywhere, `true` draws it
|
|
137
|
+
* everywhere; a toast's own `timeLine` wins. The sentence for a screen
|
|
138
|
+
* reader about the time left stays.
|
|
139
|
+
*/
|
|
140
|
+
timeLine?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Touch and pen drag a toast away sideways (and down, in the
|
|
143
|
+
* snackbar). Default `true`. A mouse does not drag; the close button
|
|
144
|
+
* is the way for a keyboard and a screen reader.
|
|
145
|
+
*/
|
|
146
|
+
swipeToDismiss?: boolean;
|
|
89
147
|
/**
|
|
90
148
|
* Called when a pointer, a finger or focus starts or stops holding a
|
|
91
149
|
* toast's timer, with the toast's id. The same state is `data-paused`
|