varsel 0.1.1 → 0.2.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 (48) hide show
  1. package/dist/VarselItem.svelte +228 -229
  2. package/dist/VarselItem.svelte.d.ts +19 -23
  3. package/dist/VarselItem.svelte.d.ts.map +1 -1
  4. package/dist/VarselManager.svelte +58 -37
  5. package/dist/VarselManager.svelte.d.ts +7 -18
  6. package/dist/VarselManager.svelte.d.ts.map +1 -1
  7. package/dist/VarselToaster.svelte +22 -5
  8. package/dist/VarselToaster.svelte.d.ts +12 -19
  9. package/dist/VarselToaster.svelte.d.ts.map +1 -1
  10. package/dist/core/accessibility.d.ts +4 -0
  11. package/dist/core/accessibility.d.ts.map +1 -1
  12. package/dist/core/accessibility.js +4 -0
  13. package/dist/core/animations.d.ts +15 -0
  14. package/dist/core/animations.d.ts.map +1 -1
  15. package/dist/core/animations.js +15 -0
  16. package/dist/core/positions.d.ts +7 -0
  17. package/dist/core/positions.d.ts.map +1 -1
  18. package/dist/core/positions.js +4 -0
  19. package/dist/core/swipe.d.ts +12 -0
  20. package/dist/core/swipe.d.ts.map +1 -1
  21. package/dist/core/swipe.js +10 -0
  22. package/dist/core/toast-factory.d.ts.map +1 -1
  23. package/dist/core/toast-factory.js +38 -0
  24. package/dist/core/toast-state.d.ts +32 -0
  25. package/dist/core/toast-state.d.ts.map +1 -1
  26. package/dist/core/toast-state.js +33 -0
  27. package/dist/core/toaster-instances.d.ts +17 -0
  28. package/dist/core/toaster-instances.d.ts.map +1 -1
  29. package/dist/core/toaster-instances.js +17 -0
  30. package/dist/core/types.d.ts +70 -0
  31. package/dist/core/types.d.ts.map +1 -1
  32. package/dist/core/utils.d.ts +8 -0
  33. package/dist/core/utils.d.ts.map +1 -1
  34. package/dist/core/utils.js +8 -0
  35. package/dist/core/variants.d.ts +10 -0
  36. package/dist/core/variants.d.ts.map +1 -1
  37. package/dist/core/variants.js +16 -0
  38. package/dist/index.d.ts +6 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +6 -0
  41. package/dist/internals.d.ts +5 -0
  42. package/dist/internals.d.ts.map +1 -1
  43. package/dist/internals.js +5 -0
  44. package/dist/styles.css +1 -765
  45. package/dist/variant-icons.d.ts +9 -0
  46. package/dist/variant-icons.d.ts.map +1 -1
  47. package/dist/variant-icons.js +5 -0
  48. package/package.json +10 -4
@@ -1,4 +1,10 @@
1
1
  <script lang="ts">
2
+ /**
3
+ * @component
4
+ * @description
5
+ * Individual toast component. Handles its own enter/exit animations,
6
+ * swipe-to-dismiss gestures, auto-closing timer, and rendering of content.
7
+ */
2
8
  import { onDestroy, onMount } from "svelte";
3
9
  import {
4
10
  ANIMATION_CONFIG,
@@ -20,101 +26,101 @@ import {
20
26
  import {
21
27
  hasVariantIcon,
22
28
  variantIconMap,
23
- type VariantIconDefinition,
24
29
  } from "./variant-icons";
25
30
 
26
- export let toast: PositionedToast;
27
- export let onRemove: (id: string) => void;
28
- export let isGroupHovered = false;
29
- export let expandedOffset = 0;
30
- export let expandedGap: number = ANIMATION_CONFIG.EXPANDED_GAP;
31
- export let collapsedOffset: number | undefined = undefined;
32
- export let hiddenCollapsedOffset: number | undefined = undefined;
33
- export let onHeightChange: ((id: string, height: number) => void) | undefined =
34
- undefined;
35
- export let onGroupHoverEnter: (() => void) | undefined = undefined;
36
- export let onGroupHoldChange:
37
- | ((holding: boolean) => void)
38
- | undefined = undefined;
39
-
40
- let toastRef: HTMLDivElement | null = null;
41
- let isItemHovered = false;
42
- let isSwiping = false;
43
- let swipeDismissDirection: SwipeDirection | null = null;
44
- let animationState: "entering" | "entered" | "exiting" | "stacking" =
45
- "entering";
31
+ let {
32
+ toast,
33
+ onRemove,
34
+ isGroupHovered = false,
35
+ expandedOffset = 0,
36
+ expandedGap = ANIMATION_CONFIG.EXPANDED_GAP,
37
+ collapsedOffset = undefined,
38
+ hiddenCollapsedOffset = undefined,
39
+ onHeightChange = undefined,
40
+ onGroupHoverEnter = undefined,
41
+ onGroupHoldChange = undefined,
42
+ }: {
43
+ /** The toast data object containing all content and state. */
44
+ toast: PositionedToast;
45
+ /** Callback to remove the toast from the DOM after exit animation. */
46
+ onRemove: (id: string) => void;
47
+ /** Whether the parent group (stack) is currently hovered. */
48
+ isGroupHovered?: boolean;
49
+ /** The vertical offset for this toast when the stack is expanded. */
50
+ expandedOffset?: number;
51
+ /** The gap between toasts when expanded. */
52
+ expandedGap?: number;
53
+ /** The vertical offset for this toast when the stack is collapsed. */
54
+ collapsedOffset?: number;
55
+ /** The offset to use when the toast is hidden in the stack. */
56
+ hiddenCollapsedOffset?: number;
57
+ /** Callback to report the height of the toast to the manager. */
58
+ onHeightChange?: (id: string, height: number) => void;
59
+ /** Callback to notify the manager that the user has entered the toast area. */
60
+ onGroupHoverEnter?: () => void;
61
+ /** Callback to notify the manager that the user is interacting (holding) the toast. */
62
+ onGroupHoldChange?: (holding: boolean) => void;
63
+ } = $props();
64
+
65
+ let id = $derived(toast.id);
66
+ let title = $derived(toast.title);
67
+ let description = $derived(toast.description);
68
+ let variant = $derived(toast.variant || "default");
69
+ let duration = $derived(toast.duration || 5000);
70
+ let action = $derived(toast.action);
71
+ let isLoading = $derived(toast.isLoading || false);
72
+ let index = $derived(toast.index);
73
+ let renderIndex = $derived(toast.renderIndex);
74
+ let shouldClose = $derived(toast.shouldClose);
75
+ let position = $derived(toast.position || "bottom-center");
76
+ let className = $derived(toast.className || "");
77
+ let onClose = $derived(toast.onClose);
78
+ let showClose = $derived(toast.showClose ?? true);
79
+
80
+ let toastRef = $state<HTMLDivElement | null>(null);
81
+ let isItemHovered = $state(false);
82
+ let isSwiping = $state(false);
83
+ let swipeDismissDirection = $state<SwipeDirection | null>(null);
84
+ let animationState = $state<"entering" | "entered" | "exiting" | "stacking">(
85
+ "entering",
86
+ );
46
87
 
47
88
  let timeoutRef: ReturnType<typeof setTimeout> | null = null;
48
89
  let timerStartRef: number | null = null;
49
- let remainingTime: number | null = Number.NaN;
90
+ let remainingTime = $state<number | null>(Number.NaN);
50
91
  let enterAnimationFrame: number | null = null;
51
92
  let focusTimeout: ReturnType<typeof setTimeout> | null = null;
52
93
  let pointerStart: { x: number; y: number } | null = null;
53
94
  let dragStartTime: number | null = null;
54
95
  let swipeAxis: SwipeAxis | null = null;
55
96
  let lastSwipe = { x: 0, y: 0 };
56
- let resizeCleanup: (() => void) | null = null;
57
- let mounted = false;
97
+ let mounted = $state(false);
58
98
  let prevShouldClose = false;
59
99
  let previousDuration: number | undefined;
60
- let isExiting = false;
100
+ let isExiting = $state(false);
61
101
  let exitAnimationComplete = false;
62
- let hasAnimatedIn = false;
102
+ let hasAnimatedIn = $state(false);
63
103
  let isPointerHeld = false;
64
- let iconConfig: VariantIconDefinition | undefined;
65
- let showStatusIcon = false;
66
104
  type SpinnerState = "hidden" | "loading" | "finishing";
67
- let spinnerState: SpinnerState = "hidden";
105
+ let spinnerState = $state<SpinnerState>("hidden");
68
106
  let spinnerFinishTimer: ReturnType<typeof setTimeout> | null = null;
69
- let shouldRenderSpinner = false;
70
- let hasShownSpinner = false;
71
- let iconStateClass: string | undefined;
72
-
73
- let id: string;
74
- let title: string | undefined;
75
- let description: string | undefined;
76
- let variant: PositionedToast["variant"];
77
- let duration: number;
78
- let action: PositionedToast["action"];
79
- let isLoading: boolean | undefined;
80
- let index: number;
81
- let renderIndex: number;
82
- let shouldClose: boolean | undefined;
83
- let position: PositionedToast["position"];
84
- let className: string | undefined;
85
- let onClose: (() => void) | undefined;
86
- let showClose: boolean;
87
-
88
- $: ({
89
- id,
90
- title,
91
- description,
92
- variant = "default",
93
- duration = 5000,
94
- action,
95
- isLoading = false,
96
- index,
97
- renderIndex,
98
- shouldClose,
99
- position = "bottom-center",
100
- className = "",
101
- onClose,
102
- showClose = true,
103
- } = toast);
104
-
105
- $: if (isLoading) {
106
- hasShownSpinner = true;
107
- }
108
-
109
- $: {
107
+ let hasShownSpinner = $state(false);
108
+
109
+ $effect(() => {
110
+ if (isLoading) {
111
+ hasShownSpinner = true;
112
+ }
113
+ });
114
+
115
+ $effect(() => {
110
116
  if (isLoading) {
111
117
  spinnerState = "loading";
112
118
  } else if (spinnerState === "loading") {
113
119
  spinnerState = "finishing";
114
120
  }
115
- }
121
+ });
116
122
 
117
- $: {
123
+ $effect(() => {
118
124
  if (spinnerState === "finishing") {
119
125
  if (!spinnerFinishTimer) {
120
126
  spinnerFinishTimer = setTimeout(() => {
@@ -126,23 +132,29 @@ $: {
126
132
  clearTimeout(spinnerFinishTimer);
127
133
  spinnerFinishTimer = null;
128
134
  }
129
- }
135
+ });
130
136
 
131
- $: shouldRenderSpinner = spinnerState !== "hidden";
137
+ let shouldRenderSpinner = $derived(spinnerState !== "hidden");
132
138
 
133
139
  const handleSpinnerAnimationEnd = (event: AnimationEvent) => {
134
140
  if (event.animationName !== "vs-spinner-finish") return;
135
141
  spinnerState = "hidden";
136
142
  };
137
143
 
138
- $: iconStateClass =
144
+ let iconConfig = $derived(
145
+ hasVariantIcon(variant) ? variantIconMap[variant] : undefined,
146
+ );
147
+ let showStatusIcon = $derived(isLoading || Boolean(iconConfig));
148
+
149
+ let iconStateClass = $derived(
139
150
  !iconConfig
140
151
  ? undefined
141
152
  : !hasShownSpinner
142
153
  ? "vs-icon--static"
143
154
  : isLoading
144
155
  ? "vs-icon--waiting"
145
- : "vs-icon--pop";
156
+ : "vs-icon--pop",
157
+ );
146
158
 
147
159
  const clearSwipeRefs = () => {
148
160
  pointerStart = null;
@@ -192,13 +204,13 @@ const handleClose = () => {
192
204
  toastState.update(id, { shouldClose: true, isLeaving: true });
193
205
  };
194
206
 
195
- $: {
207
+ $effect(() => {
196
208
  const desiredClose = Boolean(shouldClose);
197
209
  if (desiredClose && !prevShouldClose) {
198
210
  handleClose();
199
211
  }
200
212
  prevShouldClose = desiredClose;
201
- }
213
+ });
202
214
 
203
215
  onMount(() => {
204
216
  mounted = true;
@@ -207,7 +219,6 @@ onMount(() => {
207
219
  if (enterAnimationFrame) cancelAnimationFrame(enterAnimationFrame);
208
220
  if (timeoutRef) clearTimeout(timeoutRef);
209
221
  if (focusTimeout) clearTimeout(focusTimeout);
210
- resizeCleanup?.();
211
222
  };
212
223
  });
213
224
 
@@ -215,7 +226,6 @@ onDestroy(() => {
215
226
  if (enterAnimationFrame) cancelAnimationFrame(enterAnimationFrame);
216
227
  if (timeoutRef) clearTimeout(timeoutRef);
217
228
  if (focusTimeout) clearTimeout(focusTimeout);
218
- resizeCleanup?.();
219
229
  if (spinnerFinishTimer) {
220
230
  clearTimeout(spinnerFinishTimer);
221
231
  spinnerFinishTimer = null;
@@ -226,24 +236,27 @@ onDestroy(() => {
226
236
  }
227
237
  });
228
238
 
229
- $: if (mounted && duration !== previousDuration) {
230
- remainingTime = duration;
231
- previousDuration = duration;
232
- }
239
+ $effect(() => {
240
+ if (mounted && duration !== previousDuration) {
241
+ remainingTime = duration;
242
+ previousDuration = duration;
243
+ }
244
+ });
233
245
 
234
- $: if (mounted) {
235
- resizeCleanup?.();
236
- if (!toastRef || !onHeightChange) {
237
- resizeCleanup = null;
238
- } else {
239
- const el = toastRef;
240
- const notify = () => onHeightChange?.(id, el.offsetHeight);
241
- const ro = new ResizeObserver(() => notify());
242
- ro.observe(el);
243
- notify();
244
- resizeCleanup = () => ro.disconnect();
246
+ $effect(() => {
247
+ if (mounted) {
248
+ if (!toastRef || !onHeightChange) {
249
+ // do nothing
250
+ } else {
251
+ const el = toastRef;
252
+ const notify = () => onHeightChange?.(id, el.offsetHeight);
253
+ const ro = new ResizeObserver(() => notify());
254
+ ro.observe(el);
255
+ notify();
256
+ return () => ro.disconnect();
257
+ }
245
258
  }
246
- }
259
+ });
247
260
 
248
261
  const setFocusToToast = () => {
249
262
  if (!toastRef) return;
@@ -256,87 +269,91 @@ const setFocusToToast = () => {
256
269
  toastRef.focus();
257
270
  };
258
271
 
259
- $: iconConfig = hasVariantIcon(variant) ? variantIconMap[variant] : undefined;
260
- $: showStatusIcon = isLoading || Boolean(iconConfig);
261
-
262
- $: if (mounted && toastRef && !isExiting) {
263
- if (!hasAnimatedIn && isLatest) {
264
- hasAnimatedIn = true;
265
- animationState = "entering";
266
- if (enterAnimationFrame) {
267
- cancelAnimationFrame(enterAnimationFrame);
268
- }
269
- enterAnimationFrame = requestAnimationFrame(() => {
272
+ $effect(() => {
273
+ if (mounted && toastRef && !isExiting) {
274
+ if (!hasAnimatedIn && isLatest) {
275
+ hasAnimatedIn = true;
276
+ animationState = "entering";
277
+ if (enterAnimationFrame) {
278
+ cancelAnimationFrame(enterAnimationFrame);
279
+ }
270
280
  enterAnimationFrame = requestAnimationFrame(() => {
271
- animationState = "entered";
272
- if (action) {
273
- if (focusTimeout) clearTimeout(focusTimeout);
274
- focusTimeout = setTimeout(
275
- () => setFocusToToast(),
276
- ANIMATION_CONFIG.ENTER_DURATION * 1000,
277
- );
278
- }
281
+ enterAnimationFrame = requestAnimationFrame(() => {
282
+ animationState = "entered";
283
+ if (action) {
284
+ if (focusTimeout) clearTimeout(focusTimeout);
285
+ focusTimeout = setTimeout(
286
+ () => setFocusToToast(),
287
+ ANIMATION_CONFIG.ENTER_DURATION * 1000,
288
+ );
289
+ }
290
+ });
279
291
  });
280
- });
281
- } else if (hasAnimatedIn) {
282
- if (animationState !== "stacking" || index > 0) {
292
+ } else if (hasAnimatedIn) {
293
+ if (animationState !== "stacking" || index > 0) {
294
+ animationState = "stacking";
295
+ }
296
+ } else {
283
297
  animationState = "stacking";
284
298
  }
285
- } else {
286
- animationState = "stacking";
287
299
  }
288
- }
289
-
290
- $: if (mounted) {
291
- if (shouldClose || !hasAnimatedIn || duration <= 0) {
292
- if (timeoutRef) {
293
- clearTimeout(timeoutRef);
294
- timeoutRef = null;
295
- }
296
- timerStartRef = null;
297
- } else {
298
- if (remainingTime == null || Number.isNaN(remainingTime)) {
299
- remainingTime = duration;
300
- }
301
-
302
- const isPaused =
303
- isGroupHovered || isItemHovered || isSwiping || hiddenByStacking;
300
+ });
304
301
 
305
- if (isPaused) {
302
+ $effect(() => {
303
+ if (mounted) {
304
+ if (shouldClose || !hasAnimatedIn || duration <= 0) {
306
305
  if (timeoutRef) {
307
306
  clearTimeout(timeoutRef);
308
307
  timeoutRef = null;
309
308
  }
310
- if (timerStartRef !== null) {
311
- const elapsed = Date.now() - timerStartRef;
312
- remainingTime = Math.max(0, (remainingTime ?? duration) - elapsed);
313
- timerStartRef = null;
314
- }
309
+ timerStartRef = null;
315
310
  } else {
316
- if (!timeoutRef) {
317
- const ms = Math.max(0, remainingTime ?? duration);
318
- if (ms === 0) {
319
- handleClose();
320
- } else {
321
- timerStartRef = Date.now();
322
- timeoutRef = setTimeout(() => {
311
+ if (remainingTime == null || Number.isNaN(remainingTime)) {
312
+ remainingTime = duration;
313
+ }
314
+
315
+ const isPaused =
316
+ isGroupHovered || isItemHovered || isSwiping || hiddenByStacking;
317
+
318
+ if (isPaused) {
319
+ if (timeoutRef) {
320
+ clearTimeout(timeoutRef);
321
+ timeoutRef = null;
322
+ }
323
+ if (timerStartRef !== null) {
324
+ const elapsed = Date.now() - timerStartRef;
325
+ remainingTime = Math.max(0, (remainingTime ?? duration) - elapsed);
326
+ timerStartRef = null;
327
+ }
328
+ } else {
329
+ if (!timeoutRef) {
330
+ const ms = Math.max(0, remainingTime ?? duration);
331
+ if (!Number.isFinite(ms)) {
332
+ // Do not set timeout for infinite duration
333
+ } else if (ms === 0) {
323
334
  handleClose();
324
- }, ms);
335
+ } else {
336
+ timerStartRef = Date.now();
337
+ timeoutRef = setTimeout(() => {
338
+ handleClose();
339
+ }, ms);
340
+ }
325
341
  }
326
342
  }
327
343
  }
328
344
  }
329
- }
345
+ });
330
346
 
331
- $: if (mounted && toastRef && !isSwiping && !swipeDismissDirection) {
332
- toastRef.style.setProperty("--swipe-translate-x", "0px");
333
- toastRef.style.setProperty("--swipe-translate-y", "0px");
334
- }
347
+ $effect(() => {
348
+ if (mounted && toastRef && !isSwiping && !swipeDismissDirection) {
349
+ toastRef.style.setProperty("--swipe-translate-x", "0px");
350
+ toastRef.style.setProperty("--swipe-translate-y", "0px");
351
+ }
352
+ });
335
353
 
336
- let swipeDirections: SwipeDirection[] = showClose
337
- ? getDefaultSwipeDirections(position)
338
- : [];
339
- $: swipeDirections = showClose ? getDefaultSwipeDirections(position) : [];
354
+ let swipeDirections = $derived(
355
+ showClose ? getDefaultSwipeDirections(position) : [],
356
+ );
340
357
 
341
358
  const handlePointerDown = (event: PointerEvent) => {
342
359
  if (!showClose) return;
@@ -501,66 +518,47 @@ const handlePointerCancel = (event: PointerEvent) => {
501
518
 
502
519
  const zIndexBase = Number(ANIMATION_CONFIG.Z_INDEX_BASE);
503
520
 
504
- let isTopPosition = false;
505
- let maxVisibleIndex = 0;
506
- let visibleIndex = 0;
507
- let defaultCollapsedOffset = 0;
508
- let resolvedCollapsedOffset = 0;
509
- let resolvedHiddenCollapsedOffset = 0;
510
- let scale = 1;
511
- let visibleScale = 1;
512
- let zIndex = zIndexBase;
513
- let stackHidden = false;
514
- let hiddenByStacking = false;
515
- let isStackLeader = false;
516
- let isLatest = false;
517
- type PositionConfig = (typeof POSITION_CONFIGS)[ToastPosition];
518
- let config: PositionConfig = POSITION_CONFIGS["bottom-center"];
519
-
520
- $: isTopPosition = position?.startsWith("top-") ?? false;
521
- $: maxVisibleIndex = Math.max(0, ANIMATION_CONFIG.MAX_VISIBLE_TOASTS - 1);
522
- $: visibleIndex = Math.min(index, maxVisibleIndex);
523
- $: defaultCollapsedOffset = isTopPosition
524
- ? index * ANIMATION_CONFIG.STACK_OFFSET
525
- : -(index * ANIMATION_CONFIG.STACK_OFFSET);
526
- $: resolvedCollapsedOffset =
521
+ let isTopPosition = $derived(position?.startsWith("top-") ?? false);
522
+ let maxVisibleIndex = $derived(
523
+ Math.max(0, ANIMATION_CONFIG.MAX_VISIBLE_TOASTS - 1),
524
+ );
525
+ let visibleIndex = $derived(Math.min(index, maxVisibleIndex));
526
+ let defaultCollapsedOffset = $derived(
527
+ isTopPosition
528
+ ? index * ANIMATION_CONFIG.STACK_OFFSET
529
+ : -(index * ANIMATION_CONFIG.STACK_OFFSET),
530
+ );
531
+ let resolvedCollapsedOffset = $derived(
527
532
  typeof collapsedOffset === "number" && Number.isFinite(collapsedOffset)
528
533
  ? collapsedOffset
529
- : defaultCollapsedOffset;
530
- $: resolvedHiddenCollapsedOffset =
534
+ : defaultCollapsedOffset,
535
+ );
536
+ let resolvedHiddenCollapsedOffset = $derived(
531
537
  typeof hiddenCollapsedOffset === "number" &&
532
- Number.isFinite(hiddenCollapsedOffset)
538
+ Number.isFinite(hiddenCollapsedOffset)
533
539
  ? hiddenCollapsedOffset
534
- : resolvedCollapsedOffset;
535
- $: scale = Math.max(
536
- ANIMATION_CONFIG.MIN_SCALE,
537
- 1 - index * ANIMATION_CONFIG.SCALE_FACTOR,
540
+ : resolvedCollapsedOffset,
538
541
  );
539
- $: visibleScale = Math.max(
540
- ANIMATION_CONFIG.MIN_SCALE,
541
- 1 - visibleIndex * ANIMATION_CONFIG.SCALE_FACTOR,
542
+ let scale = $derived(
543
+ Math.max(ANIMATION_CONFIG.MIN_SCALE, 1 - index * ANIMATION_CONFIG.SCALE_FACTOR),
542
544
  );
543
- $: zIndex = zIndexBase - renderIndex;
544
- $: stackHidden = index >= ANIMATION_CONFIG.MAX_VISIBLE_TOASTS;
545
- $: hiddenByStacking = stackHidden && animationState !== "exiting";
546
- $: isStackLeader = index === 0;
547
- $: isLatest = isStackLeader && !shouldClose;
548
- $: config = POSITION_CONFIGS[(position ?? "bottom-center") as ToastPosition];
549
-
550
- let transformStyle = {
551
- transform: "translate(0px, 0px)",
552
- opacity: 1,
553
- };
554
- let transitionDuration = `${ANIMATION_CONFIG.ENTER_DURATION}s`;
555
- let transitionTimingFunction = ANIMATION_CONFIG.EASING_DEFAULT;
556
- let canSwipe = showClose && swipeDirections.length > 0;
557
- let swipeCursorClass: string | undefined = undefined;
558
- let titleId: string | undefined = undefined;
559
- let descriptionId: string | undefined = undefined;
560
- let liveRole: "alert" | "status" = "status";
561
- let livePoliteness: "assertive" | "polite" = "polite";
562
-
563
- $: transformStyle = (() => {
545
+ let visibleScale = $derived(
546
+ Math.max(
547
+ ANIMATION_CONFIG.MIN_SCALE,
548
+ 1 - visibleIndex * ANIMATION_CONFIG.SCALE_FACTOR,
549
+ ),
550
+ );
551
+ let zIndex = $derived(zIndexBase - renderIndex);
552
+ let stackHidden = $derived(index >= ANIMATION_CONFIG.MAX_VISIBLE_TOASTS);
553
+ let hiddenByStacking = $derived(stackHidden && animationState !== "exiting");
554
+ let isStackLeader = $derived(index === 0);
555
+ let isLatest = $derived(isStackLeader && !shouldClose);
556
+ type PositionConfig = (typeof POSITION_CONFIGS)[ToastPosition];
557
+ let config = $derived(
558
+ POSITION_CONFIGS[(position ?? "bottom-center") as ToastPosition],
559
+ );
560
+
561
+ let transformStyle = $derived.by(() => {
564
562
  const baseOffsetY = stackHidden
565
563
  ? resolvedHiddenCollapsedOffset
566
564
  : resolvedCollapsedOffset;
@@ -654,9 +652,9 @@ $: transformStyle = (() => {
654
652
  transform,
655
653
  opacity: opacityValue,
656
654
  };
657
- })();
655
+ });
658
656
 
659
- $: transitionDuration = (() => {
657
+ let transitionDuration = $derived.by(() => {
660
658
  switch (animationState) {
661
659
  case "entering":
662
660
  case "entered":
@@ -666,24 +664,25 @@ $: transitionDuration = (() => {
666
664
  default:
667
665
  return `${ANIMATION_CONFIG.STACK_DURATION}s`;
668
666
  }
669
- })();
667
+ });
670
668
 
671
- $: transitionTimingFunction =
669
+ let transitionTimingFunction = $derived(
672
670
  animationState === "exiting"
673
671
  ? ANIMATION_CONFIG.EASING_EXIT
674
- : ANIMATION_CONFIG.EASING_DEFAULT;
672
+ : ANIMATION_CONFIG.EASING_DEFAULT,
673
+ );
675
674
 
676
- $: canSwipe = showClose && swipeDirections.length > 0;
677
- $: swipeCursorClass = canSwipe
678
- ? isSwiping
679
- ? "cursor-grabbing"
680
- : "cursor-grab"
681
- : undefined;
675
+ let canSwipe = $derived(showClose && swipeDirections.length > 0);
676
+ let swipeCursorClass = $derived(
677
+ canSwipe ? (isSwiping ? "cursor-grabbing" : "cursor-grab") : undefined,
678
+ );
682
679
 
683
- $: titleId = title ? `${id}-title` : undefined;
684
- $: descriptionId = description ? `${id}-desc` : undefined;
685
- $: liveRole = variant === "destructive" ? "alert" : "status";
686
- $: livePoliteness = variant === "destructive" ? "assertive" : "polite";
680
+ let titleId = $derived(title ? `${id}-title` : undefined);
681
+ let descriptionId = $derived(description ? `${id}-desc` : undefined);
682
+ let liveRole = $derived(variant === "destructive" ? "alert" : "status");
683
+ let livePoliteness = $derived(
684
+ (variant === "destructive" ? "assertive" : "polite") as "assertive" | "polite",
685
+ );
687
686
 
688
687
  const handleBlurCapture = (event: FocusEvent) => {
689
688
  const next = event.relatedTarget as Node | null;
@@ -720,7 +719,7 @@ const handleBlurCapture = (event: FocusEvent) => {
720
719
  data-toast-id={id}
721
720
  >
722
721
  <div
723
- role="alert"
722
+ role="presentation"
724
723
  class={cn(swipeCursorClass)}
725
724
  aria-busy={isLoading ? "true" : undefined}
726
725
  onpointerdown={handlePointerDown}
@@ -1,31 +1,27 @@
1
1
  import { type PositionedToast } from "./internals";
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- declare const VarselItem: $$__sveltets_2_IsomorphicComponent<{
2
+ type $$ComponentProps = {
3
+ /** The toast data object containing all content and state. */
16
4
  toast: PositionedToast;
5
+ /** Callback to remove the toast from the DOM after exit animation. */
17
6
  onRemove: (id: string) => void;
7
+ /** Whether the parent group (stack) is currently hovered. */
18
8
  isGroupHovered?: boolean;
9
+ /** The vertical offset for this toast when the stack is expanded. */
19
10
  expandedOffset?: number;
11
+ /** The gap between toasts when expanded. */
20
12
  expandedGap?: number;
21
- collapsedOffset?: number | undefined;
22
- hiddenCollapsedOffset?: number | undefined;
23
- onHeightChange?: ((id: string, height: number) => void) | undefined;
24
- onGroupHoverEnter?: (() => void) | undefined;
25
- onGroupHoldChange?: ((holding: boolean) => void) | undefined;
26
- }, {
27
- [evt: string]: CustomEvent<any>;
28
- }, {}, {}, string>;
29
- type VarselItem = InstanceType<typeof VarselItem>;
13
+ /** The vertical offset for this toast when the stack is collapsed. */
14
+ collapsedOffset?: number;
15
+ /** The offset to use when the toast is hidden in the stack. */
16
+ hiddenCollapsedOffset?: number;
17
+ /** Callback to report the height of the toast to the manager. */
18
+ onHeightChange?: (id: string, height: number) => void;
19
+ /** Callback to notify the manager that the user has entered the toast area. */
20
+ onGroupHoverEnter?: () => void;
21
+ /** Callback to notify the manager that the user is interacting (holding) the toast. */
22
+ onGroupHoldChange?: (holding: boolean) => void;
23
+ };
24
+ declare const VarselItem: import("svelte").Component<$$ComponentProps, {}, "">;
25
+ type VarselItem = ReturnType<typeof VarselItem>;
30
26
  export default VarselItem;
31
27
  //# sourceMappingURL=VarselItem.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"VarselItem.svelte.d.ts","sourceRoot":"","sources":["../src/lib/VarselItem.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAYN,KAAK,eAAe,EAIpB,MAAM,aAAa,CAAC;AA8wBrB,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,UAAU;WAX6U,eAAe;cAAY,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI;;;kBAAgG,MAAM;sBAAoB,MAAM,GAAG,SAAS;4BAA0B,MAAM,GAAG,SAAS;qBAAmB,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS;wBAAsB,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;wBAAwB,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GACxuB,SAAS;;;kBAUmF,CAAC;AAC9E,KAAK,UAAU,GAAG,YAAY,CAAC,OAAO,UAAU,CAAC,CAAC;AACpD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"VarselItem.svelte.d.ts","sourceRoot":"","sources":["../src/lib/VarselItem.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,EAYN,KAAK,eAAe,EAIpB,MAAM,aAAa,CAAC;AAQpB,KAAK,gBAAgB,GAAI;IACzB,8DAA8D;IAC9D,KAAK,EAAE,eAAe,CAAC;IACvB,sEAAsE;IACtE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C,CAAC;AA6uBF,QAAA,MAAM,UAAU,sDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}