better-toast 0.1.0 → 0.2.1

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.
@@ -1,6 +1,50 @@
1
- import { NgComponentOutlet } from '@angular/common';
2
1
  import * as i0 from '@angular/core';
3
- import { signal, Injectable, inject, ElementRef, input, output, computed, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, ViewContainerRef, input, effect, untracked, Directive, signal, Injectable, ElementRef, output, computed, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
3
+
4
+ /**
5
+ * Tiny dynamic-component host used for headless toasts, custom bodies, and icon overrides.
6
+ * Uses signal inputs and recreates the guest only when the component type changes.
7
+ */
8
+ class BetterToastOutlet {
9
+ viewContainer = inject(ViewContainerRef);
10
+ betterToastOutlet = input.required(/* @ts-ignore */
11
+ ...(ngDevMode ? [{ debugName: "betterToastOutlet" }] : /* istanbul ignore next */ []));
12
+ betterToastOutletInputs = input(/* @ts-ignore */
13
+ ...(ngDevMode ? [undefined, { debugName: "betterToastOutletInputs" }] : /* istanbul ignore next */ []));
14
+ ref;
15
+ currentType;
16
+ constructor() {
17
+ effect(() => {
18
+ const component = this.betterToastOutlet();
19
+ const inputs = this.betterToastOutletInputs();
20
+ untracked(() => this.sync(component, inputs));
21
+ });
22
+ }
23
+ sync(component, inputs) {
24
+ if (this.currentType !== component) {
25
+ this.viewContainer.clear();
26
+ this.ref = undefined;
27
+ this.currentType = component;
28
+ if (component) {
29
+ this.ref = this.viewContainer.createComponent(component);
30
+ }
31
+ }
32
+ if (!this.ref || !inputs) {
33
+ return;
34
+ }
35
+ for (const name of Object.keys(inputs)) {
36
+ this.ref.setInput(name, inputs[name]);
37
+ }
38
+ }
39
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BetterToastOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive });
40
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: BetterToastOutlet, isStandalone: true, selector: "[betterToastOutlet]", inputs: { betterToastOutlet: { classPropertyName: "betterToastOutlet", publicName: "betterToastOutlet", isSignal: true, isRequired: true, transformFunction: null }, betterToastOutletInputs: { classPropertyName: "betterToastOutletInputs", publicName: "betterToastOutletInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
41
+ }
42
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BetterToastOutlet, decorators: [{
43
+ type: Directive,
44
+ args: [{
45
+ selector: '[betterToastOutlet]',
46
+ }]
47
+ }], ctorParameters: () => [], propDecorators: { betterToastOutlet: [{ type: i0.Input, args: [{ isSignal: true, alias: "betterToastOutlet", required: true }] }], betterToastOutletInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "betterToastOutletInputs", required: false }] }] } });
4
48
 
5
49
  /** Fallback when `<app-toaster [duration]>` is absent and a helper omits `durationMs`. */
6
50
  const DEFAULT_TOAST_DURATION_MS = 4000;
@@ -23,6 +67,23 @@ function parseToasterDurationMs(value) {
23
67
  }
24
68
  return value;
25
69
  }
70
+ function newToastId() {
71
+ return globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
72
+ }
73
+ function chromeFromOptions(options) {
74
+ if (!options) {
75
+ return {};
76
+ }
77
+ const { icon, style, classNames, description, onDismiss, onAutoClose } = options;
78
+ return {
79
+ ...(icon !== undefined ? { icon } : {}),
80
+ ...(style !== undefined ? { style } : {}),
81
+ ...(classNames !== undefined ? { classNames } : {}),
82
+ ...(description !== undefined ? { description } : {}),
83
+ ...(onDismiss !== undefined ? { onDismiss } : {}),
84
+ ...(onAutoClose !== undefined ? { onAutoClose } : {}),
85
+ };
86
+ }
26
87
  function shouldScheduleAutoDismiss(durationMs) {
27
88
  return Number.isFinite(durationMs) && durationMs > 0;
28
89
  }
@@ -174,10 +235,10 @@ class ToasterService {
174
235
  * Show a toast whose **message area** is a standalone Angular component while keeping normal toast chrome
175
236
  * (surface, icon column when applicable, close button, stack motion).
176
237
  *
177
- * Pass {@link CustomToastOptions.inputs} for `input()` / `@Input()` on that component.
238
+ * Pass {@link CustomToastOptions.inputs} for `input()` on that component.
178
239
  * The component also receives **`toastId`** (matches the returned id) for programmatic dismiss.
179
240
  *
180
- * @param component The component class (must be usable with `NgComponentOutlet`).
241
+ * @param component The component class (must work with `ViewContainerRef.createComponent`).
181
242
  * @param options Optional configuration object {@link CustomToastOptions}.
182
243
  * @returns The toast ID, useful for programmatic dismissal.
183
244
  */
@@ -191,7 +252,7 @@ class ToasterService {
191
252
  * Pass {@link HeadlessToastOptions.inputs} to feed `input()` on that component.
192
253
  * The component also automatically receives **`toastId`** (matches the returned id) for programmatic dismiss inside the component.
193
254
  *
194
- * @param component The component class (must be usable with `NgComponentOutlet`).
255
+ * @param component The component class (must work with `ViewContainerRef.createComponent`).
195
256
  * @param options Optional configuration object {@link HeadlessToastOptions}.
196
257
  * @returns The toast ID, useful for programmatic dismissal.
197
258
  */
@@ -293,81 +354,48 @@ class ToasterService {
293
354
  toast.onDismiss?.();
294
355
  }
295
356
  }
357
+ pushToast(item, durationMs) {
358
+ this._toasts.update((list) => [...list, item]);
359
+ this.scheduleAutoDismiss(item.id, durationMs);
360
+ return item.id;
361
+ }
296
362
  add(message, variant, durationMs, options, toastAction) {
297
- const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
298
- const icon = options?.icon;
299
- const style = options?.style;
300
- const classNames = options?.classNames;
301
- const onDismiss = options?.onDismiss;
302
- const onAutoClose = options?.onAutoClose;
303
- const description = options?.description;
304
- const item = {
363
+ const id = newToastId();
364
+ return this.pushToast({
305
365
  id,
306
366
  message,
307
367
  variant,
308
- ...(icon !== undefined ? { icon } : {}),
309
- ...(style !== undefined ? { style } : {}),
310
- ...(classNames !== undefined ? { classNames } : {}),
311
- ...(description !== undefined ? { description } : {}),
312
- ...(onDismiss !== undefined ? { onDismiss } : {}),
313
- ...(onAutoClose !== undefined ? { onAutoClose } : {}),
368
+ ...chromeFromOptions(options),
314
369
  ...(toastAction !== undefined ? { toastAction } : {}),
315
- };
316
- this._toasts.update((list) => [...list, item]);
317
- this.scheduleAutoDismiss(id, durationMs);
318
- return id;
370
+ }, durationMs);
319
371
  }
320
372
  addContentComponent(component, variant, durationMs, options) {
321
- const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
322
- const componentInputs = {
323
- ...(options?.inputs ?? {}),
324
- toastId: id,
325
- };
326
- const icon = options?.icon;
327
- const style = options?.style;
328
- const classNames = options?.classNames;
329
- const onDismiss = options?.onDismiss;
330
- const onAutoClose = options?.onAutoClose;
331
- const description = options?.description;
332
- const item = {
373
+ const id = newToastId();
374
+ return this.pushToast({
333
375
  id,
334
376
  message: '',
335
377
  variant,
336
378
  contentComponent: component,
337
- contentComponentInputs: componentInputs,
338
- ...(icon !== undefined ? { icon } : {}),
339
- ...(style !== undefined ? { style } : {}),
340
- ...(classNames !== undefined ? { classNames } : {}),
341
- ...(description !== undefined ? { description } : {}),
342
- ...(onDismiss !== undefined ? { onDismiss } : {}),
343
- ...(onAutoClose !== undefined ? { onAutoClose } : {}),
344
- };
345
- this._toasts.update((list) => [...list, item]);
346
- this.scheduleAutoDismiss(id, durationMs);
347
- return id;
379
+ contentComponentInputs: { ...(options?.inputs ?? {}), toastId: id },
380
+ ...chromeFromOptions(options),
381
+ }, durationMs);
348
382
  }
349
383
  addComponent(component, durationMs, options) {
350
- const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
351
- const componentInputs = {
352
- ...(options?.inputs ?? {}),
353
- toastId: id,
354
- };
355
- const classNames = options?.classNames;
356
- const onDismiss = options?.onDismiss;
357
- const onAutoClose = options?.onAutoClose;
358
- const item = {
384
+ const id = newToastId();
385
+ return this.pushToast({
359
386
  id,
360
387
  message: '',
361
388
  variant: 'default',
362
389
  component,
363
- componentInputs,
364
- ...(classNames !== undefined ? { classNames } : {}),
365
- ...(onDismiss !== undefined ? { onDismiss } : {}),
366
- ...(onAutoClose !== undefined ? { onAutoClose } : {}),
367
- };
368
- this._toasts.update((list) => [...list, item]);
369
- this.scheduleAutoDismiss(id, durationMs);
370
- return id;
390
+ componentInputs: { ...(options?.inputs ?? {}), toastId: id },
391
+ ...chromeFromOptions(options
392
+ ? {
393
+ classNames: options.classNames,
394
+ onDismiss: options.onDismiss,
395
+ onAutoClose: options.onAutoClose,
396
+ }
397
+ : undefined),
398
+ }, durationMs);
371
399
  }
372
400
  updateToast(id, message, variant, durationMs) {
373
401
  let found = false;
@@ -427,9 +455,31 @@ const TOAST_VARIANTS = [
427
455
  ];
428
456
 
429
457
  const GAP = 16;
458
+ /**
459
+ * Visible layers in the collapsed stack. Older toasts stay in the list and appear on hover.
460
+ * CSS hides the rest with `:nth-last-child(n + 4)` — keep that selector in sync.
461
+ */
462
+ const VISIBLE_TOASTS = 3;
430
463
  function swipeDirectionForPosition(position) {
431
464
  return position.startsWith('bottom') ? 'down' : 'up';
432
465
  }
466
+ /** True when the event target is a toast row (not the list itself, which can steal focus on dismiss). */
467
+ function isInsideToastItem(target) {
468
+ if (target instanceof Element) {
469
+ return target.closest('.toast') != null;
470
+ }
471
+ if (target instanceof Node && target.parentElement) {
472
+ return target.parentElement.closest('.toast') != null;
473
+ }
474
+ return false;
475
+ }
476
+ /**
477
+ * Touch and pen pointers do not hover. `pointerleave` fires on lift, which would collapse
478
+ * the stack before a second tap can reach a toast behind the front card.
479
+ */
480
+ function isNonHoverPointer(event) {
481
+ return event.pointerType === 'touch' || event.pointerType === 'pen';
482
+ }
433
483
  function resolveToasterOffsetSide(offset, side) {
434
484
  if (offset == null) {
435
485
  return undefined;
@@ -457,6 +507,16 @@ function mergeToastClassNames(base, override) {
457
507
  return base;
458
508
  return { ...base, ...override };
459
509
  }
510
+ /** Newest measured height in the stack. Skips the front toast until it has been measured. */
511
+ function resolveFrontToastHeightPx(toasts, heights) {
512
+ for (let i = toasts.length - 1; i >= 0; i--) {
513
+ const height = heights[toasts[i].id];
514
+ if (height) {
515
+ return height;
516
+ }
517
+ }
518
+ return undefined;
519
+ }
460
520
  class BetterToastItem {
461
521
  /** Shared toaster service (e.g. dismiss from the close button). */
462
522
  toaster = inject(ToasterService);
@@ -480,9 +540,6 @@ class BetterToastItem {
480
540
  /** Which icon and color treatment to show for this row. */
481
541
  variant = input('default', /* @ts-ignore */
482
542
  ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
483
- /** Vertical stack offset in px; bound to `--offset` on the host for layout. */
484
- offset = input.required(/* @ts-ignore */
485
- ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
486
543
  /** When false, the dismiss control is not rendered (toasts may still auto-dismiss or be cleared via the service). */
487
544
  closeButton = input(true, /* @ts-ignore */
488
545
  ...(ngDevMode ? [{ debugName: "closeButton" }] : /* istanbul ignore next */ []));
@@ -501,6 +558,14 @@ class BetterToastItem {
501
558
  /** Stack anchor from `<better-toaster [position]>` — drives swipe axis and `data-swipe-direction`. */
502
559
  stackPosition = input('bottom-right', /* @ts-ignore */
503
560
  ...(ngDevMode ? [{ debugName: "stackPosition" }] : /* istanbul ignore next */ []));
561
+ /** True while the stack is hovered or focus is inside it (expanded layout). */
562
+ stackExpanded = input(false, /* @ts-ignore */
563
+ ...(ngDevMode ? [{ debugName: "stackExpanded" }] : /* istanbul ignore next */ []));
564
+ /** True when this toast is the newest (front) layer in the stack. */
565
+ stackFront = input(false, /* @ts-ignore */
566
+ ...(ngDevMode ? [{ debugName: "stackFront" }] : /* istanbul ignore next */ []));
567
+ /** Tells the parent whether the pointer is still over the toast stack. */
568
+ stackHover = output();
504
569
  /**
505
570
  * Color palette from `<better-toaster [theme]>`; mirrored on the host as `data-theme`
506
571
  * so item-scoped CSS can react to the chosen mode without `:host-context()`.
@@ -509,6 +574,15 @@ class BetterToastItem {
509
574
  ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
510
575
  /** Emits the measured host height in px once after the first render so the parent can stack siblings. */
511
576
  heightChange = output();
577
+ /** Natural host height in px, captured once after first paint (used to restore height on expand). */
578
+ initialHeight = signal(undefined, /* @ts-ignore */
579
+ ...(ngDevMode ? [{ debugName: "initialHeight" }] : /* istanbul ignore next */ []));
580
+ /** Bound as `--initial-height` so collapsed layers can return to their own size. */
581
+ initialHeightCss = computed(() => {
582
+ const height = this.initialHeight();
583
+ return height ? `${height}px` : null;
584
+ }, /* @ts-ignore */
585
+ ...(ngDevMode ? [{ debugName: "initialHeightCss" }] : /* istanbul ignore next */ []));
512
586
  /**
513
587
  * Stacked title + optional secondary line: {@link ToastVariant} **`description`**, or any variant with
514
588
  * non-empty {@link ToasterItem.description} ({@link ToastOptions.description} on `show` / `success` / etc.).
@@ -569,12 +643,6 @@ class BetterToastItem {
569
643
  return true;
570
644
  }, /* @ts-ignore */
571
645
  ...(ngDevMode ? [{ debugName: "shouldShowIconColumn" }] : /* istanbul ignore next */ []));
572
- /** Bound to {@link ToasterItem.componentInputs} for headless (`NgComponentOutlet`) toasts. */
573
- componentOutletInputs = computed(() => this.toast()?.componentInputs ?? {}, /* @ts-ignore */
574
- ...(ngDevMode ? [{ debugName: "componentOutletInputs" }] : /* istanbul ignore next */ []));
575
- /** Bound to {@link ToasterItem.contentComponentInputs} for {@link ToasterService.custom} body components. */
576
- contentComponentOutletInputs = computed(() => this.toast()?.contentComponentInputs ?? {}, /* @ts-ignore */
577
- ...(ngDevMode ? [{ debugName: "contentComponentOutletInputs" }] : /* istanbul ignore next */ []));
578
646
  /** When true, host uses no default toast chrome (border, padding, surface) — only stack + motion. */
579
647
  isHeadless = computed(() => this.toast()?.component != null, /* @ts-ignore */
580
648
  ...(ngDevMode ? [{ debugName: "isHeadless" }] : /* istanbul ignore next */ []));
@@ -587,6 +655,7 @@ class BetterToastItem {
587
655
  constructor() {
588
656
  afterNextRender(() => {
589
657
  const height = this.host.nativeElement.offsetHeight;
658
+ this.initialHeight.set(height);
590
659
  this.heightChange.emit(height);
591
660
  });
592
661
  }
@@ -596,10 +665,9 @@ class BetterToastItem {
596
665
  tracking = false;
597
666
  startY = 0;
598
667
  pointerId = -1;
599
- dragStartThreshold = 0;
600
668
  swipeCloseThreshold = 30;
601
669
  /** Transform applied when swipe-dismiss completes (matches leave direction / headless centering). */
602
- swipeDismissTransform = computed(() => {
670
+ swipeDismissTransform() {
603
671
  const pos = this.stackPosition();
604
672
  const down = this.swipeDirection() === 'down';
605
673
  const y = down ? '130%' : '-130%';
@@ -607,8 +675,7 @@ class BetterToastItem {
607
675
  return `translateX(-50%) translateY(${y})`;
608
676
  }
609
677
  return `translateY(${y})`;
610
- }, /* @ts-ignore */
611
- ...(ngDevMode ? [{ debugName: "swipeDismissTransform" }] : /* istanbul ignore next */ []));
678
+ }
612
679
  /** Prevents swipe-to-dismiss from starting when pressing the row action / cancel control. */
613
680
  onRowButtonPointerDown(event) {
614
681
  event.stopPropagation();
@@ -628,15 +695,26 @@ class BetterToastItem {
628
695
  * Pauses auto-dismiss when the pointer enters the toast.
629
696
  */
630
697
  onPointerEnter() {
698
+ this.stackHover.emit(true);
631
699
  const id = this.toast()?.id;
632
700
  if (id) {
633
701
  this.toaster.pauseAutoDismiss(id);
634
702
  }
635
703
  }
636
704
  /**
637
- * Resumes auto-dismiss when the pointer leaves the toast.
705
+ * Resumes auto-dismiss when a hover pointer leaves the toast stack.
706
+ * Moving onto a sibling toast keeps the stack expanded.
707
+ * Touch/pen leave is ignored: the stack stays expanded until a tap outside.
638
708
  */
639
- onPointerLeave() {
709
+ onPointerLeave(event) {
710
+ if (isNonHoverPointer(event)) {
711
+ return;
712
+ }
713
+ const stillInStack = isInsideToastItem(event.relatedTarget);
714
+ this.stackHover.emit(stillInStack);
715
+ if (stillInStack || this.stackExpanded()) {
716
+ return;
717
+ }
640
718
  const id = this.toast()?.id;
641
719
  if (id) {
642
720
  this.toaster.resumeAutoDismiss(id);
@@ -666,13 +744,7 @@ class BetterToastItem {
666
744
  const down = this.swipeDirection() === 'down';
667
745
  const dragDy = down ? Math.max(0, rawDy) : Math.min(0, rawDy);
668
746
  if (!this.isDragging()) {
669
- const passed = this.dragStartThreshold > 0
670
- ? down
671
- ? rawDy >= this.dragStartThreshold
672
- : rawDy <= -this.dragStartThreshold
673
- : down
674
- ? rawDy > 0
675
- : rawDy < 0;
747
+ const passed = down ? rawDy > 0 : rawDy < 0;
676
748
  if (passed) {
677
749
  this.isDragging.set(true);
678
750
  el.setPointerCapture(this.pointerId);
@@ -733,97 +805,52 @@ class BetterToastItem {
733
805
  }
734
806
  }
735
807
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BetterToastItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
736
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: BetterToastItem, isStandalone: true, selector: "li[betterToastItem]", inputs: { toast: { classPropertyName: "toast", publicName: "toast", isSignal: true, isRequired: false, transformFunction: null }, toasterStyle: { classPropertyName: "toasterStyle", publicName: "toasterStyle", isSignal: true, isRequired: false, transformFunction: null }, toasterClassNames: { classPropertyName: "toasterClassNames", publicName: "toasterClassNames", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: true, transformFunction: null }, closeButton: { classPropertyName: "closeButton", publicName: "closeButton", isSignal: true, isRequired: false, transformFunction: null }, customIcons: { classPropertyName: "customIcons", publicName: "customIcons", isSignal: true, isRequired: false, transformFunction: null }, dismissButtonAriaLabel: { classPropertyName: "dismissButtonAriaLabel", publicName: "dismissButtonAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, stackPosition: { classPropertyName: "stackPosition", publicName: "stackPosition", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { heightChange: "heightChange" }, host: { attributes: { "role": "listitem", "tabindex": "0" }, listeners: { "pointerdown": "onPointerDown($event)", "pointermove": "onPointerMove($event)", "pointerup": "onPointerUp()", "pointercancel": "onPointerCancel()", "pointerenter": "onPointerEnter()", "pointerleave": "onPointerLeave()" }, properties: { "class": "resolvedClassNames()?.toast", "attr.data-variant": "variant()", "attr.data-icon": "shouldShowIconColumn() ? \"true\" : \"false\"", "attr.data-headless": "isHeadless() ? \"true\" : null", "attr.data-swipe-direction": "swipeDirection()", "attr.data-theme": "theme()", "style.--offset": "offset() + \"px\"", "style": "isHeadless() ? undefined : hostStyle()", "animate.leave": "\"leave\"" }, classAttribute: "toast" }, ngImport: i0, template: `
808
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: BetterToastItem, isStandalone: true, selector: "li[betterToastItem]", inputs: { toast: { classPropertyName: "toast", publicName: "toast", isSignal: true, isRequired: false, transformFunction: null }, toasterStyle: { classPropertyName: "toasterStyle", publicName: "toasterStyle", isSignal: true, isRequired: false, transformFunction: null }, toasterClassNames: { classPropertyName: "toasterClassNames", publicName: "toasterClassNames", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, closeButton: { classPropertyName: "closeButton", publicName: "closeButton", isSignal: true, isRequired: false, transformFunction: null }, customIcons: { classPropertyName: "customIcons", publicName: "customIcons", isSignal: true, isRequired: false, transformFunction: null }, dismissButtonAriaLabel: { classPropertyName: "dismissButtonAriaLabel", publicName: "dismissButtonAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, stackPosition: { classPropertyName: "stackPosition", publicName: "stackPosition", isSignal: true, isRequired: false, transformFunction: null }, stackExpanded: { classPropertyName: "stackExpanded", publicName: "stackExpanded", isSignal: true, isRequired: false, transformFunction: null }, stackFront: { classPropertyName: "stackFront", publicName: "stackFront", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stackHover: "stackHover", heightChange: "heightChange" }, host: { attributes: { "role": "listitem", "tabindex": "0" }, listeners: { "pointerdown": "onPointerDown($event)", "pointermove": "onPointerMove($event)", "pointerup": "onPointerUp()", "pointercancel": "onPointerCancel()", "pointerenter": "onPointerEnter()", "pointerleave": "onPointerLeave($event)" }, properties: { "class": "resolvedClassNames()?.toast", "attr.data-variant": "variant()", "attr.data-icon": "shouldShowIconColumn() ? \"true\" : \"false\"", "attr.data-headless": "isHeadless() ? \"true\" : null", "attr.data-swipe-direction": "swipeDirection()", "attr.data-theme": "theme()", "style.--initial-height": "initialHeightCss()", "style": "isHeadless() ? undefined : hostStyle()", "animate.leave": "\"leave\"" }, classAttribute: "toast" }, ngImport: i0, template: `
737
809
  @if (toast()?.component) {
738
- <ng-container *ngComponentOutlet="toast()!.component!; inputs: componentOutletInputs()" />
810
+ <ng-container
811
+ [betterToastOutlet]="toast()!.component!"
812
+ [betterToastOutletInputs]="toast()?.componentInputs"
813
+ />
739
814
  } @else {
740
815
  @if (shouldShowIconColumn()) {
741
816
  <span class="toast-icon" aria-hidden="true">
742
817
  @if (toast()?.icon) {
743
- <ng-container *ngComponentOutlet="toast()!.icon!" />
818
+ <ng-container [betterToastOutlet]="toast()!.icon!" />
744
819
  } @else if (iconComponent(); as IconCmp) {
745
- <ng-container *ngComponentOutlet="IconCmp" />
820
+ <ng-container [betterToastOutlet]="IconCmp" />
821
+ } @else if (variant() === 'loading') {
822
+ <div class="toast-icon-loading"></div>
746
823
  } @else {
747
- @switch (variant()) {
748
- @case ('success') {
749
- <svg
750
- xmlns="http://www.w3.org/2000/svg"
751
- fill="none"
752
- viewBox="0 0 24 24"
753
- aria-hidden="true"
754
- >
755
- <circle
756
- cx="12"
757
- cy="12"
758
- r="9"
759
- stroke="currentColor"
760
- stroke-linecap="round"
761
- stroke-linejoin="round"
762
- stroke-width="1.75"
763
- />
764
- <path
765
- stroke="currentColor"
766
- stroke-linecap="round"
767
- stroke-linejoin="round"
768
- stroke-width="1.75"
769
- d="M8.48 12.22 10.9 14.64 15.74 9.14"
770
- />
771
- </svg>
772
- }
773
- @case ('error') {
774
- <svg
775
- aria-hidden="true"
776
- xmlns="http://www.w3.org/2000/svg"
777
- viewBox="0 0 24 24"
778
- fill="none"
779
- stroke="currentColor"
780
- stroke-width="2"
781
- stroke-linecap="round"
782
- stroke-linejoin="round"
783
- >
824
+ <svg
825
+ fill="none"
826
+ viewBox="0 0 24 24"
827
+ stroke="currentColor"
828
+ stroke-linecap="round"
829
+ stroke-linejoin="round"
830
+ [attr.stroke-width]="variant() === 'error' ? 2 : 1.75"
831
+ >
832
+ @switch (variant()) {
833
+ @case ('success') {
834
+ <circle cx="12" cy="12" r="9" />
835
+ <path d="M8.48 12.22 10.9 14.64 15.74 9.14" />
836
+ }
837
+ @case ('error') {
784
838
  <circle cx="12" cy="12" r="10" />
785
839
  <line x1="9" y1="9" x2="15" y2="15" />
786
840
  <line x1="15" y1="9" x2="9" y2="15" />
787
- </svg>
788
- }
789
- @case ('info') {
790
- <svg
791
- aria-hidden="true"
792
- xmlns="http://www.w3.org/2000/svg"
793
- viewBox="0 0 24 24"
794
- fill="none"
795
- stroke="currentColor"
796
- stroke-width="1.75"
797
- stroke-linecap="round"
798
- stroke-linejoin="round"
799
- >
841
+ }
842
+ @case ('info') {
800
843
  <circle cx="12" cy="12" r="10" />
801
844
  <line x1="12" y1="16" x2="12" y2="12" />
802
845
  <line x1="12" y1="8" x2="12.01" y2="8" />
803
- </svg>
804
- }
805
- @case ('warning') {
806
- <svg
807
- aria-hidden="true"
808
- xmlns="http://www.w3.org/2000/svg"
809
- fill="none"
810
- viewBox="0 0 24 24"
811
- >
846
+ }
847
+ @case ('warning') {
812
848
  <path
813
- stroke="currentColor"
814
- stroke-linecap="round"
815
- stroke-linejoin="round"
816
- stroke-width="1.75"
817
849
  d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
818
850
  />
819
- </svg>
820
- }
821
- @case ('loading') {
822
- <div class="toast-icon-loading" aria-hidden="true"></div>
851
+ }
823
852
  }
824
- @case ('description') {}
825
- @case ('default') {}
826
- }
853
+ </svg>
827
854
  }
828
855
  </span>
829
856
  }
@@ -833,10 +860,8 @@ class BetterToastItem {
833
860
  @if (toast()?.contentComponent) {
834
861
  <div class="msg" [class]="resolvedClassNames()?.message">
835
862
  <ng-container
836
- *ngComponentOutlet="
837
- toast()!.contentComponent!;
838
- inputs: contentComponentOutletInputs()
839
- "
863
+ [betterToastOutlet]="toast()!.contentComponent!"
864
+ [betterToastOutletInputs]="toast()?.contentComponentInputs"
840
865
  />
841
866
  </div>
842
867
  } @else {
@@ -852,10 +877,8 @@ class BetterToastItem {
852
877
  @if (toast()?.contentComponent) {
853
878
  <div class="msg" [class]="resolvedClassNames()?.message">
854
879
  <ng-container
855
- *ngComponentOutlet="
856
- toast()!.contentComponent!;
857
- inputs: contentComponentOutletInputs()
858
- "
880
+ [betterToastOutlet]="toast()!.contentComponent!"
881
+ [betterToastOutletInputs]="toast()?.contentComponentInputs"
859
882
  />
860
883
  </div>
861
884
  } @else {
@@ -864,59 +887,53 @@ class BetterToastItem {
864
887
  }
865
888
 
866
889
  @if (toast()?.toastAction; as rowAction) {
867
- @switch (rowAction.role) {
868
- @case ('action') {
869
- <button
870
- type="button"
871
- class="toast-row-btn action-btn"
872
- [class]="resolvedClassNames()?.actionButton"
873
- [attr.data-row-btn]="rowAction.role"
874
- (pointerdown)="onRowButtonPointerDown($event)"
875
- (click)="onToastRowClick($event)"
876
- >
877
- {{ rowAction.label }}
878
- </button>
879
- }
880
- @case ('cancel') {
881
- <button
882
- type="button"
883
- class="toast-row-btn cancel-btn"
884
- [class]="resolvedClassNames()?.cancelButton"
885
- [attr.data-row-btn]="rowAction.role"
886
- (pointerdown)="onRowButtonPointerDown($event)"
887
- (click)="onToastRowClick($event)"
888
- >
889
- {{ rowAction.label }}
890
- </button>
891
- }
892
- }
890
+ <button
891
+ type="button"
892
+ class="toast-row-btn"
893
+ [class.action-btn]="rowAction.role === 'action'"
894
+ [class.cancel-btn]="rowAction.role === 'cancel'"
895
+ [class]="
896
+ rowAction.role === 'action'
897
+ ? resolvedClassNames()?.actionButton
898
+ : resolvedClassNames()?.cancelButton
899
+ "
900
+ [attr.data-row-btn]="rowAction.role"
901
+ (pointerdown)="onRowButtonPointerDown($event)"
902
+ (click)="onToastRowClick($event)"
903
+ >
904
+ {{ rowAction.label }}
905
+ </button>
893
906
  }
894
907
  }
895
908
 
896
- @if (closeButton() && !isHeadless()) {
909
+ @if (closeButton() && !isHeadless() && (stackFront() || stackExpanded())) {
897
910
  <button
898
911
  type="button"
899
912
  class="close-btn"
900
913
  [class]="resolvedClassNames()?.closeButton"
914
+ [animate.enter]="stackFront() ? null : 'close-enter'"
915
+ [animate.leave]="stackFront() ? null : 'close-leave'"
901
916
  (click)="toaster.dismiss(toast()?.id ?? '')"
902
917
  [attr.aria-label]="dismissButtonAriaLabel()"
903
918
  >
904
- <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
905
- <path
906
- stroke="currentColor"
907
- stroke-linecap="round"
908
- stroke-linejoin="round"
909
- stroke-width="1.75"
910
- d="M6 18 18 6M6 6l12 12"
911
- />
919
+ <svg
920
+ fill="none"
921
+ viewBox="0 0 24 24"
922
+ stroke="currentColor"
923
+ stroke-linecap="round"
924
+ stroke-linejoin="round"
925
+ stroke-width="1.75"
926
+ aria-hidden="true"
927
+ >
928
+ <path d="M6 18 18 6M6 6l12 12" />
912
929
  </svg>
913
930
  </button>
914
931
  }
915
- `, isInline: true, styles: [":host{display:flex;align-items:center;gap:.375rem;flex:1;min-width:0;width:100%;padding-inline:.75rem;padding-block:1rem;box-sizing:border-box;border:1px solid var(--toast-border-color);border-radius:.5rem;box-shadow:var(--toast-shadow);background:var(--toast-bg);color:var(--toast-color);--toast-line-height: 1.35;font:500 .875rem/var(--toast-line-height) system-ui,sans-serif;pointer-events:auto;touch-action:none;-webkit-user-select:none;user-select:none;position:absolute;opacity:1;scale:1;transition:opacity .4s ease,transform .4s ease,scale .4s ease,box-shadow .2s ease}:host([data-variant=\"loading\"]){touch-action:auto}@media(width>=40rem){:host{width:var(--toast-width)}}:host([data-position^=\"bottom\"]){bottom:0;transform:translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-position^=\"bottom\"]){opacity:0;transform:translateY(100%)}}:host([data-position^=\"top\"]){top:0;transform:translateY(calc(1 * var(--offset, 0px)))}@starting-style{:host([data-position^=\"top\"]){opacity:0;transform:translateY(-100%)}}@starting-style{:host([data-headless=\"true\"][data-position^=\"bottom\"]){opacity:1}}@starting-style{:host([data-headless=\"true\"][data-position^=\"top\"]){opacity:1}}:host([data-variant=\"default\"]) .toast-icon svg,:host([data-variant=\"default\"]) .toast-icon ::ng-deep svg,:host([data-variant=\"description\"]) .toast-icon svg,:host([data-variant=\"description\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"success\"]){background:var(--toast-success-bg);color:var(--toast-success-text);border-color:var(--toast-success-border)}:host([data-rich-colors=\"false\"][data-variant=\"success\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"success\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"success\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"success\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-success-text),var(--toast-success-bg));stroke:light-dark(var(--toast-success-bg),var(--toast-success-text));color:light-dark(var(--toast-success-bg),var(--toast-success-text))}:host([data-rich-colors=\"true\"][data-variant=\"error\"]){background:var(--toast-error-bg);color:var(--toast-error-text);border-color:var(--toast-error-border)}:host([data-rich-colors=\"false\"][data-variant=\"error\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"error\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"error\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"error\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-error-text),var(--toast-error-bg));stroke:light-dark(var(--toast-error-bg),var(--toast-error-text));color:light-dark(var(--toast-error-bg),var(--toast-error-text))}:host([data-rich-colors=\"true\"][data-variant=\"info\"]){background:var(--toast-info-bg);color:var(--toast-info-text);border-color:var(--toast-info-border)}:host([data-rich-colors=\"false\"][data-variant=\"info\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"info\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"info\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"info\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-info-text),var(--toast-info-bg));stroke:light-dark(var(--toast-info-bg),var(--toast-info-text));color:light-dark(var(--toast-info-bg),var(--toast-info-text))}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]){background:var(--toast-warning-bg);color:var(--toast-warning-text);border-color:var(--toast-warning-border)}:host([data-rich-colors=\"false\"][data-variant=\"warning\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"warning\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"warning\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-warning-text),var(--toast-warning-bg));stroke:light-dark(var(--toast-warning-bg),var(--toast-warning-text));color:light-dark(var(--toast-warning-bg),var(--toast-warning-text))}:host([data-headless=\"true\"]){width:max-content;max-width:calc(100dvw - var(--toast-offset-mobile-left, 16px) - var(--toast-offset-mobile-right, 16px));padding:0;gap:0;border:none;border-radius:0;box-shadow:none;background:transparent;color:inherit;font:inherit;line-height:normal;--toast-line-height: inherit}@media(width>=40rem){:host([data-headless=\"true\"]){max-width:calc(100dvw - var(--toast-offset-left, 24px) - var(--toast-offset-right, 24px))}}@media(width>=40rem){:host([data-headless=\"true\"][data-position$=\"-right\"]){left:auto;right:0}:host([data-headless=\"true\"][data-position$=\"-left\"]){left:0;right:auto}}:host([data-headless=\"true\"][data-position=\"bottom-center\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position=\"bottom-center\"]){opacity:1;transform:translate(-50%) translateY(100%)}}:host([data-headless=\"true\"][data-position=\"top-center\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position=\"top-center\"]){opacity:1;transform:translate(-50%) translateY(-100%)}}@media(width<40rem){:host([data-headless=\"true\"][data-position^=\"bottom\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position^=\"bottom\"]){opacity:1;transform:translate(-50%) translateY(100%)}}:host([data-headless=\"true\"][data-position^=\"top\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position^=\"top\"]){opacity:1;transform:translate(-50%) translateY(-100%)}}:host([data-headless=\"true\"][data-swipe-direction=\"down\"].leave){transform:translate(-50%) translateY(100%)}:host([data-headless=\"true\"][data-swipe-direction=\"up\"].leave){transform:translate(-50%) translateY(-100%)}}:host([data-headless=\"true\"][data-rich-colors=\"true\"]){background:transparent;color:inherit;border-color:transparent}:host([data-headless=\"true\"][data-icon=\"false\"]){padding-left:0}:host([data-icon=\"false\"]){padding-left:.925rem}.toast-icon,.toast-icon ::ng-deep{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.425rem;height:1.425rem}.toast-icon svg,.toast-icon ::ng-deep svg{display:block;margin-inline:auto;fill:light-dark(var(--toast-color),var(--toast-bg));stroke:light-dark(var(--toast-bg),var(--toast-color));width:95%;height:95%}:host([data-theme=\"dark\"]) .toast-icon svg,:host([data-theme=\"dark\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}@media(prefers-color-scheme:dark){:host([data-theme=\"system\"]) .toast-icon svg,:host([data-theme=\"system\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}}.toast-icon-loading{display:block;width:.9rem;height:.9rem;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;border-right-color:transparent;animation:spin .45s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.msg{flex:1;min-width:0;max-width:95%;margin:0;font-size:.825rem;font-weight:500;line-height:calc(1.25 / .875);letter-spacing:.01em}@media(width<40rem){.msg{max-width:90%;font-size:.8rem}}.stack{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;flex:1;min-width:0}.stack .msg{flex:0 1 auto;max-width:100%}:host([data-icon=\"false\"]) .stack .msg{max-width:90%}.description{margin:0;font-size:.825rem;font-weight:400;line-height:1.35;letter-spacing:.01em;opacity:.9;max-width:100%}@media(width<40rem){.description{font-size:.8rem}}.toast-row-btn{flex-shrink:0;margin:0;margin-inline-start:auto;padding:.35rem .65rem;border-radius:.375rem;border:1px solid transparent;font:inherit;font-size:.8rem;font-weight:600;letter-spacing:.01em;line-height:1.2;cursor:pointer;white-space:nowrap;transition:transform .2s ease}.toast-row-btn:focus-visible{outline:2px solid var(--toast-color);outline-offset:2px}.toast-row-btn:active{transform:scale(.95)}@media(prefers-reduced-motion:reduce){.toast-row-btn:active{transform:none}}.action-btn{background:var(--toast-action-bg);color:var(--toast-action-text);border-color:var(--toast-action-border)}.cancel-btn{background:var(--toast-cancel-bg);color:var(--toast-color);border-color:var(--toast-cancel-border)}.toast-custom{flex:1;min-width:0;width:100%;font-size:.825rem;font-weight:400;line-height:calc(1.25 / .875)}.toast-custom :first-child{margin-top:0}.toast-custom :last-child{margin-bottom:0}.close-btn{position:absolute;top:-.3875rem;right:-.3875rem;flex-shrink:0;margin:-.15rem -.25rem -.15rem 0;border:1px solid var(--toast-border-color);box-shadow:var(--toast-shadow);background:var(--toast-bg);color:var(--toast-color);cursor:pointer;line-height:1;padding:.25rem;border-radius:50%;display:inline-flex;align-items:center;justify-content:center}:host([data-rich-colors=\"true\"][data-variant=\"success\"]) .close-btn{border-color:var(--toast-success-border);background:var(--toast-success-bg);color:var(--toast-success-text)}:host([data-rich-colors=\"true\"][data-variant=\"error\"]) .close-btn{border-color:var(--toast-error-border);background:var(--toast-error-bg);color:var(--toast-error-text)}:host([data-rich-colors=\"true\"][data-variant=\"info\"]) .close-btn{border-color:var(--toast-info-border);background:var(--toast-info-bg);color:var(--toast-info-text)}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]) .close-btn{border-color:var(--toast-warning-border);background:var(--toast-warning-bg);color:var(--toast-warning-text)}.close-btn:focus-visible{outline:2px solid currentColor}.close-btn svg{display:block;width:.825rem;height:.825rem}:host(.leave){opacity:0;scale:.95}:host([data-swipe-direction=\"down\"].leave){transform:translateY(100%)}:host([data-headless=\"true\"][data-position=\"bottom-center\"].leave){transform:translate(-50%) translateY(100%)}:host([data-swipe-direction=\"up\"].leave){transform:translateY(-100%)}:host([data-headless=\"true\"][data-position=\"top-center\"].leave){transform:translate(-50%) translateY(-100%)}@media(prefers-reduced-motion:reduce){:host{transition:none}:host(.leave){scale:1}@starting-style{:host([data-position^=\"bottom\"]){opacity:0;transform:translateY(calc(-1 * var(--offset, 0px)))}}@starting-style{:host([data-position^=\"top\"]){opacity:0;transform:translateY(calc(1 * var(--offset, 0px)))}}@starting-style{:host([data-headless=\"true\"][data-position=\"bottom-center\"]){opacity:1;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}}@starting-style{:host([data-headless=\"true\"][data-position=\"top-center\"]){opacity:1;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}}:host([data-swipe-direction=\"down\"].leave){transform:translateY(calc(-1 * var(--offset, 0px)))}:host([data-swipe-direction=\"up\"].leave){transform:translateY(calc(1 * var(--offset, 0px)))}:host([data-headless=\"true\"][data-position=\"bottom-center\"].leave){transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}:host([data-headless=\"true\"][data-position=\"top-center\"].leave){transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}@media(width<40rem){@starting-style{:host([data-headless=\"true\"][data-position^=\"bottom\"]){opacity:1;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}}:host([data-headless=\"true\"][data-swipe-direction=\"down\"].leave){transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position^=\"top\"]){opacity:1;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}}:host([data-headless=\"true\"][data-swipe-direction=\"up\"].leave){transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}}.toast-icon-loading{animation:none}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
932
+ `, isInline: true, styles: [":host{--_chrome-bg: var(--toast-bg);--_chrome-fg: var(--toast-color);--_chrome-border: var(--toast-border-color);--_icon-fill: light-dark(var(--toast-color), var(--toast-bg));--_icon-stroke: light-dark(var(--toast-bg), var(--toast-color));--_icon-color: var(--_icon-stroke);--_toast-x: 0%;--_toast-y: 0px;--_toast-enter-y: 0px;--_toast-leave-y: 0px;--toast-line-height: 1.35;--index: 0;--collapsed-peek: 14px;--offset: calc(var(--index, 0) * var(--collapsed-peek));display:flex;align-items:center;gap:.375rem;flex:1;min-width:0;width:100%;padding-inline:.75rem;padding-block:1rem;box-sizing:border-box;border:1px solid var(--_chrome-border);border-radius:.5rem;box-shadow:var(--toast-shadow);background:var(--_chrome-bg);color:var(--_chrome-fg);font:500 .875rem/var(--toast-line-height) system-ui,sans-serif;pointer-events:auto;touch-action:none;-webkit-user-select:none;user-select:none;position:absolute;opacity:1;height:var(--initial-height, auto);scale:1;z-index:calc(1000 - var(--index, 0));transform:translate(var(--_toast-x)) translateY(var(--_toast-y));transition:opacity .4s ease,transform .4s ease,scale .4s ease,height .4s ease,box-shadow .2s ease}@starting-style{:host{opacity:0;transform:translate(var(--_toast-x)) translateY(var(--_toast-enter-y))}}:host>*:not(.close-btn){transition:opacity .4s ease}:host([data-expanded=\"true\"]):after{content:\"\";position:absolute;left:0;width:100%;height:calc(var(--toast-gap, 16px) + 1px);bottom:100%;pointer-events:auto}:host([data-variant=\"loading\"]){touch-action:auto}:host([data-position^=\"bottom\"]){bottom:0;transform-origin:50% 100%;--_toast-y: calc(-1 * var(--offset, 0px));--_toast-enter-y: 100%}:host([data-position^=\"top\"]){top:0;transform-origin:50% 0;--_toast-y: calc(1 * var(--offset, 0px));--_toast-enter-y: -100%}:host([data-swipe-direction=\"down\"]){--_toast-leave-y: 100%}:host([data-swipe-direction=\"up\"]){--_toast-leave-y: -100%}:host([data-expanded=\"false\"]){scale:calc(1 - var(--index, 0) * .05)}:host([data-expanded=\"false\"]:not(:last-child)){height:var(--front-toast-height);overflow:hidden}:host([data-expanded=\"false\"]:not(:last-child):not([data-headless=\"true\"]))>*:not(.close-btn){opacity:0;pointer-events:none}:host([data-expanded=\"false\"]:nth-last-child(n + 4)){opacity:0;pointer-events:none}:host([data-rich-colors=\"true\"][data-variant=\"success\"]){--_chrome-bg: var(--toast-success-bg);--_chrome-fg: var(--toast-success-text);--_chrome-border: var(--toast-success-border)}:host([data-rich-colors=\"true\"][data-variant=\"error\"]){--_chrome-bg: var(--toast-error-bg);--_chrome-fg: var(--toast-error-text);--_chrome-border: var(--toast-error-border)}:host([data-rich-colors=\"true\"][data-variant=\"info\"]){--_chrome-bg: var(--toast-info-bg);--_chrome-fg: var(--toast-info-text);--_chrome-border: var(--toast-info-border)}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]){--_chrome-bg: var(--toast-warning-bg);--_chrome-fg: var(--toast-warning-text);--_chrome-border: var(--toast-warning-border)}:host([data-rich-colors=\"true\"]:is([data-variant=\"success\"],[data-variant=\"error\"],[data-variant=\"info\"],[data-variant=\"warning\"])){--_icon-fill: light-dark(var(--_chrome-fg), var(--_chrome-bg));--_icon-stroke: light-dark(var(--_chrome-bg), var(--_chrome-fg));--_icon-color: var(--_icon-stroke)}:host([data-headless=\"true\"]){width:max-content;max-width:calc(100dvw - var(--toast-offset-mobile-left, 16px) - var(--toast-offset-mobile-right, 16px));padding:0;gap:0;border:none;border-radius:0;box-shadow:none;background:transparent;color:inherit;font:inherit;line-height:normal;--toast-line-height: inherit}@starting-style{:host([data-headless=\"true\"]){opacity:1}}:host([data-headless=\"true\"][data-position=\"bottom-center\"]),:host([data-headless=\"true\"][data-position=\"top-center\"]){left:50%;right:auto;--_toast-x: -50%}:host([data-headless=\"true\"][data-icon=\"false\"]){padding-left:0}:host([data-icon=\"false\"]){padding-left:.925rem}@media(width>=40rem){:host{width:var(--toast-width)}:host([data-headless=\"true\"]){max-width:calc(100dvw - var(--toast-offset-left, 24px) - var(--toast-offset-right, 24px))}:host([data-headless=\"true\"][data-position$=\"-right\"]){left:auto;right:0}:host([data-headless=\"true\"][data-position$=\"-left\"]){left:0;right:auto}}@media(width<40rem){:host([data-headless=\"true\"][data-position^=\"bottom\"]),:host([data-headless=\"true\"][data-position^=\"top\"]){left:50%;right:auto;--_toast-x: -50%}}.toast-icon,.toast-icon ::ng-deep{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.425rem;height:1.425rem}.toast-icon svg,.toast-icon ::ng-deep svg{display:block;margin-inline:auto;fill:var(--_icon-fill);stroke:var(--_icon-stroke);color:var(--_icon-color);width:95%;height:95%}:host([data-theme=\"dark\"]) .toast-icon svg,:host([data-theme=\"dark\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}@media(prefers-color-scheme:dark){:host([data-theme=\"system\"]) .toast-icon svg,:host([data-theme=\"system\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}}.toast-icon-loading{display:block;width:.9rem;height:.9rem;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;border-right-color:transparent;animation:spin .45s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.msg{flex:1;min-width:0;max-width:95%;margin:0;font-size:.825rem;font-weight:500;line-height:calc(1.25 / .875);letter-spacing:.01em}@media(width<40rem){.msg{max-width:90%;font-size:.8rem}}.stack{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;flex:1;min-width:0}.stack .msg{flex:0 1 auto;max-width:100%}:host([data-icon=\"false\"]) .stack .msg{max-width:90%}.description{margin:0;font-size:.825rem;font-weight:400;line-height:1.35;letter-spacing:.01em;opacity:.9;max-width:100%}@media(width<40rem){.description{font-size:.8rem}}.toast-row-btn{flex-shrink:0;margin:0;margin-inline-start:auto;padding:.35rem .65rem;border-radius:.375rem;border:1px solid transparent;font:inherit;font-size:.8rem;font-weight:600;letter-spacing:.01em;line-height:1.2;cursor:pointer;white-space:nowrap;transition:transform .2s ease}.toast-row-btn:focus-visible{outline:2px solid var(--toast-color);outline-offset:2px}.toast-row-btn:active{transform:scale(.95)}.action-btn{background:var(--toast-action-bg);color:var(--toast-action-text);border-color:var(--toast-action-border)}.cancel-btn{background:var(--toast-cancel-bg);color:var(--toast-color);border-color:var(--toast-cancel-border)}.close-btn{position:absolute;top:-.3875rem;right:-.3875rem;flex-shrink:0;margin:-.15rem -.25rem -.15rem 0;border:1px solid var(--_chrome-border);box-shadow:var(--toast-shadow);background:var(--_chrome-bg);color:var(--_chrome-fg);cursor:pointer;line-height:1;padding:.25rem;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;opacity:1;scale:1;filter:blur(0)}.close-btn.close-enter{opacity:1;scale:1;filter:blur(0);transition:opacity .18s cubic-bezier(.19,1,.22,1),scale .18s cubic-bezier(.19,1,.22,1),filter .18s cubic-bezier(.19,1,.22,1)}@starting-style{.close-btn.close-enter{opacity:0;scale:.92;filter:blur(4px)}}.close-btn.close-leave{opacity:0;scale:.92;filter:blur(4px);pointer-events:none;transition:opacity .15s cubic-bezier(.19,1,.22,1),scale .15s cubic-bezier(.19,1,.22,1),filter .15s cubic-bezier(.19,1,.22,1)}.close-btn:focus-visible{outline:2px solid currentColor}.close-btn svg{display:block;width:.825rem;height:.825rem}:host(.leave){opacity:0;scale:.95;transform:translate(var(--_toast-x)) translateY(var(--_toast-leave-y))}@media(prefers-reduced-motion:reduce){:host{transition:none}:host([data-position]){--_toast-enter-y: var(--_toast-y);--_toast-leave-y: var(--_toast-y)}:host>*{transition:none}:host(.leave){scale:1}.toast-row-btn:active{transform:none}.close-btn.close-enter{scale:1;filter:none;transition:opacity .15s ease}@starting-style{.close-btn.close-enter{opacity:0;scale:1;filter:none}}.close-btn.close-leave{scale:1;filter:none;transition:opacity .15s ease}.toast-icon-loading{animation:none}}\n"], dependencies: [{ kind: "directive", type: BetterToastOutlet, selector: "[betterToastOutlet]", inputs: ["betterToastOutlet", "betterToastOutletInputs"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
916
933
  }
917
934
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BetterToastItem, decorators: [{
918
935
  type: Component,
919
- args: [{ selector: 'li[betterToastItem]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgComponentOutlet], host: {
936
+ args: [{ selector: 'li[betterToastItem]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [BetterToastOutlet], host: {
920
937
  role: 'listitem',
921
938
  tabindex: '0',
922
939
  class: 'toast',
@@ -926,7 +943,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
926
943
  '[attr.data-headless]': 'isHeadless() ? "true" : null',
927
944
  '[attr.data-swipe-direction]': 'swipeDirection()',
928
945
  '[attr.data-theme]': 'theme()',
929
- '[style.--offset]': 'offset() + "px"',
946
+ '[style.--initial-height]': 'initialHeightCss()',
930
947
  '[style]': 'isHeadless() ? undefined : hostStyle()',
931
948
  '[animate.leave]': '"leave"',
932
949
  '(pointerdown)': 'onPointerDown($event)',
@@ -934,98 +951,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
934
951
  '(pointerup)': 'onPointerUp()',
935
952
  '(pointercancel)': 'onPointerCancel()',
936
953
  '(pointerenter)': 'onPointerEnter()',
937
- '(pointerleave)': 'onPointerLeave()',
954
+ '(pointerleave)': 'onPointerLeave($event)',
938
955
  }, template: `
939
956
  @if (toast()?.component) {
940
- <ng-container *ngComponentOutlet="toast()!.component!; inputs: componentOutletInputs()" />
957
+ <ng-container
958
+ [betterToastOutlet]="toast()!.component!"
959
+ [betterToastOutletInputs]="toast()?.componentInputs"
960
+ />
941
961
  } @else {
942
962
  @if (shouldShowIconColumn()) {
943
963
  <span class="toast-icon" aria-hidden="true">
944
964
  @if (toast()?.icon) {
945
- <ng-container *ngComponentOutlet="toast()!.icon!" />
965
+ <ng-container [betterToastOutlet]="toast()!.icon!" />
946
966
  } @else if (iconComponent(); as IconCmp) {
947
- <ng-container *ngComponentOutlet="IconCmp" />
967
+ <ng-container [betterToastOutlet]="IconCmp" />
968
+ } @else if (variant() === 'loading') {
969
+ <div class="toast-icon-loading"></div>
948
970
  } @else {
949
- @switch (variant()) {
950
- @case ('success') {
951
- <svg
952
- xmlns="http://www.w3.org/2000/svg"
953
- fill="none"
954
- viewBox="0 0 24 24"
955
- aria-hidden="true"
956
- >
957
- <circle
958
- cx="12"
959
- cy="12"
960
- r="9"
961
- stroke="currentColor"
962
- stroke-linecap="round"
963
- stroke-linejoin="round"
964
- stroke-width="1.75"
965
- />
966
- <path
967
- stroke="currentColor"
968
- stroke-linecap="round"
969
- stroke-linejoin="round"
970
- stroke-width="1.75"
971
- d="M8.48 12.22 10.9 14.64 15.74 9.14"
972
- />
973
- </svg>
974
- }
975
- @case ('error') {
976
- <svg
977
- aria-hidden="true"
978
- xmlns="http://www.w3.org/2000/svg"
979
- viewBox="0 0 24 24"
980
- fill="none"
981
- stroke="currentColor"
982
- stroke-width="2"
983
- stroke-linecap="round"
984
- stroke-linejoin="round"
985
- >
971
+ <svg
972
+ fill="none"
973
+ viewBox="0 0 24 24"
974
+ stroke="currentColor"
975
+ stroke-linecap="round"
976
+ stroke-linejoin="round"
977
+ [attr.stroke-width]="variant() === 'error' ? 2 : 1.75"
978
+ >
979
+ @switch (variant()) {
980
+ @case ('success') {
981
+ <circle cx="12" cy="12" r="9" />
982
+ <path d="M8.48 12.22 10.9 14.64 15.74 9.14" />
983
+ }
984
+ @case ('error') {
986
985
  <circle cx="12" cy="12" r="10" />
987
986
  <line x1="9" y1="9" x2="15" y2="15" />
988
987
  <line x1="15" y1="9" x2="9" y2="15" />
989
- </svg>
990
- }
991
- @case ('info') {
992
- <svg
993
- aria-hidden="true"
994
- xmlns="http://www.w3.org/2000/svg"
995
- viewBox="0 0 24 24"
996
- fill="none"
997
- stroke="currentColor"
998
- stroke-width="1.75"
999
- stroke-linecap="round"
1000
- stroke-linejoin="round"
1001
- >
988
+ }
989
+ @case ('info') {
1002
990
  <circle cx="12" cy="12" r="10" />
1003
991
  <line x1="12" y1="16" x2="12" y2="12" />
1004
992
  <line x1="12" y1="8" x2="12.01" y2="8" />
1005
- </svg>
1006
- }
1007
- @case ('warning') {
1008
- <svg
1009
- aria-hidden="true"
1010
- xmlns="http://www.w3.org/2000/svg"
1011
- fill="none"
1012
- viewBox="0 0 24 24"
1013
- >
993
+ }
994
+ @case ('warning') {
1014
995
  <path
1015
- stroke="currentColor"
1016
- stroke-linecap="round"
1017
- stroke-linejoin="round"
1018
- stroke-width="1.75"
1019
996
  d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
1020
997
  />
1021
- </svg>
1022
- }
1023
- @case ('loading') {
1024
- <div class="toast-icon-loading" aria-hidden="true"></div>
998
+ }
1025
999
  }
1026
- @case ('description') {}
1027
- @case ('default') {}
1028
- }
1000
+ </svg>
1029
1001
  }
1030
1002
  </span>
1031
1003
  }
@@ -1035,10 +1007,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
1035
1007
  @if (toast()?.contentComponent) {
1036
1008
  <div class="msg" [class]="resolvedClassNames()?.message">
1037
1009
  <ng-container
1038
- *ngComponentOutlet="
1039
- toast()!.contentComponent!;
1040
- inputs: contentComponentOutletInputs()
1041
- "
1010
+ [betterToastOutlet]="toast()!.contentComponent!"
1011
+ [betterToastOutletInputs]="toast()?.contentComponentInputs"
1042
1012
  />
1043
1013
  </div>
1044
1014
  } @else {
@@ -1054,10 +1024,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
1054
1024
  @if (toast()?.contentComponent) {
1055
1025
  <div class="msg" [class]="resolvedClassNames()?.message">
1056
1026
  <ng-container
1057
- *ngComponentOutlet="
1058
- toast()!.contentComponent!;
1059
- inputs: contentComponentOutletInputs()
1060
- "
1027
+ [betterToastOutlet]="toast()!.contentComponent!"
1028
+ [betterToastOutletInputs]="toast()?.contentComponentInputs"
1061
1029
  />
1062
1030
  </div>
1063
1031
  } @else {
@@ -1066,58 +1034,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
1066
1034
  }
1067
1035
 
1068
1036
  @if (toast()?.toastAction; as rowAction) {
1069
- @switch (rowAction.role) {
1070
- @case ('action') {
1071
- <button
1072
- type="button"
1073
- class="toast-row-btn action-btn"
1074
- [class]="resolvedClassNames()?.actionButton"
1075
- [attr.data-row-btn]="rowAction.role"
1076
- (pointerdown)="onRowButtonPointerDown($event)"
1077
- (click)="onToastRowClick($event)"
1078
- >
1079
- {{ rowAction.label }}
1080
- </button>
1081
- }
1082
- @case ('cancel') {
1083
- <button
1084
- type="button"
1085
- class="toast-row-btn cancel-btn"
1086
- [class]="resolvedClassNames()?.cancelButton"
1087
- [attr.data-row-btn]="rowAction.role"
1088
- (pointerdown)="onRowButtonPointerDown($event)"
1089
- (click)="onToastRowClick($event)"
1090
- >
1091
- {{ rowAction.label }}
1092
- </button>
1093
- }
1094
- }
1037
+ <button
1038
+ type="button"
1039
+ class="toast-row-btn"
1040
+ [class.action-btn]="rowAction.role === 'action'"
1041
+ [class.cancel-btn]="rowAction.role === 'cancel'"
1042
+ [class]="
1043
+ rowAction.role === 'action'
1044
+ ? resolvedClassNames()?.actionButton
1045
+ : resolvedClassNames()?.cancelButton
1046
+ "
1047
+ [attr.data-row-btn]="rowAction.role"
1048
+ (pointerdown)="onRowButtonPointerDown($event)"
1049
+ (click)="onToastRowClick($event)"
1050
+ >
1051
+ {{ rowAction.label }}
1052
+ </button>
1095
1053
  }
1096
1054
  }
1097
1055
 
1098
- @if (closeButton() && !isHeadless()) {
1056
+ @if (closeButton() && !isHeadless() && (stackFront() || stackExpanded())) {
1099
1057
  <button
1100
1058
  type="button"
1101
1059
  class="close-btn"
1102
1060
  [class]="resolvedClassNames()?.closeButton"
1061
+ [animate.enter]="stackFront() ? null : 'close-enter'"
1062
+ [animate.leave]="stackFront() ? null : 'close-leave'"
1103
1063
  (click)="toaster.dismiss(toast()?.id ?? '')"
1104
1064
  [attr.aria-label]="dismissButtonAriaLabel()"
1105
1065
  >
1106
- <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
1107
- <path
1108
- stroke="currentColor"
1109
- stroke-linecap="round"
1110
- stroke-linejoin="round"
1111
- stroke-width="1.75"
1112
- d="M6 18 18 6M6 6l12 12"
1113
- />
1066
+ <svg
1067
+ fill="none"
1068
+ viewBox="0 0 24 24"
1069
+ stroke="currentColor"
1070
+ stroke-linecap="round"
1071
+ stroke-linejoin="round"
1072
+ stroke-width="1.75"
1073
+ aria-hidden="true"
1074
+ >
1075
+ <path d="M6 18 18 6M6 6l12 12" />
1114
1076
  </svg>
1115
1077
  </button>
1116
1078
  }
1117
- `, styles: [":host{display:flex;align-items:center;gap:.375rem;flex:1;min-width:0;width:100%;padding-inline:.75rem;padding-block:1rem;box-sizing:border-box;border:1px solid var(--toast-border-color);border-radius:.5rem;box-shadow:var(--toast-shadow);background:var(--toast-bg);color:var(--toast-color);--toast-line-height: 1.35;font:500 .875rem/var(--toast-line-height) system-ui,sans-serif;pointer-events:auto;touch-action:none;-webkit-user-select:none;user-select:none;position:absolute;opacity:1;scale:1;transition:opacity .4s ease,transform .4s ease,scale .4s ease,box-shadow .2s ease}:host([data-variant=\"loading\"]){touch-action:auto}@media(width>=40rem){:host{width:var(--toast-width)}}:host([data-position^=\"bottom\"]){bottom:0;transform:translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-position^=\"bottom\"]){opacity:0;transform:translateY(100%)}}:host([data-position^=\"top\"]){top:0;transform:translateY(calc(1 * var(--offset, 0px)))}@starting-style{:host([data-position^=\"top\"]){opacity:0;transform:translateY(-100%)}}@starting-style{:host([data-headless=\"true\"][data-position^=\"bottom\"]){opacity:1}}@starting-style{:host([data-headless=\"true\"][data-position^=\"top\"]){opacity:1}}:host([data-variant=\"default\"]) .toast-icon svg,:host([data-variant=\"default\"]) .toast-icon ::ng-deep svg,:host([data-variant=\"description\"]) .toast-icon svg,:host([data-variant=\"description\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"success\"]){background:var(--toast-success-bg);color:var(--toast-success-text);border-color:var(--toast-success-border)}:host([data-rich-colors=\"false\"][data-variant=\"success\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"success\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"success\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"success\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-success-text),var(--toast-success-bg));stroke:light-dark(var(--toast-success-bg),var(--toast-success-text));color:light-dark(var(--toast-success-bg),var(--toast-success-text))}:host([data-rich-colors=\"true\"][data-variant=\"error\"]){background:var(--toast-error-bg);color:var(--toast-error-text);border-color:var(--toast-error-border)}:host([data-rich-colors=\"false\"][data-variant=\"error\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"error\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"error\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"error\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-error-text),var(--toast-error-bg));stroke:light-dark(var(--toast-error-bg),var(--toast-error-text));color:light-dark(var(--toast-error-bg),var(--toast-error-text))}:host([data-rich-colors=\"true\"][data-variant=\"info\"]){background:var(--toast-info-bg);color:var(--toast-info-text);border-color:var(--toast-info-border)}:host([data-rich-colors=\"false\"][data-variant=\"info\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"info\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"info\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"info\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-info-text),var(--toast-info-bg));stroke:light-dark(var(--toast-info-bg),var(--toast-info-text));color:light-dark(var(--toast-info-bg),var(--toast-info-text))}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]){background:var(--toast-warning-bg);color:var(--toast-warning-text);border-color:var(--toast-warning-border)}:host([data-rich-colors=\"false\"][data-variant=\"warning\"]) .toast-icon svg,:host([data-rich-colors=\"false\"][data-variant=\"warning\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-color),var(--toast-bg));color:light-dark(var(--toast-bg),var(--toast-color))}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]) .toast-icon svg,:host([data-rich-colors=\"true\"][data-variant=\"warning\"]) .toast-icon ::ng-deep svg{fill:light-dark(var(--toast-warning-text),var(--toast-warning-bg));stroke:light-dark(var(--toast-warning-bg),var(--toast-warning-text));color:light-dark(var(--toast-warning-bg),var(--toast-warning-text))}:host([data-headless=\"true\"]){width:max-content;max-width:calc(100dvw - var(--toast-offset-mobile-left, 16px) - var(--toast-offset-mobile-right, 16px));padding:0;gap:0;border:none;border-radius:0;box-shadow:none;background:transparent;color:inherit;font:inherit;line-height:normal;--toast-line-height: inherit}@media(width>=40rem){:host([data-headless=\"true\"]){max-width:calc(100dvw - var(--toast-offset-left, 24px) - var(--toast-offset-right, 24px))}}@media(width>=40rem){:host([data-headless=\"true\"][data-position$=\"-right\"]){left:auto;right:0}:host([data-headless=\"true\"][data-position$=\"-left\"]){left:0;right:auto}}:host([data-headless=\"true\"][data-position=\"bottom-center\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position=\"bottom-center\"]){opacity:1;transform:translate(-50%) translateY(100%)}}:host([data-headless=\"true\"][data-position=\"top-center\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position=\"top-center\"]){opacity:1;transform:translate(-50%) translateY(-100%)}}@media(width<40rem){:host([data-headless=\"true\"][data-position^=\"bottom\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position^=\"bottom\"]){opacity:1;transform:translate(-50%) translateY(100%)}}:host([data-headless=\"true\"][data-position^=\"top\"]){left:50%;right:auto;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position^=\"top\"]){opacity:1;transform:translate(-50%) translateY(-100%)}}:host([data-headless=\"true\"][data-swipe-direction=\"down\"].leave){transform:translate(-50%) translateY(100%)}:host([data-headless=\"true\"][data-swipe-direction=\"up\"].leave){transform:translate(-50%) translateY(-100%)}}:host([data-headless=\"true\"][data-rich-colors=\"true\"]){background:transparent;color:inherit;border-color:transparent}:host([data-headless=\"true\"][data-icon=\"false\"]){padding-left:0}:host([data-icon=\"false\"]){padding-left:.925rem}.toast-icon,.toast-icon ::ng-deep{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.425rem;height:1.425rem}.toast-icon svg,.toast-icon ::ng-deep svg{display:block;margin-inline:auto;fill:light-dark(var(--toast-color),var(--toast-bg));stroke:light-dark(var(--toast-bg),var(--toast-color));width:95%;height:95%}:host([data-theme=\"dark\"]) .toast-icon svg,:host([data-theme=\"dark\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}@media(prefers-color-scheme:dark){:host([data-theme=\"system\"]) .toast-icon svg,:host([data-theme=\"system\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}}.toast-icon-loading{display:block;width:.9rem;height:.9rem;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;border-right-color:transparent;animation:spin .45s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.msg{flex:1;min-width:0;max-width:95%;margin:0;font-size:.825rem;font-weight:500;line-height:calc(1.25 / .875);letter-spacing:.01em}@media(width<40rem){.msg{max-width:90%;font-size:.8rem}}.stack{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;flex:1;min-width:0}.stack .msg{flex:0 1 auto;max-width:100%}:host([data-icon=\"false\"]) .stack .msg{max-width:90%}.description{margin:0;font-size:.825rem;font-weight:400;line-height:1.35;letter-spacing:.01em;opacity:.9;max-width:100%}@media(width<40rem){.description{font-size:.8rem}}.toast-row-btn{flex-shrink:0;margin:0;margin-inline-start:auto;padding:.35rem .65rem;border-radius:.375rem;border:1px solid transparent;font:inherit;font-size:.8rem;font-weight:600;letter-spacing:.01em;line-height:1.2;cursor:pointer;white-space:nowrap;transition:transform .2s ease}.toast-row-btn:focus-visible{outline:2px solid var(--toast-color);outline-offset:2px}.toast-row-btn:active{transform:scale(.95)}@media(prefers-reduced-motion:reduce){.toast-row-btn:active{transform:none}}.action-btn{background:var(--toast-action-bg);color:var(--toast-action-text);border-color:var(--toast-action-border)}.cancel-btn{background:var(--toast-cancel-bg);color:var(--toast-color);border-color:var(--toast-cancel-border)}.toast-custom{flex:1;min-width:0;width:100%;font-size:.825rem;font-weight:400;line-height:calc(1.25 / .875)}.toast-custom :first-child{margin-top:0}.toast-custom :last-child{margin-bottom:0}.close-btn{position:absolute;top:-.3875rem;right:-.3875rem;flex-shrink:0;margin:-.15rem -.25rem -.15rem 0;border:1px solid var(--toast-border-color);box-shadow:var(--toast-shadow);background:var(--toast-bg);color:var(--toast-color);cursor:pointer;line-height:1;padding:.25rem;border-radius:50%;display:inline-flex;align-items:center;justify-content:center}:host([data-rich-colors=\"true\"][data-variant=\"success\"]) .close-btn{border-color:var(--toast-success-border);background:var(--toast-success-bg);color:var(--toast-success-text)}:host([data-rich-colors=\"true\"][data-variant=\"error\"]) .close-btn{border-color:var(--toast-error-border);background:var(--toast-error-bg);color:var(--toast-error-text)}:host([data-rich-colors=\"true\"][data-variant=\"info\"]) .close-btn{border-color:var(--toast-info-border);background:var(--toast-info-bg);color:var(--toast-info-text)}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]) .close-btn{border-color:var(--toast-warning-border);background:var(--toast-warning-bg);color:var(--toast-warning-text)}.close-btn:focus-visible{outline:2px solid currentColor}.close-btn svg{display:block;width:.825rem;height:.825rem}:host(.leave){opacity:0;scale:.95}:host([data-swipe-direction=\"down\"].leave){transform:translateY(100%)}:host([data-headless=\"true\"][data-position=\"bottom-center\"].leave){transform:translate(-50%) translateY(100%)}:host([data-swipe-direction=\"up\"].leave){transform:translateY(-100%)}:host([data-headless=\"true\"][data-position=\"top-center\"].leave){transform:translate(-50%) translateY(-100%)}@media(prefers-reduced-motion:reduce){:host{transition:none}:host(.leave){scale:1}@starting-style{:host([data-position^=\"bottom\"]){opacity:0;transform:translateY(calc(-1 * var(--offset, 0px)))}}@starting-style{:host([data-position^=\"top\"]){opacity:0;transform:translateY(calc(1 * var(--offset, 0px)))}}@starting-style{:host([data-headless=\"true\"][data-position=\"bottom-center\"]){opacity:1;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}}@starting-style{:host([data-headless=\"true\"][data-position=\"top-center\"]){opacity:1;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}}:host([data-swipe-direction=\"down\"].leave){transform:translateY(calc(-1 * var(--offset, 0px)))}:host([data-swipe-direction=\"up\"].leave){transform:translateY(calc(1 * var(--offset, 0px)))}:host([data-headless=\"true\"][data-position=\"bottom-center\"].leave){transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}:host([data-headless=\"true\"][data-position=\"top-center\"].leave){transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}@media(width<40rem){@starting-style{:host([data-headless=\"true\"][data-position^=\"bottom\"]){opacity:1;transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}}:host([data-headless=\"true\"][data-swipe-direction=\"down\"].leave){transform:translate(-50%) translateY(calc(-1 * var(--offset, 0px)))}@starting-style{:host([data-headless=\"true\"][data-position^=\"top\"]){opacity:1;transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}}:host([data-headless=\"true\"][data-swipe-direction=\"up\"].leave){transform:translate(-50%) translateY(calc(1 * var(--offset, 0px)))}}.toast-icon-loading{animation:none}}\n"] }]
1118
- }], ctorParameters: () => [], propDecorators: { toast: [{ type: i0.Input, args: [{ isSignal: true, alias: "toast", required: false }] }], toasterStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "toasterStyle", required: false }] }], toasterClassNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "toasterClassNames", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: true }] }], closeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeButton", required: false }] }], customIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "customIcons", required: false }] }], dismissButtonAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissButtonAriaLabel", required: false }] }], stackPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackPosition", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], heightChange: [{ type: i0.Output, args: ["heightChange"] }] } });
1079
+ `, styles: [":host{--_chrome-bg: var(--toast-bg);--_chrome-fg: var(--toast-color);--_chrome-border: var(--toast-border-color);--_icon-fill: light-dark(var(--toast-color), var(--toast-bg));--_icon-stroke: light-dark(var(--toast-bg), var(--toast-color));--_icon-color: var(--_icon-stroke);--_toast-x: 0%;--_toast-y: 0px;--_toast-enter-y: 0px;--_toast-leave-y: 0px;--toast-line-height: 1.35;--index: 0;--collapsed-peek: 14px;--offset: calc(var(--index, 0) * var(--collapsed-peek));display:flex;align-items:center;gap:.375rem;flex:1;min-width:0;width:100%;padding-inline:.75rem;padding-block:1rem;box-sizing:border-box;border:1px solid var(--_chrome-border);border-radius:.5rem;box-shadow:var(--toast-shadow);background:var(--_chrome-bg);color:var(--_chrome-fg);font:500 .875rem/var(--toast-line-height) system-ui,sans-serif;pointer-events:auto;touch-action:none;-webkit-user-select:none;user-select:none;position:absolute;opacity:1;height:var(--initial-height, auto);scale:1;z-index:calc(1000 - var(--index, 0));transform:translate(var(--_toast-x)) translateY(var(--_toast-y));transition:opacity .4s ease,transform .4s ease,scale .4s ease,height .4s ease,box-shadow .2s ease}@starting-style{:host{opacity:0;transform:translate(var(--_toast-x)) translateY(var(--_toast-enter-y))}}:host>*:not(.close-btn){transition:opacity .4s ease}:host([data-expanded=\"true\"]):after{content:\"\";position:absolute;left:0;width:100%;height:calc(var(--toast-gap, 16px) + 1px);bottom:100%;pointer-events:auto}:host([data-variant=\"loading\"]){touch-action:auto}:host([data-position^=\"bottom\"]){bottom:0;transform-origin:50% 100%;--_toast-y: calc(-1 * var(--offset, 0px));--_toast-enter-y: 100%}:host([data-position^=\"top\"]){top:0;transform-origin:50% 0;--_toast-y: calc(1 * var(--offset, 0px));--_toast-enter-y: -100%}:host([data-swipe-direction=\"down\"]){--_toast-leave-y: 100%}:host([data-swipe-direction=\"up\"]){--_toast-leave-y: -100%}:host([data-expanded=\"false\"]){scale:calc(1 - var(--index, 0) * .05)}:host([data-expanded=\"false\"]:not(:last-child)){height:var(--front-toast-height);overflow:hidden}:host([data-expanded=\"false\"]:not(:last-child):not([data-headless=\"true\"]))>*:not(.close-btn){opacity:0;pointer-events:none}:host([data-expanded=\"false\"]:nth-last-child(n + 4)){opacity:0;pointer-events:none}:host([data-rich-colors=\"true\"][data-variant=\"success\"]){--_chrome-bg: var(--toast-success-bg);--_chrome-fg: var(--toast-success-text);--_chrome-border: var(--toast-success-border)}:host([data-rich-colors=\"true\"][data-variant=\"error\"]){--_chrome-bg: var(--toast-error-bg);--_chrome-fg: var(--toast-error-text);--_chrome-border: var(--toast-error-border)}:host([data-rich-colors=\"true\"][data-variant=\"info\"]){--_chrome-bg: var(--toast-info-bg);--_chrome-fg: var(--toast-info-text);--_chrome-border: var(--toast-info-border)}:host([data-rich-colors=\"true\"][data-variant=\"warning\"]){--_chrome-bg: var(--toast-warning-bg);--_chrome-fg: var(--toast-warning-text);--_chrome-border: var(--toast-warning-border)}:host([data-rich-colors=\"true\"]:is([data-variant=\"success\"],[data-variant=\"error\"],[data-variant=\"info\"],[data-variant=\"warning\"])){--_icon-fill: light-dark(var(--_chrome-fg), var(--_chrome-bg));--_icon-stroke: light-dark(var(--_chrome-bg), var(--_chrome-fg));--_icon-color: var(--_icon-stroke)}:host([data-headless=\"true\"]){width:max-content;max-width:calc(100dvw - var(--toast-offset-mobile-left, 16px) - var(--toast-offset-mobile-right, 16px));padding:0;gap:0;border:none;border-radius:0;box-shadow:none;background:transparent;color:inherit;font:inherit;line-height:normal;--toast-line-height: inherit}@starting-style{:host([data-headless=\"true\"]){opacity:1}}:host([data-headless=\"true\"][data-position=\"bottom-center\"]),:host([data-headless=\"true\"][data-position=\"top-center\"]){left:50%;right:auto;--_toast-x: -50%}:host([data-headless=\"true\"][data-icon=\"false\"]){padding-left:0}:host([data-icon=\"false\"]){padding-left:.925rem}@media(width>=40rem){:host{width:var(--toast-width)}:host([data-headless=\"true\"]){max-width:calc(100dvw - var(--toast-offset-left, 24px) - var(--toast-offset-right, 24px))}:host([data-headless=\"true\"][data-position$=\"-right\"]){left:auto;right:0}:host([data-headless=\"true\"][data-position$=\"-left\"]){left:0;right:auto}}@media(width<40rem){:host([data-headless=\"true\"][data-position^=\"bottom\"]),:host([data-headless=\"true\"][data-position^=\"top\"]){left:50%;right:auto;--_toast-x: -50%}}.toast-icon,.toast-icon ::ng-deep{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.425rem;height:1.425rem}.toast-icon svg,.toast-icon ::ng-deep svg{display:block;margin-inline:auto;fill:var(--_icon-fill);stroke:var(--_icon-stroke);color:var(--_icon-color);width:95%;height:95%}:host([data-theme=\"dark\"]) .toast-icon svg,:host([data-theme=\"dark\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}@media(prefers-color-scheme:dark){:host([data-theme=\"system\"]) .toast-icon svg,:host([data-theme=\"system\"]) .toast-icon ::ng-deep svg{width:87.5%;height:87.5%}}.toast-icon-loading{display:block;width:.9rem;height:.9rem;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;border-right-color:transparent;animation:spin .45s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.msg{flex:1;min-width:0;max-width:95%;margin:0;font-size:.825rem;font-weight:500;line-height:calc(1.25 / .875);letter-spacing:.01em}@media(width<40rem){.msg{max-width:90%;font-size:.8rem}}.stack{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;flex:1;min-width:0}.stack .msg{flex:0 1 auto;max-width:100%}:host([data-icon=\"false\"]) .stack .msg{max-width:90%}.description{margin:0;font-size:.825rem;font-weight:400;line-height:1.35;letter-spacing:.01em;opacity:.9;max-width:100%}@media(width<40rem){.description{font-size:.8rem}}.toast-row-btn{flex-shrink:0;margin:0;margin-inline-start:auto;padding:.35rem .65rem;border-radius:.375rem;border:1px solid transparent;font:inherit;font-size:.8rem;font-weight:600;letter-spacing:.01em;line-height:1.2;cursor:pointer;white-space:nowrap;transition:transform .2s ease}.toast-row-btn:focus-visible{outline:2px solid var(--toast-color);outline-offset:2px}.toast-row-btn:active{transform:scale(.95)}.action-btn{background:var(--toast-action-bg);color:var(--toast-action-text);border-color:var(--toast-action-border)}.cancel-btn{background:var(--toast-cancel-bg);color:var(--toast-color);border-color:var(--toast-cancel-border)}.close-btn{position:absolute;top:-.3875rem;right:-.3875rem;flex-shrink:0;margin:-.15rem -.25rem -.15rem 0;border:1px solid var(--_chrome-border);box-shadow:var(--toast-shadow);background:var(--_chrome-bg);color:var(--_chrome-fg);cursor:pointer;line-height:1;padding:.25rem;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;opacity:1;scale:1;filter:blur(0)}.close-btn.close-enter{opacity:1;scale:1;filter:blur(0);transition:opacity .18s cubic-bezier(.19,1,.22,1),scale .18s cubic-bezier(.19,1,.22,1),filter .18s cubic-bezier(.19,1,.22,1)}@starting-style{.close-btn.close-enter{opacity:0;scale:.92;filter:blur(4px)}}.close-btn.close-leave{opacity:0;scale:.92;filter:blur(4px);pointer-events:none;transition:opacity .15s cubic-bezier(.19,1,.22,1),scale .15s cubic-bezier(.19,1,.22,1),filter .15s cubic-bezier(.19,1,.22,1)}.close-btn:focus-visible{outline:2px solid currentColor}.close-btn svg{display:block;width:.825rem;height:.825rem}:host(.leave){opacity:0;scale:.95;transform:translate(var(--_toast-x)) translateY(var(--_toast-leave-y))}@media(prefers-reduced-motion:reduce){:host{transition:none}:host([data-position]){--_toast-enter-y: var(--_toast-y);--_toast-leave-y: var(--_toast-y)}:host>*{transition:none}:host(.leave){scale:1}.toast-row-btn:active{transform:none}.close-btn.close-enter{scale:1;filter:none;transition:opacity .15s ease}@starting-style{.close-btn.close-enter{opacity:0;scale:1;filter:none}}.close-btn.close-leave{scale:1;filter:none;transition:opacity .15s ease}.toast-icon-loading{animation:none}}\n"] }]
1080
+ }], ctorParameters: () => [], propDecorators: { toast: [{ type: i0.Input, args: [{ isSignal: true, alias: "toast", required: false }] }], toasterStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "toasterStyle", required: false }] }], toasterClassNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "toasterClassNames", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], closeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeButton", required: false }] }], customIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "customIcons", required: false }] }], dismissButtonAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissButtonAriaLabel", required: false }] }], stackPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackPosition", required: false }] }], stackExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackExpanded", required: false }] }], stackFront: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackFront", required: false }] }], stackHover: [{ type: i0.Output, args: ["stackHover"] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], heightChange: [{ type: i0.Output, args: ["heightChange"] }] } });
1119
1081
  /**
1120
1082
  * Renders the toaster stack. Add once near the root of your app (e.g. in `App`).
1083
+ * With `[stacked]="true"` (default), extra toasts collapse into a 3-layer card stack (latest in front); hover, focus, or a press on touch expands the full list.
1084
+ * Set `[stacked]="false"` to keep every toast fully visible, spaced by height plus gap.
1121
1085
  * Variant-colored surfaces are off by default; set `[richColors]="true"` to enable them.
1122
1086
  * Set `[duration]` for auto-dismiss when service helpers omit their duration argument.
1123
1087
  * Use **`duration="Infinity"`** (that exact literal) or `[duration]="…"` with a number / {@link TOAST_DURATION_MANUAL_DISMISS} for persist until dismissed; `0` still works.
@@ -1138,14 +1102,16 @@ class BetterToaster {
1138
1102
  * `0` still works. Does not apply to `loading()`. Defaults to the library default (4000ms).
1139
1103
  */
1140
1104
  durationMs = input(DEFAULT_TOAST_DURATION_MS, { ...(ngDevMode ? { debugName: "durationMs" } : /* istanbul ignore next */ {}), alias: 'duration',
1141
- transform: (value) => {
1142
- const durationMs = parseToasterDurationMs(value);
1143
- this.toaster.setDefaultDurationMs(durationMs);
1144
- return durationMs;
1145
- } });
1105
+ transform: parseToasterDurationMs });
1146
1106
  /** Where the stack is anchored on the viewport. */
1147
1107
  position = input('bottom-right', /* @ts-ignore */
1148
1108
  ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
1109
+ /**
1110
+ * When true (default), extra toasts collapse into a 3-layer card stack; hover, focus, or a press on touch expands the list.
1111
+ * When false, every toast stays fully visible, spaced by measured height plus gap.
1112
+ */
1113
+ stacked = input(true, /* @ts-ignore */
1114
+ ...(ngDevMode ? [{ debugName: "stacked" }] : /* istanbul ignore next */ []));
1149
1115
  /**
1150
1116
  * Viewport inset for the toast stack: a single CSS value for all sides, or an object with any of `top` / `right` / `bottom` / `left`.
1151
1117
  * Binds `--toast-offset-top` / `right` / `bottom` / `left` on `.toast-container`.
@@ -1198,41 +1164,58 @@ class BetterToaster {
1198
1164
  /** Measured height in px per toast id, updated when a toast item reports `heightChange`. */
1199
1165
  heights = signal({}, /* @ts-ignore */
1200
1166
  ...(ngDevMode ? [{ debugName: "heights" }] : /* istanbul ignore next */ []));
1167
+ /** Gap between expanded toasts; kept in sync with layout math and the CSS hit-area. */
1168
+ stackGapPx = GAP;
1169
+ /** Height of the newest measured toast, used so collapsed layers share one silhouette. */
1170
+ frontToastHeightCss = computed(() => {
1171
+ const height = resolveFrontToastHeightPx(this.toaster.toasts(), this.heights());
1172
+ return height ? `${height}px` : null;
1173
+ }, /* @ts-ignore */
1174
+ ...(ngDevMode ? [{ debugName: "frontToastHeightCss" }] : /* istanbul ignore next */ []));
1175
+ /** Pointer is over a toast (or moving between toasts in the stack). */
1176
+ hovering = signal(false, /* @ts-ignore */
1177
+ ...(ngDevMode ? [{ debugName: "hovering" }] : /* istanbul ignore next */ []));
1178
+ /** Keyboard focus is inside the toast list. */
1179
+ focusWithin = signal(false, /* @ts-ignore */
1180
+ ...(ngDevMode ? [{ debugName: "focusWithin" }] : /* istanbul ignore next */ []));
1181
+ /** Hover or focus: expands a stacked list and pauses auto-dismiss. */
1182
+ expanded = computed(() => this.hovering() || this.focusWithin(), /* @ts-ignore */
1183
+ ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
1184
+ /** Layout uses the expanded list while stacked is off, or while the stack is hovered / focused. */
1185
+ layoutExpanded = computed(() => !this.stacked() || this.expanded(), /* @ts-ignore */
1186
+ ...(ngDevMode ? [{ debugName: "layoutExpanded" }] : /* istanbul ignore next */ []));
1187
+ constructor() {
1188
+ effect(() => {
1189
+ this.toaster.setDefaultDurationMs(this.durationMs());
1190
+ });
1191
+ effect(() => {
1192
+ if (this.toaster.toasts().length === 0) {
1193
+ this.hovering.set(false);
1194
+ this.focusWithin.set(false);
1195
+ }
1196
+ });
1197
+ effect(() => {
1198
+ if (!this.expanded()) {
1199
+ return;
1200
+ }
1201
+ for (const toast of this.toaster.toasts()) {
1202
+ this.toaster.pauseAutoDismiss(toast.id);
1203
+ }
1204
+ });
1205
+ }
1201
1206
  /** Resolved `aria-label` for the outer `<section>` live region. */
1202
1207
  notificationsRegionAriaLabel = computed(() => this.accessibilityLabels()?.notificationsRegion ?? DEFAULT_TOASTER_ARIA_NOTIFICATIONS_REGION, /* @ts-ignore */
1203
1208
  ...(ngDevMode ? [{ debugName: "notificationsRegionAriaLabel" }] : /* istanbul ignore next */ []));
1204
1209
  /** Resolved `aria-label` for each toast’s dismiss control. */
1205
1210
  dismissButtonAriaLabel = computed(() => this.accessibilityLabels()?.dismissButton ?? DEFAULT_TOASTER_ARIA_DISMISS_BUTTON, /* @ts-ignore */
1206
1211
  ...(ngDevMode ? [{ debugName: "dismissButtonAriaLabel" }] : /* istanbul ignore next */ []));
1207
- /** Vertical offset in px for the toast stack. */
1208
- offsetTop = computed(() => resolveToasterOffsetSide(this.offset(), 'top'), /* @ts-ignore */
1209
- ...(ngDevMode ? [{ debugName: "offsetTop" }] : /* istanbul ignore next */ []));
1210
- /** Horizontal offset in px for the toast stack. */
1211
- offsetRight = computed(() => resolveToasterOffsetSide(this.offset(), 'right'), /* @ts-ignore */
1212
- ...(ngDevMode ? [{ debugName: "offsetRight" }] : /* istanbul ignore next */ []));
1213
- /** Vertical offset in px for the toast stack. */
1214
- offsetBottom = computed(() => resolveToasterOffsetSide(this.offset(), 'bottom'), /* @ts-ignore */
1215
- ...(ngDevMode ? [{ debugName: "offsetBottom" }] : /* istanbul ignore next */ []));
1216
- /** Horizontal offset in px for the toast stack. */
1217
- offsetLeft = computed(() => resolveToasterOffsetSide(this.offset(), 'left'), /* @ts-ignore */
1218
- ...(ngDevMode ? [{ debugName: "offsetLeft" }] : /* istanbul ignore next */ []));
1219
- /** Vertical offset in px for the toast stack on narrow layouts. */
1220
- mobileOffsetTop = computed(() => resolveToasterOffsetSide(this.mobileOffset(), 'top'), /* @ts-ignore */
1221
- ...(ngDevMode ? [{ debugName: "mobileOffsetTop" }] : /* istanbul ignore next */ []));
1222
- /** Horizontal offset in px for the toast stack on narrow layouts. */
1223
- mobileOffsetRight = computed(() => resolveToasterOffsetSide(this.mobileOffset(), 'right'), /* @ts-ignore */
1224
- ...(ngDevMode ? [{ debugName: "mobileOffsetRight" }] : /* istanbul ignore next */ []));
1225
- /** Vertical offset in px for the toast stack on narrow layouts. */
1226
- mobileOffsetBottom = computed(() => resolveToasterOffsetSide(this.mobileOffset(), 'bottom'), /* @ts-ignore */
1227
- ...(ngDevMode ? [{ debugName: "mobileOffsetBottom" }] : /* istanbul ignore next */ []));
1228
- /** Horizontal offset in px for the toast stack on narrow layouts. */
1229
- mobileOffsetLeft = computed(() => resolveToasterOffsetSide(this.mobileOffset(), 'left'), /* @ts-ignore */
1230
- ...(ngDevMode ? [{ debugName: "mobileOffsetLeft" }] : /* istanbul ignore next */ []));
1212
+ offsetSide = resolveToasterOffsetSide;
1231
1213
  /**
1232
- * Vertical offset in px for each toast id so stacked toasts do not overlap.
1233
- * Walks newest-to-oldest (end of the list first): the latest toast has offset 0; each older toast sits above by the sum of heights below plus the inter-toast gap.
1214
+ * `--index` is the stack depth: last item is 0 (front), older items count up.
1215
+ * Collapsed peek, scale, front, and hidden layers are CSS from that index / `:last-child`.
1216
+ * Expanded offset still needs measured heights: each toast sits at the sum of those in front plus gap.
1234
1217
  */
1235
- offsets = computed(() => {
1218
+ expandedOffsets = computed(() => {
1236
1219
  const toastsList = this.toaster.toasts();
1237
1220
  const heights = this.heights();
1238
1221
  const result = {};
@@ -1244,19 +1227,89 @@ class BetterToaster {
1244
1227
  }
1245
1228
  return result;
1246
1229
  }, /* @ts-ignore */
1247
- ...(ngDevMode ? [{ debugName: "offsets" }] : /* istanbul ignore next */ []));
1248
- /** Merges a toast item’s reported height into `heights` so `offsets` can recompute. */
1230
+ ...(ngDevMode ? [{ debugName: "expandedOffsets" }] : /* istanbul ignore next */ []));
1231
+ /** Oldest toasts past the 3 visible collapsed layers. */
1232
+ hiddenToastIds = computed(() => {
1233
+ if (this.layoutExpanded()) {
1234
+ return new Set();
1235
+ }
1236
+ const toasts = this.toaster.toasts();
1237
+ const hidden = new Set();
1238
+ const cutoff = toasts.length - VISIBLE_TOASTS;
1239
+ for (let i = 0; i < cutoff; i++) {
1240
+ hidden.add(toasts[i].id);
1241
+ }
1242
+ return hidden;
1243
+ }, /* @ts-ignore */
1244
+ ...(ngDevMode ? [{ debugName: "hiddenToastIds" }] : /* istanbul ignore next */ []));
1245
+ /** Expanded stack only: measured height + gap. Collapsed `--offset` comes from `--index` in CSS. */
1246
+ toastOffsetCss(toastId) {
1247
+ if (!this.layoutExpanded()) {
1248
+ return null;
1249
+ }
1250
+ return `${this.expandedOffsets()[toastId] ?? 0}px`;
1251
+ }
1252
+ /** Merges a toast item’s reported height into `heights` so expanded offsets can recompute. */
1249
1253
  onHeightChange(toastId, height) {
1250
1254
  this.heights.update((h) => ({ ...h, [toastId]: height }));
1251
1255
  }
1256
+ /** Expands the stack and pauses auto-dismiss while the pointer is over any toast. */
1257
+ onItemStackHover(hovering) {
1258
+ this.hovering.set(hovering);
1259
+ if (hovering || this.focusWithin()) {
1260
+ this.pauseAllAutoDismiss();
1261
+ }
1262
+ else {
1263
+ this.resumeAllAutoDismiss();
1264
+ }
1265
+ }
1252
1266
  /**
1253
- * Initializes the toaster service with the default duration.
1267
+ * Collapses a touch/pen sticky expand when the next press is outside the stack.
1268
+ * Hover pointers already collapse on `pointerleave`.
1254
1269
  */
1255
- ngOnInit() {
1256
- this.toaster.setDefaultDurationMs(this.durationMs());
1270
+ onDocumentPointerDown(event) {
1271
+ if (!this.hovering() || isInsideToastItem(event.target)) {
1272
+ return;
1273
+ }
1274
+ this.hovering.set(false);
1275
+ if (!this.focusWithin()) {
1276
+ this.resumeAllAutoDismiss();
1277
+ }
1278
+ }
1279
+ /** Expands the stack when a toast (or dismiss control) receives keyboard focus. */
1280
+ onStackFocusIn(event) {
1281
+ if (!isInsideToastItem(event.target)) {
1282
+ if (event.target instanceof HTMLElement) {
1283
+ event.target.blur();
1284
+ }
1285
+ return;
1286
+ }
1287
+ this.focusWithin.set(true);
1288
+ this.pauseAllAutoDismiss();
1289
+ }
1290
+ /** Collapses when focus leaves the list, unless the pointer is still over it. */
1291
+ onStackFocusOut(event) {
1292
+ const next = event.relatedTarget;
1293
+ if (isInsideToastItem(next)) {
1294
+ return;
1295
+ }
1296
+ this.focusWithin.set(false);
1297
+ if (!this.hovering()) {
1298
+ this.resumeAllAutoDismiss();
1299
+ }
1300
+ }
1301
+ pauseAllAutoDismiss() {
1302
+ for (const toast of this.toaster.toasts()) {
1303
+ this.toaster.pauseAutoDismiss(toast.id);
1304
+ }
1305
+ }
1306
+ resumeAllAutoDismiss() {
1307
+ for (const toast of this.toaster.toasts()) {
1308
+ this.toaster.resumeAutoDismiss(toast.id);
1309
+ }
1257
1310
  }
1258
1311
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BetterToaster, deps: [], target: i0.ɵɵFactoryTarget.Component });
1259
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: BetterToaster, isStandalone: true, selector: "better-toaster", inputs: { durationMs: { classPropertyName: "durationMs", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, mobileOffset: { classPropertyName: "mobileOffset", publicName: "mobileOffset", isSignal: true, isRequired: false, transformFunction: null }, richColors: { classPropertyName: "richColors", publicName: "richColors", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, icons: { classPropertyName: "icons", publicName: "icons", isSignal: true, isRequired: false, transformFunction: null }, toastOptions: { classPropertyName: "toastOptions", publicName: "toastOptions", isSignal: true, isRequired: false, transformFunction: null }, closeButton: { classPropertyName: "closeButton", publicName: "closeButton", isSignal: true, isRequired: false, transformFunction: null }, accessibilityLabels: { classPropertyName: "accessibilityLabels", publicName: "accessibilityLabels", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1312
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: BetterToaster, isStandalone: true, selector: "better-toaster", inputs: { durationMs: { classPropertyName: "durationMs", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, stacked: { classPropertyName: "stacked", publicName: "stacked", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, mobileOffset: { classPropertyName: "mobileOffset", publicName: "mobileOffset", isSignal: true, isRequired: false, transformFunction: null }, richColors: { classPropertyName: "richColors", publicName: "richColors", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, icons: { classPropertyName: "icons", publicName: "icons", isSignal: true, isRequired: false, transformFunction: null }, toastOptions: { classPropertyName: "toastOptions", publicName: "toastOptions", isSignal: true, isRequired: false, transformFunction: null }, closeButton: { classPropertyName: "closeButton", publicName: "closeButton", isSignal: true, isRequired: false, transformFunction: null }, accessibilityLabels: { classPropertyName: "accessibilityLabels", publicName: "accessibilityLabels", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:pointerdown": "onDocumentPointerDown($event)" } }, ngImport: i0, template: `
1260
1313
  <section
1261
1314
  [attr.aria-label]="notificationsRegionAriaLabel()"
1262
1315
  tabindex="-1"
@@ -1266,18 +1319,24 @@ class BetterToaster {
1266
1319
  >
1267
1320
  <ol
1268
1321
  class="toast-container"
1269
- [style.--toast-offset-top]="offsetTop()"
1270
- [style.--toast-offset-right]="offsetRight()"
1271
- [style.--toast-offset-bottom]="offsetBottom()"
1272
- [style.--toast-offset-left]="offsetLeft()"
1273
- [style.--toast-offset-mobile-top]="mobileOffsetTop()"
1274
- [style.--toast-offset-mobile-right]="mobileOffsetRight()"
1275
- [style.--toast-offset-mobile-bottom]="mobileOffsetBottom()"
1276
- [style.--toast-offset-mobile-left]="mobileOffsetLeft()"
1322
+ [style.--toast-offset-top]="offsetSide(offset(), 'top')"
1323
+ [style.--toast-offset-right]="offsetSide(offset(), 'right')"
1324
+ [style.--toast-offset-bottom]="offsetSide(offset(), 'bottom')"
1325
+ [style.--toast-offset-left]="offsetSide(offset(), 'left')"
1326
+ [style.--toast-offset-mobile-top]="offsetSide(mobileOffset(), 'top')"
1327
+ [style.--toast-offset-mobile-right]="offsetSide(mobileOffset(), 'right')"
1328
+ [style.--toast-offset-mobile-bottom]="offsetSide(mobileOffset(), 'bottom')"
1329
+ [style.--toast-offset-mobile-left]="offsetSide(mobileOffset(), 'left')"
1330
+ [style.--toast-gap]="stackGapPx + 'px'"
1331
+ [style.--front-toast-height]="frontToastHeightCss()"
1277
1332
  [attr.data-position]="position()"
1278
1333
  [attr.data-rich-colors]="richColors()"
1279
1334
  [attr.data-theme]="theme()"
1335
+ [attr.data-stacked]="stacked() ? 'true' : 'false'"
1336
+ [attr.data-expanded]="layoutExpanded() ? 'true' : 'false'"
1280
1337
  tabindex="-1"
1338
+ (focusin)="onStackFocusIn($event)"
1339
+ (focusout)="onStackFocusOut($event)"
1281
1340
  >
1282
1341
  @for (toast of toaster.toasts(); track toast.id) {
1283
1342
  <li
@@ -1287,23 +1346,33 @@ class BetterToaster {
1287
1346
  [toasterClassNames]="toastOptions()?.classNames"
1288
1347
  [variant]="toast.variant"
1289
1348
  [customIcons]="icons()"
1290
- [offset]="offsets()[toast.id]"
1291
1349
  [closeButton]="closeButton()"
1292
1350
  [dismissButtonAriaLabel]="dismissButtonAriaLabel()"
1293
1351
  [stackPosition]="position()"
1352
+ [stackExpanded]="layoutExpanded()"
1353
+ [stackFront]="$last"
1294
1354
  [theme]="theme()"
1295
1355
  (heightChange)="onHeightChange(toast.id, $event)"
1356
+ (stackHover)="onItemStackHover($event)"
1357
+ [style.--index]="$count - 1 - $index"
1358
+ [style.--offset]="toastOffsetCss(toast.id)"
1296
1359
  [attr.data-position]="position()"
1297
1360
  [attr.data-rich-colors]="richColors()"
1361
+ [attr.data-expanded]="layoutExpanded() ? 'true' : 'false'"
1362
+ [attr.data-front]="$last ? 'true' : null"
1363
+ [attr.aria-hidden]="hiddenToastIds().has(toast.id) ? 'true' : null"
1364
+ [attr.inert]="hiddenToastIds().has(toast.id) ? true : null"
1298
1365
  ></li>
1299
1366
  }
1300
1367
  </ol>
1301
1368
  </section>
1302
- `, isInline: true, styles: [":host{--toast-width: 356px;--toast-gap: 1rem;--toast-z-index: 99999;--toast-offset-top: 24px;--toast-offset-bottom: 24px;--toast-offset-left: 24px;--toast-offset-right: 24px;--toast-offset-mobile-top: 16px;--toast-offset-mobile-bottom: 16px;--toast-offset-mobile-left: 16px;--toast-offset-mobile-right: 16px}.toast-container[data-theme=light]{color-scheme:light}.toast-container[data-theme=dark]{color-scheme:dark}.toast-container[data-theme=system]{color-scheme:light dark}.toast-container{--toast-bg: light-dark(oklch(100% 0 0), oklch(10% 0 0));--toast-color: light-dark(oklch(30% 0 0), oklch(98% 0 0));--toast-border-color: light-dark(hsl(0, 0%, 93%), hsl(0, 0%, 20%));--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1);--toast-success-bg: light-dark(oklch(97.9% .021 166.113), oklch(26.2% .051 172.552));--toast-success-text: light-dark(oklch(59.6% .145 163.225), oklch(92.5% .084 155.995));--toast-success-border: light-dark( oklch(69.6% .17 162.48 / .1), oklch(92.5% .084 155.995 / .1) );--toast-error-bg: light-dark(oklch(97.5% .012 25), oklch(25.8% .092 26.042));--toast-error-text: light-dark(oklch(57.7% .245 27.325), oklch(88.5% .062 18.334));--toast-error-border: light-dark( oklch(80.8% .114 19.571 / .1), oklch(88.5% .062 18.334 / .125) );--toast-info-bg: light-dark(oklch(97.7% .013 236.62), oklch(29.3% .066 243.157));--toast-info-text: light-dark(oklch(68.5% .169 237.323), oklch(90.1% .058 230.902));--toast-info-border: light-dark( oklch(82.8% .111 230.318 / .15), oklch(90.1% .058 230.902 / .125) );--toast-warning-bg: light-dark(oklch(98.7% .022 95.277), oklch(28.6% .066 53.813));--toast-warning-text: light-dark(oklch(66.6% .179 58.318), oklch(92.4% .12 95.746));--toast-warning-border: light-dark( oklch(76.9% .188 70.08 / .15), oklch(92.4% .12 95.746 / .125) );--toast-action-bg: light-dark(var(--color-black), var(--color-white));--toast-action-text: light-dark(var(--color-white), var(--color-black));--toast-action-border: light-dark(var(--color-black), var(--color-white));--toast-cancel-bg: light-dark(oklch(86.9% .005 56.366 / .3), oklch(26.8% .007 34.298 / .85));--toast-cancel-text: var(--toast-color);--toast-cancel-border: light-dark( oklch(86.9% .005 56.366 / .1), oklch(26.8% .007 34.298 / .5) );position:fixed;z-index:var(--toast-z-index);width:auto;pointer-events:none;overflow:visible;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.toast-container[data-theme=dark]{--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1)}@media(prefers-color-scheme:dark){.toast-container[data-theme=system]{--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1)}}.toast-container[data-position^=top]{top:var(--toast-offset-mobile-top);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}.toast-container[data-position^=bottom]{bottom:var(--toast-offset-mobile-bottom);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}@media(width>=40rem){.toast-container{width:var(--toast-width)}.toast-container[data-position=top-left]{top:var(--toast-offset-top);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=top-center]{top:var(--toast-offset-top);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=top-right]{top:var(--toast-offset-top);right:var(--toast-offset-right);left:auto;align-items:flex-end}.toast-container[data-position=bottom-left]{bottom:var(--toast-offset-bottom);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=bottom-center]{bottom:var(--toast-offset-bottom);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=bottom-right]{bottom:var(--toast-offset-bottom);right:var(--toast-offset-right);left:auto;align-items:flex-end}}\n"], dependencies: [{ kind: "component", type: BetterToastItem, selector: "li[betterToastItem]", inputs: ["toast", "toasterStyle", "toasterClassNames", "variant", "offset", "closeButton", "customIcons", "dismissButtonAriaLabel", "stackPosition", "theme"], outputs: ["heightChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1369
+ `, isInline: true, styles: [":host{--toast-width: 356px;--toast-gap: 16px;--toast-z-index: 99999;--toast-offset-top: 24px;--toast-offset-bottom: 24px;--toast-offset-left: 24px;--toast-offset-right: 24px;--toast-offset-mobile-top: 16px;--toast-offset-mobile-bottom: 16px;--toast-offset-mobile-left: 16px;--toast-offset-mobile-right: 16px}.toast-container[data-theme=light]{color-scheme:light}.toast-container[data-theme=dark]{color-scheme:dark}.toast-container[data-theme=system]{color-scheme:light dark}.toast-container{--toast-bg: light-dark(oklch(100% 0 0), oklch(10% 0 0));--toast-color: light-dark(oklch(30% 0 0), oklch(98% 0 0));--toast-border-color: light-dark(hsl(0, 0%, 93%), hsl(0, 0%, 20%));--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1);--toast-success-bg: light-dark(oklch(97.9% .021 166.113), oklch(26.2% .051 172.552));--toast-success-text: light-dark(oklch(59.6% .145 163.225), oklch(92.5% .084 155.995));--toast-success-border: light-dark( oklch(69.6% .17 162.48 / .1), oklch(92.5% .084 155.995 / .1) );--toast-error-bg: light-dark(oklch(97.5% .012 25), oklch(25.8% .092 26.042));--toast-error-text: light-dark(oklch(57.7% .245 27.325), oklch(88.5% .062 18.334));--toast-error-border: light-dark( oklch(80.8% .114 19.571 / .1), oklch(88.5% .062 18.334 / .125) );--toast-info-bg: light-dark(oklch(97.7% .013 236.62), oklch(29.3% .066 243.157));--toast-info-text: light-dark(oklch(68.5% .169 237.323), oklch(90.1% .058 230.902));--toast-info-border: light-dark( oklch(82.8% .111 230.318 / .15), oklch(90.1% .058 230.902 / .125) );--toast-warning-bg: light-dark(oklch(98.7% .022 95.277), oklch(28.6% .066 53.813));--toast-warning-text: light-dark(oklch(66.6% .179 58.318), oklch(92.4% .12 95.746));--toast-warning-border: light-dark( oklch(76.9% .188 70.08 / .15), oklch(92.4% .12 95.746 / .125) );--toast-action-bg: light-dark(var(--color-black), var(--color-white));--toast-action-text: light-dark(var(--color-white), var(--color-black));--toast-action-border: light-dark(var(--color-black), var(--color-white));--toast-cancel-bg: light-dark(oklch(86.9% .005 56.366 / .3), oklch(26.8% .007 34.298 / .85));--toast-cancel-border: light-dark( oklch(86.9% .005 56.366 / .1), oklch(26.8% .007 34.298 / .5) );position:fixed;z-index:var(--toast-z-index);width:auto;pointer-events:none;overflow:visible;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.toast-container[data-position^=top]{top:var(--toast-offset-mobile-top);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}.toast-container[data-position^=bottom]{bottom:var(--toast-offset-mobile-bottom);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}@media(width>=40rem){.toast-container{width:var(--toast-width)}.toast-container[data-position=top-left]{top:var(--toast-offset-top);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=top-center]{top:var(--toast-offset-top);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=top-right]{top:var(--toast-offset-top);right:var(--toast-offset-right);left:auto;align-items:flex-end}.toast-container[data-position=bottom-left]{bottom:var(--toast-offset-bottom);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=bottom-center]{bottom:var(--toast-offset-bottom);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=bottom-right]{bottom:var(--toast-offset-bottom);right:var(--toast-offset-right);left:auto;align-items:flex-end}}\n"], dependencies: [{ kind: "component", type: BetterToastItem, selector: "li[betterToastItem]", inputs: ["toast", "toasterStyle", "toasterClassNames", "variant", "closeButton", "customIcons", "dismissButtonAriaLabel", "stackPosition", "stackExpanded", "stackFront", "theme"], outputs: ["stackHover", "heightChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1303
1370
  }
1304
1371
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: BetterToaster, decorators: [{
1305
1372
  type: Component,
1306
- args: [{ selector: 'better-toaster', changeDetection: ChangeDetectionStrategy.OnPush, imports: [BetterToastItem], template: `
1373
+ args: [{ selector: 'better-toaster', changeDetection: ChangeDetectionStrategy.OnPush, imports: [BetterToastItem], host: {
1374
+ '(document:pointerdown)': 'onDocumentPointerDown($event)',
1375
+ }, template: `
1307
1376
  <section
1308
1377
  [attr.aria-label]="notificationsRegionAriaLabel()"
1309
1378
  tabindex="-1"
@@ -1313,18 +1382,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
1313
1382
  >
1314
1383
  <ol
1315
1384
  class="toast-container"
1316
- [style.--toast-offset-top]="offsetTop()"
1317
- [style.--toast-offset-right]="offsetRight()"
1318
- [style.--toast-offset-bottom]="offsetBottom()"
1319
- [style.--toast-offset-left]="offsetLeft()"
1320
- [style.--toast-offset-mobile-top]="mobileOffsetTop()"
1321
- [style.--toast-offset-mobile-right]="mobileOffsetRight()"
1322
- [style.--toast-offset-mobile-bottom]="mobileOffsetBottom()"
1323
- [style.--toast-offset-mobile-left]="mobileOffsetLeft()"
1385
+ [style.--toast-offset-top]="offsetSide(offset(), 'top')"
1386
+ [style.--toast-offset-right]="offsetSide(offset(), 'right')"
1387
+ [style.--toast-offset-bottom]="offsetSide(offset(), 'bottom')"
1388
+ [style.--toast-offset-left]="offsetSide(offset(), 'left')"
1389
+ [style.--toast-offset-mobile-top]="offsetSide(mobileOffset(), 'top')"
1390
+ [style.--toast-offset-mobile-right]="offsetSide(mobileOffset(), 'right')"
1391
+ [style.--toast-offset-mobile-bottom]="offsetSide(mobileOffset(), 'bottom')"
1392
+ [style.--toast-offset-mobile-left]="offsetSide(mobileOffset(), 'left')"
1393
+ [style.--toast-gap]="stackGapPx + 'px'"
1394
+ [style.--front-toast-height]="frontToastHeightCss()"
1324
1395
  [attr.data-position]="position()"
1325
1396
  [attr.data-rich-colors]="richColors()"
1326
1397
  [attr.data-theme]="theme()"
1398
+ [attr.data-stacked]="stacked() ? 'true' : 'false'"
1399
+ [attr.data-expanded]="layoutExpanded() ? 'true' : 'false'"
1327
1400
  tabindex="-1"
1401
+ (focusin)="onStackFocusIn($event)"
1402
+ (focusout)="onStackFocusOut($event)"
1328
1403
  >
1329
1404
  @for (toast of toaster.toasts(); track toast.id) {
1330
1405
  <li
@@ -1334,20 +1409,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
1334
1409
  [toasterClassNames]="toastOptions()?.classNames"
1335
1410
  [variant]="toast.variant"
1336
1411
  [customIcons]="icons()"
1337
- [offset]="offsets()[toast.id]"
1338
1412
  [closeButton]="closeButton()"
1339
1413
  [dismissButtonAriaLabel]="dismissButtonAriaLabel()"
1340
1414
  [stackPosition]="position()"
1415
+ [stackExpanded]="layoutExpanded()"
1416
+ [stackFront]="$last"
1341
1417
  [theme]="theme()"
1342
1418
  (heightChange)="onHeightChange(toast.id, $event)"
1419
+ (stackHover)="onItemStackHover($event)"
1420
+ [style.--index]="$count - 1 - $index"
1421
+ [style.--offset]="toastOffsetCss(toast.id)"
1343
1422
  [attr.data-position]="position()"
1344
1423
  [attr.data-rich-colors]="richColors()"
1424
+ [attr.data-expanded]="layoutExpanded() ? 'true' : 'false'"
1425
+ [attr.data-front]="$last ? 'true' : null"
1426
+ [attr.aria-hidden]="hiddenToastIds().has(toast.id) ? 'true' : null"
1427
+ [attr.inert]="hiddenToastIds().has(toast.id) ? true : null"
1345
1428
  ></li>
1346
1429
  }
1347
1430
  </ol>
1348
1431
  </section>
1349
- `, styles: [":host{--toast-width: 356px;--toast-gap: 1rem;--toast-z-index: 99999;--toast-offset-top: 24px;--toast-offset-bottom: 24px;--toast-offset-left: 24px;--toast-offset-right: 24px;--toast-offset-mobile-top: 16px;--toast-offset-mobile-bottom: 16px;--toast-offset-mobile-left: 16px;--toast-offset-mobile-right: 16px}.toast-container[data-theme=light]{color-scheme:light}.toast-container[data-theme=dark]{color-scheme:dark}.toast-container[data-theme=system]{color-scheme:light dark}.toast-container{--toast-bg: light-dark(oklch(100% 0 0), oklch(10% 0 0));--toast-color: light-dark(oklch(30% 0 0), oklch(98% 0 0));--toast-border-color: light-dark(hsl(0, 0%, 93%), hsl(0, 0%, 20%));--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1);--toast-success-bg: light-dark(oklch(97.9% .021 166.113), oklch(26.2% .051 172.552));--toast-success-text: light-dark(oklch(59.6% .145 163.225), oklch(92.5% .084 155.995));--toast-success-border: light-dark( oklch(69.6% .17 162.48 / .1), oklch(92.5% .084 155.995 / .1) );--toast-error-bg: light-dark(oklch(97.5% .012 25), oklch(25.8% .092 26.042));--toast-error-text: light-dark(oklch(57.7% .245 27.325), oklch(88.5% .062 18.334));--toast-error-border: light-dark( oklch(80.8% .114 19.571 / .1), oklch(88.5% .062 18.334 / .125) );--toast-info-bg: light-dark(oklch(97.7% .013 236.62), oklch(29.3% .066 243.157));--toast-info-text: light-dark(oklch(68.5% .169 237.323), oklch(90.1% .058 230.902));--toast-info-border: light-dark( oklch(82.8% .111 230.318 / .15), oklch(90.1% .058 230.902 / .125) );--toast-warning-bg: light-dark(oklch(98.7% .022 95.277), oklch(28.6% .066 53.813));--toast-warning-text: light-dark(oklch(66.6% .179 58.318), oklch(92.4% .12 95.746));--toast-warning-border: light-dark( oklch(76.9% .188 70.08 / .15), oklch(92.4% .12 95.746 / .125) );--toast-action-bg: light-dark(var(--color-black), var(--color-white));--toast-action-text: light-dark(var(--color-white), var(--color-black));--toast-action-border: light-dark(var(--color-black), var(--color-white));--toast-cancel-bg: light-dark(oklch(86.9% .005 56.366 / .3), oklch(26.8% .007 34.298 / .85));--toast-cancel-text: var(--toast-color);--toast-cancel-border: light-dark( oklch(86.9% .005 56.366 / .1), oklch(26.8% .007 34.298 / .5) );position:fixed;z-index:var(--toast-z-index);width:auto;pointer-events:none;overflow:visible;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.toast-container[data-theme=dark]{--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1)}@media(prefers-color-scheme:dark){.toast-container[data-theme=system]{--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1)}}.toast-container[data-position^=top]{top:var(--toast-offset-mobile-top);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}.toast-container[data-position^=bottom]{bottom:var(--toast-offset-mobile-bottom);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}@media(width>=40rem){.toast-container{width:var(--toast-width)}.toast-container[data-position=top-left]{top:var(--toast-offset-top);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=top-center]{top:var(--toast-offset-top);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=top-right]{top:var(--toast-offset-top);right:var(--toast-offset-right);left:auto;align-items:flex-end}.toast-container[data-position=bottom-left]{bottom:var(--toast-offset-bottom);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=bottom-center]{bottom:var(--toast-offset-bottom);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=bottom-right]{bottom:var(--toast-offset-bottom);right:var(--toast-offset-right);left:auto;align-items:flex-end}}\n"] }]
1350
- }], propDecorators: { durationMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "duration", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], mobileOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileOffset", required: false }] }], richColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "richColors", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], icons: [{ type: i0.Input, args: [{ isSignal: true, alias: "icons", required: false }] }], toastOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "toastOptions", required: false }] }], closeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeButton", required: false }] }], accessibilityLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "accessibilityLabels", required: false }] }] } });
1432
+ `, styles: [":host{--toast-width: 356px;--toast-gap: 16px;--toast-z-index: 99999;--toast-offset-top: 24px;--toast-offset-bottom: 24px;--toast-offset-left: 24px;--toast-offset-right: 24px;--toast-offset-mobile-top: 16px;--toast-offset-mobile-bottom: 16px;--toast-offset-mobile-left: 16px;--toast-offset-mobile-right: 16px}.toast-container[data-theme=light]{color-scheme:light}.toast-container[data-theme=dark]{color-scheme:dark}.toast-container[data-theme=system]{color-scheme:light dark}.toast-container{--toast-bg: light-dark(oklch(100% 0 0), oklch(10% 0 0));--toast-color: light-dark(oklch(30% 0 0), oklch(98% 0 0));--toast-border-color: light-dark(hsl(0, 0%, 93%), hsl(0, 0%, 20%));--toast-shadow: 0px 4px 12px rgba(0, 0, 0, .1);--toast-success-bg: light-dark(oklch(97.9% .021 166.113), oklch(26.2% .051 172.552));--toast-success-text: light-dark(oklch(59.6% .145 163.225), oklch(92.5% .084 155.995));--toast-success-border: light-dark( oklch(69.6% .17 162.48 / .1), oklch(92.5% .084 155.995 / .1) );--toast-error-bg: light-dark(oklch(97.5% .012 25), oklch(25.8% .092 26.042));--toast-error-text: light-dark(oklch(57.7% .245 27.325), oklch(88.5% .062 18.334));--toast-error-border: light-dark( oklch(80.8% .114 19.571 / .1), oklch(88.5% .062 18.334 / .125) );--toast-info-bg: light-dark(oklch(97.7% .013 236.62), oklch(29.3% .066 243.157));--toast-info-text: light-dark(oklch(68.5% .169 237.323), oklch(90.1% .058 230.902));--toast-info-border: light-dark( oklch(82.8% .111 230.318 / .15), oklch(90.1% .058 230.902 / .125) );--toast-warning-bg: light-dark(oklch(98.7% .022 95.277), oklch(28.6% .066 53.813));--toast-warning-text: light-dark(oklch(66.6% .179 58.318), oklch(92.4% .12 95.746));--toast-warning-border: light-dark( oklch(76.9% .188 70.08 / .15), oklch(92.4% .12 95.746 / .125) );--toast-action-bg: light-dark(var(--color-black), var(--color-white));--toast-action-text: light-dark(var(--color-white), var(--color-black));--toast-action-border: light-dark(var(--color-black), var(--color-white));--toast-cancel-bg: light-dark(oklch(86.9% .005 56.366 / .3), oklch(26.8% .007 34.298 / .85));--toast-cancel-border: light-dark( oklch(86.9% .005 56.366 / .1), oklch(26.8% .007 34.298 / .5) );position:fixed;z-index:var(--toast-z-index);width:auto;pointer-events:none;overflow:visible;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.toast-container[data-position^=top]{top:var(--toast-offset-mobile-top);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}.toast-container[data-position^=bottom]{bottom:var(--toast-offset-mobile-bottom);left:var(--toast-offset-mobile-left);right:var(--toast-offset-mobile-right)}@media(width>=40rem){.toast-container{width:var(--toast-width)}.toast-container[data-position=top-left]{top:var(--toast-offset-top);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=top-center]{top:var(--toast-offset-top);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=top-right]{top:var(--toast-offset-top);right:var(--toast-offset-right);left:auto;align-items:flex-end}.toast-container[data-position=bottom-left]{bottom:var(--toast-offset-bottom);left:var(--toast-offset-left);right:auto;align-items:flex-start}.toast-container[data-position=bottom-center]{bottom:var(--toast-offset-bottom);left:50%;transform:translate(-50%);align-items:center}.toast-container[data-position=bottom-right]{bottom:var(--toast-offset-bottom);right:var(--toast-offset-right);left:auto;align-items:flex-end}}\n"] }]
1433
+ }], ctorParameters: () => [], propDecorators: { durationMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "duration", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], stacked: [{ type: i0.Input, args: [{ isSignal: true, alias: "stacked", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], mobileOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileOffset", required: false }] }], richColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "richColors", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], icons: [{ type: i0.Input, args: [{ isSignal: true, alias: "icons", required: false }] }], toastOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "toastOptions", required: false }] }], closeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeButton", required: false }] }], accessibilityLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "accessibilityLabels", required: false }] }] } });
1351
1434
 
1352
1435
  /*
1353
1436
  * Public API Surface of better-toast