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.
Files changed (273) hide show
  1. package/CHANGELOG.md +1011 -0
  2. package/README.md +1 -1
  3. package/THEME.md +18 -5
  4. package/THEMING.md +308 -74
  5. package/dist/atoms/ActionPanel.svelte +1 -1
  6. package/dist/atoms/Avatar.svelte +19 -3
  7. package/dist/atoms/Avatar.svelte.d.ts +12 -0
  8. package/dist/atoms/Badge.svelte +37 -8
  9. package/dist/atoms/Badge.svelte.d.ts +6 -0
  10. package/dist/atoms/Block.svelte +74 -0
  11. package/dist/atoms/Block.svelte.d.ts +29 -0
  12. package/dist/atoms/Button.svelte +52 -15
  13. package/dist/atoms/Button.svelte.d.ts +3 -2
  14. package/dist/atoms/Card.svelte +181 -25
  15. package/dist/atoms/Card.svelte.d.ts +55 -3
  16. package/dist/atoms/Checkbox.svelte +33 -0
  17. package/dist/atoms/Checkbox.svelte.d.ts +27 -0
  18. package/dist/atoms/Chip.svelte +351 -0
  19. package/dist/atoms/Chip.svelte.d.ts +57 -0
  20. package/dist/atoms/CodeField.svelte +385 -0
  21. package/dist/atoms/CodeField.svelte.d.ts +61 -0
  22. package/dist/atoms/ColorPicker.svelte +1 -0
  23. package/dist/atoms/DateField.svelte +41 -3
  24. package/dist/atoms/DateField.svelte.d.ts +31 -3
  25. package/dist/atoms/FloatingActionButton.svelte +5 -3
  26. package/dist/atoms/FloatingActionButton.svelte.d.ts +1 -1
  27. package/dist/atoms/Heading.svelte +27 -1
  28. package/dist/atoms/Heading.svelte.d.ts +7 -0
  29. package/dist/atoms/IconButton.svelte +81 -9
  30. package/dist/atoms/IconButton.svelte.d.ts +13 -0
  31. package/dist/atoms/Input.svelte +10 -5
  32. package/dist/atoms/Input.svelte.d.ts +3 -2
  33. package/dist/atoms/List.svelte +3 -2
  34. package/dist/atoms/ListItem.svelte +31 -7
  35. package/dist/atoms/ListItem.svelte.d.ts +6 -0
  36. package/dist/atoms/PickerField.svelte +239 -0
  37. package/dist/atoms/PickerField.svelte.d.ts +67 -0
  38. package/dist/atoms/Progress.svelte +78 -17
  39. package/dist/atoms/Progress.svelte.d.ts +11 -1
  40. package/dist/atoms/Radio.svelte +32 -0
  41. package/dist/atoms/Radio.svelte.d.ts +26 -0
  42. package/dist/atoms/Rating.svelte +132 -6
  43. package/dist/atoms/Rating.svelte.d.ts +24 -3
  44. package/dist/atoms/Select.svelte +24 -26
  45. package/dist/atoms/Select.svelte.d.ts +12 -0
  46. package/dist/atoms/SelectionControl.svelte +99 -6
  47. package/dist/atoms/SelectionControl.svelte.d.ts +25 -0
  48. package/dist/atoms/Skeleton.svelte +114 -18
  49. package/dist/atoms/Skeleton.svelte.d.ts +30 -3
  50. package/dist/atoms/SkeletonGroup.svelte +43 -0
  51. package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
  52. package/dist/atoms/Slider.svelte +35 -0
  53. package/dist/atoms/Sparkline.svelte +109 -0
  54. package/dist/atoms/Sparkline.svelte.d.ts +27 -0
  55. package/dist/atoms/SpeechBubble.svelte +187 -0
  56. package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
  57. package/dist/atoms/Stat.svelte +72 -0
  58. package/dist/atoms/Stat.svelte.d.ts +19 -0
  59. package/dist/atoms/TemporalField.svelte +342 -10
  60. package/dist/atoms/TemporalField.svelte.d.ts +20 -4
  61. package/dist/atoms/Textarea.svelte +100 -5
  62. package/dist/atoms/Textarea.svelte.d.ts +21 -0
  63. package/dist/atoms/TimeColumn.svelte +150 -0
  64. package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
  65. package/dist/atoms/TimeField.svelte +46 -3
  66. package/dist/atoms/TimeField.svelte.d.ts +34 -3
  67. package/dist/atoms/Toast.svelte +6 -5
  68. package/dist/atoms/Toggle.svelte +61 -10
  69. package/dist/atoms/Toggle.svelte.d.ts +15 -0
  70. package/dist/atoms/Tooltip.svelte +20 -1
  71. package/dist/atoms/Tooltip.svelte.d.ts +7 -0
  72. package/dist/atoms/index.d.ts +21 -0
  73. package/dist/atoms/index.js +17 -0
  74. package/dist/atoms/selection-control.styles.d.ts +15 -4
  75. package/dist/atoms/selection-control.styles.js +16 -5
  76. package/dist/components/atoms/index.d.ts +21 -0
  77. package/dist/components/atoms/index.d.ts.map +1 -1
  78. package/dist/components/atoms/selection-control.styles.d.ts +15 -4
  79. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  80. package/dist/components/index.d.ts +27 -3
  81. package/dist/components/index.d.ts.map +1 -1
  82. package/dist/components/map.d.ts +11 -0
  83. package/dist/components/map.d.ts.map +1 -0
  84. package/dist/components/molecules/index.d.ts +7 -2
  85. package/dist/components/molecules/index.d.ts.map +1 -1
  86. package/dist/components/molecules/toast-store.d.ts +30 -0
  87. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  88. package/dist/components/organisms/index.d.ts +6 -1
  89. package/dist/components/organisms/index.d.ts.map +1 -1
  90. package/dist/components/types/variants.d.ts +12 -3
  91. package/dist/components/types/variants.d.ts.map +1 -1
  92. package/dist/components/util/app-shell.d.ts +55 -4
  93. package/dist/components/util/app-shell.d.ts.map +1 -1
  94. package/dist/components/util/avatar.d.ts +3 -0
  95. package/dist/components/util/avatar.d.ts.map +1 -1
  96. package/dist/components/util/bottom-tab-bar.d.ts +6 -0
  97. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -1
  98. package/dist/components/util/chart.d.ts +140 -0
  99. package/dist/components/util/chart.d.ts.map +1 -0
  100. package/dist/components/util/chip.d.ts +66 -0
  101. package/dist/components/util/chip.d.ts.map +1 -0
  102. package/dist/components/util/cn.d.ts.map +1 -1
  103. package/dist/components/util/code-field.d.ts +63 -0
  104. package/dist/components/util/code-field.d.ts.map +1 -0
  105. package/dist/components/util/collapsible.d.ts +6 -0
  106. package/dist/components/util/collapsible.d.ts.map +1 -1
  107. package/dist/components/util/display-voice.d.ts +10 -0
  108. package/dist/components/util/display-voice.d.ts.map +1 -0
  109. package/dist/components/util/field-control.d.ts +30 -0
  110. package/dist/components/util/field-control.d.ts.map +1 -0
  111. package/dist/components/util/field.d.ts +6 -0
  112. package/dist/components/util/field.d.ts.map +1 -1
  113. package/dist/components/util/focus-utils.d.ts +2 -0
  114. package/dist/components/util/focus-utils.d.ts.map +1 -1
  115. package/dist/components/util/map-view.d.ts +144 -0
  116. package/dist/components/util/map-view.d.ts.map +1 -0
  117. package/dist/components/util/photo.d.ts +19 -0
  118. package/dist/components/util/photo.d.ts.map +1 -1
  119. package/dist/components/util/progress.d.ts +19 -0
  120. package/dist/components/util/progress.d.ts.map +1 -0
  121. package/dist/components/util/scroll-edge.d.ts +33 -0
  122. package/dist/components/util/scroll-edge.d.ts.map +1 -0
  123. package/dist/components/util/sheet-drag.d.ts +25 -1
  124. package/dist/components/util/sheet-drag.d.ts.map +1 -1
  125. package/dist/components/util/skeleton.d.ts +15 -0
  126. package/dist/components/util/skeleton.d.ts.map +1 -0
  127. package/dist/components/util/sliding-indicator.d.ts +25 -0
  128. package/dist/components/util/sliding-indicator.d.ts.map +1 -0
  129. package/dist/components/util/temporal-field.d.ts +89 -0
  130. package/dist/components/util/temporal-field.d.ts.map +1 -0
  131. package/dist/components/util/toaster.d.ts +40 -0
  132. package/dist/components/util/toaster.d.ts.map +1 -1
  133. package/dist/components/util/tone-surface.d.ts +34 -0
  134. package/dist/components/util/tone-surface.d.ts.map +1 -0
  135. package/dist/components/util/top-navbar.d.ts +26 -2
  136. package/dist/components/util/top-navbar.d.ts.map +1 -1
  137. package/dist/components/util/zabi-strings.d.ts +9 -0
  138. package/dist/components/util/zabi-strings.d.ts.map +1 -1
  139. package/dist/create-theme/cli.js +6 -2
  140. package/dist/create-theme/index.d.ts +17 -3
  141. package/dist/create-theme/index.js +252 -35
  142. package/dist/create-theme/lib/resolve.js +97 -0
  143. package/dist/create-theme/theme-data.js +1618 -90
  144. package/dist/index.d.ts +27 -3
  145. package/dist/index.js +18 -0
  146. package/dist/map.d.ts +10 -0
  147. package/dist/map.js +9 -0
  148. package/dist/molecules/Alert.svelte +31 -12
  149. package/dist/molecules/Alert.svelte.d.ts +12 -1
  150. package/dist/molecules/AppBar.svelte +287 -28
  151. package/dist/molecules/AppBar.svelte.d.ts +62 -0
  152. package/dist/molecules/AvatarGroup.svelte +1 -1
  153. package/dist/molecules/BarChart.svelte +381 -0
  154. package/dist/molecules/BarChart.svelte.d.ts +45 -0
  155. package/dist/molecules/BottomSheet.svelte +88 -20
  156. package/dist/molecules/BottomSheet.svelte.d.ts +17 -1
  157. package/dist/molecules/BottomTabBar.svelte +134 -28
  158. package/dist/molecules/BottomTabBar.svelte.d.ts +36 -2
  159. package/dist/molecules/Calendar.svelte.d.ts +1 -1
  160. package/dist/molecules/ChipGroup.svelte +325 -0
  161. package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
  162. package/dist/molecules/Collapsible.svelte +158 -54
  163. package/dist/molecules/Collapsible.svelte.d.ts +23 -6
  164. package/dist/molecules/CollapsibleGroup.svelte +16 -6
  165. package/dist/molecules/ConfirmDialog.svelte +5 -1
  166. package/dist/molecules/ConfirmDialog.svelte.d.ts +3 -0
  167. package/dist/molecules/Drawer.svelte +33 -12
  168. package/dist/molecules/Dropdown.svelte +9 -1
  169. package/dist/molecules/Dropdown.svelte.d.ts +2 -0
  170. package/dist/molecules/DropdownItem.svelte +1 -1
  171. package/dist/molecules/ImageUpload.svelte +230 -120
  172. package/dist/molecules/ImageUpload.svelte.d.ts +38 -3
  173. package/dist/molecules/LineChart.svelte +459 -0
  174. package/dist/molecules/LineChart.svelte.d.ts +53 -0
  175. package/dist/molecules/MapView.svelte +565 -0
  176. package/dist/molecules/MapView.svelte.d.ts +88 -0
  177. package/dist/molecules/Modal.svelte +11 -6
  178. package/dist/molecules/PhotoGrid.svelte +100 -10
  179. package/dist/molecules/PhotoGrid.svelte.d.ts +11 -0
  180. package/dist/molecules/PhotoViewer.svelte +121 -24
  181. package/dist/molecules/PhotoViewer.svelte.d.ts +18 -0
  182. package/dist/molecules/PieChart.svelte +421 -0
  183. package/dist/molecules/PieChart.svelte.d.ts +51 -0
  184. package/dist/molecules/RadioGroup.svelte +20 -4
  185. package/dist/molecules/RadioGroup.svelte.d.ts +5 -0
  186. package/dist/molecules/SegmentedControl.svelte +136 -3
  187. package/dist/molecules/SegmentedControl.svelte.d.ts +6 -0
  188. package/dist/molecules/SidebarFooter.svelte +1 -1
  189. package/dist/molecules/SidebarNavSection.svelte +1 -1
  190. package/dist/molecules/SortableList.svelte +1 -0
  191. package/dist/molecules/Stepper.svelte +21 -7
  192. package/dist/molecules/StickyActionBar.svelte +67 -8
  193. package/dist/molecules/StickyActionBar.svelte.d.ts +14 -1
  194. package/dist/molecules/Tabs.svelte +79 -2
  195. package/dist/molecules/Toaster.svelte +171 -7
  196. package/dist/molecules/Toaster.svelte.d.ts +60 -2
  197. package/dist/molecules/ToasterToast.svelte +292 -60
  198. package/dist/molecules/ToasterToast.svelte.d.ts +6 -0
  199. package/dist/molecules/UnsavedChangesBar.svelte +13 -2
  200. package/dist/molecules/index.d.ts +7 -2
  201. package/dist/molecules/index.js +4 -0
  202. package/dist/molecules/toast-store.d.ts +30 -0
  203. package/dist/molecules/toast-store.js +4 -1
  204. package/dist/organisms/AppNavigation.svelte +440 -0
  205. package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
  206. package/dist/organisms/AppShell.svelte +539 -39
  207. package/dist/organisms/AppShell.svelte.d.ts +102 -6
  208. package/dist/organisms/AppShellHeaderPart.svelte +163 -0
  209. package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
  210. package/dist/organisms/SidebarNavigation.svelte +23 -36
  211. package/dist/organisms/SidebarPanel.svelte +10 -14
  212. package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
  213. package/dist/organisms/SidebarShell.svelte +2 -3
  214. package/dist/organisms/TopNavbar.svelte +117 -28
  215. package/dist/organisms/TopNavbar.svelte.d.ts +22 -1
  216. package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
  217. package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
  218. package/dist/organisms/index.d.ts +6 -1
  219. package/dist/organisms/index.js +4 -0
  220. package/dist/types/components.d.ts +17 -1
  221. package/dist/types/components.d.ts.map +1 -1
  222. package/dist/types/index.d.ts +497 -10
  223. package/dist/types/index.d.ts.map +1 -1
  224. package/dist/types/variants.ts +14 -3
  225. package/dist/util/app-shell.d.ts +55 -4
  226. package/dist/util/app-shell.js +32 -7
  227. package/dist/util/avatar.d.ts +3 -0
  228. package/dist/util/bottom-tab-bar.d.ts +6 -0
  229. package/dist/util/chart.d.ts +139 -0
  230. package/dist/util/chart.js +305 -0
  231. package/dist/util/chip.d.ts +65 -0
  232. package/dist/util/chip.js +58 -0
  233. package/dist/util/cn.js +28 -2
  234. package/dist/util/code-field.d.ts +62 -0
  235. package/dist/util/code-field.js +112 -0
  236. package/dist/util/collapsible.d.ts +6 -0
  237. package/dist/util/display-voice.d.ts +9 -0
  238. package/dist/util/display-voice.js +9 -0
  239. package/dist/util/field-control.d.ts +29 -0
  240. package/dist/util/field-control.js +32 -0
  241. package/dist/util/field.d.ts +6 -0
  242. package/dist/util/focus-utils.d.ts +2 -0
  243. package/dist/util/focus-utils.js +4 -0
  244. package/dist/util/map-view.d.ts +143 -0
  245. package/dist/util/map-view.js +121 -0
  246. package/dist/util/photo.d.ts +19 -0
  247. package/dist/util/photo.js +3 -0
  248. package/dist/util/progress.d.ts +18 -0
  249. package/dist/util/progress.js +17 -0
  250. package/dist/util/scroll-edge.d.ts +32 -0
  251. package/dist/util/scroll-edge.js +87 -0
  252. package/dist/util/sheet-drag.d.ts +25 -1
  253. package/dist/util/sheet-drag.js +71 -1
  254. package/dist/util/skeleton.d.ts +14 -0
  255. package/dist/util/skeleton.js +22 -0
  256. package/dist/util/sliding-indicator.d.ts +24 -0
  257. package/dist/util/sliding-indicator.js +44 -0
  258. package/dist/util/temporal-field.d.ts +88 -0
  259. package/dist/util/temporal-field.js +136 -0
  260. package/dist/util/toaster.d.ts +40 -0
  261. package/dist/util/toaster.js +55 -0
  262. package/dist/util/tone-surface.d.ts +33 -0
  263. package/dist/util/tone-surface.js +35 -0
  264. package/dist/util/top-navbar.d.ts +26 -2
  265. package/dist/util/top-navbar.js +23 -0
  266. package/dist/util/zabi-strings.d.ts +9 -0
  267. package/dist/zabi-components-colors.css +9 -3
  268. package/dist/zabi-components-theme-dark-only.css +474 -164
  269. package/dist/zabi-components-theme-dark.css +474 -164
  270. package/dist/zabi-components-theme-only.css +933 -99
  271. package/dist/zabi-components-theme.css +933 -99
  272. package/dist/zabi-components.css +2275 -321
  273. 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 title = $derived(toast.title?.trim() ?? "");
61
- const message = $derived(toast.message.trim());
62
-
63
- /**
64
- * What a toast pushed with neither a title nor a message says: a word
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
- onMount(() => {
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
- * The kind of pointer last seen on the toast. After a tap a browser makes
217
- * up mouse events for it, `mouseenter` among them and never a
218
- * `mouseleave`: taken for a mouse, a tap held the timer for good.
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 handleMouseEnter() {
233
- if (lastPointer !== "touch") hovered = true;
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
- class={cn("pointer-events-auto relative w-full min-w-[min(18rem,100%)] shrink-0 overflow-hidden rounded-overlay border border-border-overlay bg-surface-overlay shadow-lg", className)}
272
- in:fly={toastEnter}
273
- out:fly={toastLeave}
274
- onpointerenter={handlePointerEnter}
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
- onmouseenter={handleMouseEnter}
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 border border-border-overlay bg-surface-overlay px-4 py-3 shadow-lg",
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 && (position === "top" ? "top-2" : "bottom-[max(0.5rem,env(safe-area-inset-bottom))]"),
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';
@@ -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
  /**