@forgeroute/ui 0.4.69 → 0.4.70

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.
@@ -7886,66 +7886,154 @@ i0.ɵɵngDeclareClassMetadata({
7886
7886
  }
7887
7887
  });
7888
7888
  const MIN_RESUME_MS = 1e3;
7889
+ const QUEUE_GAP_MS = 80;
7890
+ const COUNTDOWN_TICK_MS = 200;
7891
+ const DEFAULT_MAX_QUEUED = 20;
7889
7892
  var ToastService = class ToastService {
7890
7893
  constructor() {
7891
7894
  this.message = signal(null, ...ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []);
7892
7895
  this.type = signal("success", ...ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []);
7893
7896
  this.dismissible = signal(false, ...ngDevMode ? [{ debugName: "dismissible" }] : /* istanbul ignore next */ []);
7894
7897
  this.persistentLiveRegion = signal(false, ...ngDevMode ? [{ debugName: "persistentLiveRegion" }] : /* istanbul ignore next */ []);
7898
+ this.details = signal(null, ...ngDevMode ? [{ debugName: "details" }] : /* istanbul ignore next */ []);
7899
+ this.queuedCount = signal(0, ...ngDevMode ? [{ debugName: "queuedCount" }] : /* istanbul ignore next */ []);
7900
+ this.queueDefault = signal(false, ...ngDevMode ? [{ debugName: "queueDefault" }] : /* istanbul ignore next */ []);
7901
+ this.countdownDefault = signal(false, ...ngDevMode ? [{ debugName: "countdownDefault" }] : /* istanbul ignore next */ []);
7902
+ this.maxQueued = signal(DEFAULT_MAX_QUEUED, ...ngDevMode ? [{ debugName: "maxQueued" }] : /* istanbul ignore next */ []);
7903
+ this.remainingMs = signal(0, ...ngDevMode ? [{ debugName: "remainingMs" }] : /* istanbul ignore next */ []);
7904
+ this.totalMs = signal(0, ...ngDevMode ? [{ debugName: "totalMs" }] : /* istanbul ignore next */ []);
7905
+ this.action$ = new Subject();
7895
7906
  this._timer = null;
7907
+ this._ticker = null;
7908
+ this._advanceTimer = null;
7896
7909
  this._deadline = 0;
7897
7910
  this._remaining = 0;
7898
7911
  this._paused = false;
7912
+ this._queue = [];
7899
7913
  }
7900
- show(msg, duration = 3e3) {
7901
- this._display("success", msg, duration);
7914
+ show(msg, durationOrOptions = 3e3) {
7915
+ this._enter("success", msg, 3e3, durationOrOptions);
7902
7916
  }
7903
- success(msg, duration = 3e3) {
7904
- this._display("success", msg, duration);
7917
+ success(msg, durationOrOptions = 3e3) {
7918
+ this._enter("success", msg, 3e3, durationOrOptions);
7905
7919
  }
7906
- info(msg, duration = 3e3) {
7907
- this._display("info", msg, duration);
7920
+ info(msg, durationOrOptions = 3e3) {
7921
+ this._enter("info", msg, 3e3, durationOrOptions);
7908
7922
  }
7909
- error(msg, duration = 4e3) {
7910
- this._display("error", msg, duration);
7923
+ error(msg, durationOrOptions = 4e3) {
7924
+ this._enter("error", msg, 4e3, durationOrOptions);
7925
+ }
7926
+ notify(msg, options = {}) {
7927
+ const type = options.type ?? "success";
7928
+ this._enter(type, msg, type === "error" ? 4e3 : 3e3, options);
7911
7929
  }
7912
7930
  dismiss() {
7913
- this._clearTimer();
7914
- this._paused = false;
7915
- this.message.set(null);
7931
+ this._finish();
7932
+ }
7933
+ runAction(id) {
7934
+ const d = this.details();
7935
+ const a = d?.actions.find((x) => x.id === id);
7936
+ if (!d || !a || a.disabled) return;
7937
+ d.onAction?.(id);
7938
+ this.action$.next({
7939
+ id,
7940
+ key: d.key
7941
+ });
7942
+ if (!a.keepOpen) this._finish();
7916
7943
  }
7917
7944
  pause() {
7918
7945
  if (this._paused || !this._timer || this.message() === null) return;
7919
7946
  this._remaining = Math.max(0, this._deadline - Date.now());
7947
+ this.remainingMs.set(this._remaining);
7920
7948
  this._clearTimer();
7921
7949
  this._paused = true;
7922
7950
  }
7923
7951
  resume() {
7924
7952
  if (!this._paused) return;
7925
7953
  this._paused = false;
7926
- this._start(Math.max(this._remaining, MIN_RESUME_MS));
7954
+ this._start(Math.max(this._remaining, MIN_RESUME_MS), false);
7927
7955
  }
7928
7956
  ngOnDestroy() {
7929
7957
  this._clearTimer();
7958
+ this._clearTicker();
7959
+ this._clearAdvance();
7960
+ this._queue = [];
7930
7961
  this._paused = false;
7962
+ this.action$.complete();
7931
7963
  }
7932
7964
  _clearTimer() {
7933
7965
  if (this._timer) clearTimeout(this._timer);
7934
7966
  this._timer = null;
7935
7967
  }
7936
- _start(duration) {
7968
+ _clearTicker() {
7969
+ if (this._ticker) clearInterval(this._ticker);
7970
+ this._ticker = null;
7971
+ }
7972
+ _clearAdvance() {
7973
+ if (this._advanceTimer) clearTimeout(this._advanceTimer);
7974
+ this._advanceTimer = null;
7975
+ }
7976
+ _start(duration, fresh) {
7937
7977
  this._deadline = Date.now() + duration;
7938
- this._timer = setTimeout(() => {
7939
- this.message.set(null);
7940
- this._timer = null;
7941
- }, duration);
7978
+ this._timer = setTimeout(() => this._finish(), duration);
7979
+ if (fresh) this.totalMs.set(duration);
7980
+ this.remainingMs.set(duration);
7981
+ if (this.details()?.countdown && !this._ticker) this._ticker = setInterval(() => {
7982
+ if (!this._paused && this._timer) this.remainingMs.set(Math.max(0, this._deadline - Date.now()));
7983
+ }, COUNTDOWN_TICK_MS);
7984
+ }
7985
+ _finish() {
7986
+ this._clearTimer();
7987
+ this._clearTicker();
7988
+ this._paused = false;
7989
+ this.message.set(null);
7990
+ this.details.set(null);
7991
+ this.remainingMs.set(0);
7992
+ if (this._queue.length > 0 && !this._advanceTimer) this._advanceTimer = setTimeout(() => {
7993
+ this._advanceTimer = null;
7994
+ const next = this._queue.shift();
7995
+ this.queuedCount.set(this._queue.length);
7996
+ if (next) this._present(next);
7997
+ }, QUEUE_GAP_MS);
7998
+ }
7999
+ _enter(type, msg, defaultDuration, arg) {
8000
+ const options = typeof arg === "number" ? { duration: arg } : arg ?? {};
8001
+ const hasRich = !!(options.title || options.actions?.length);
8002
+ const countdown = options.countdown ?? this.countdownDefault();
8003
+ const details = hasRich || countdown || options.key !== void 0 || !!options.onAction ? {
8004
+ title: options.title,
8005
+ actions: options.actions ?? [],
8006
+ countdown,
8007
+ key: options.key,
8008
+ onAction: options.onAction
8009
+ } : null;
8010
+ const pending = {
8011
+ type,
8012
+ message: msg,
8013
+ duration: options.duration ?? defaultDuration,
8014
+ details
8015
+ };
8016
+ const queue = options.queue ?? this.queueDefault();
8017
+ const busy = this.message() !== null || this._advanceTimer !== null;
8018
+ if (queue && busy) {
8019
+ const key = options.key;
8020
+ if (key !== void 0 && (this.details()?.key === key || this._queue.some((q) => q.details?.key === key))) return;
8021
+ if (this._queue.length >= this.maxQueued()) return;
8022
+ this._queue.push(pending);
8023
+ this.queuedCount.set(this._queue.length);
8024
+ return;
8025
+ }
8026
+ this._clearAdvance();
8027
+ this._present(pending);
7942
8028
  }
7943
- _display(type, msg, duration) {
8029
+ _present(p) {
7944
8030
  this._clearTimer();
8031
+ this._clearTicker();
7945
8032
  this._paused = false;
7946
- this.type.set(type);
7947
- this.message.set(msg);
7948
- this._start(duration);
8033
+ this.type.set(p.type);
8034
+ this.details.set(p.details);
8035
+ this.message.set(p.message);
8036
+ this._start(p.duration, true);
7949
8037
  }
7950
8038
  static {
7951
8039
  this.ɵfac = i0.ɵɵngDeclareFactory({
@@ -8019,6 +8107,9 @@ i0.ɵɵngDeclareClassMetadata({
8019
8107
  }]
8020
8108
  });
8021
8109
  var ToastComponent = class ToastComponent {
8110
+ onAction(id) {
8111
+ this.toast.runAction(id);
8112
+ }
8022
8113
  onEscape(e) {
8023
8114
  e.stopPropagation();
8024
8115
  this.toast.dismiss();
@@ -8049,6 +8140,22 @@ var ToastComponent = class ToastComponent {
8049
8140
  this.showClose = computed(() => this.dismissible() ?? this.toast.dismissible(), ...ngDevMode ? [{ debugName: "showClose" }] : /* istanbul ignore next */ []);
8050
8141
  this.persistentLiveRegion = input(null, ...ngDevMode ? [{ debugName: "persistentLiveRegion" }] : /* istanbul ignore next */ []);
8051
8142
  this.persistent = computed(() => this.persistentLiveRegion() ?? this.toast.persistentLiveRegion(), ...ngDevMode ? [{ debugName: "persistent" }] : /* istanbul ignore next */ []);
8143
+ this.queue = input(null, ...ngDevMode ? [{ debugName: "queue" }] : /* istanbul ignore next */ []);
8144
+ this.countdown = input(null, ...ngDevMode ? [{ debugName: "countdown" }] : /* istanbul ignore next */ []);
8145
+ this.maxQueued = input(null, ...ngDevMode ? [{ debugName: "maxQueued" }] : /* istanbul ignore next */ []);
8146
+ this.countdownLabel = input("Dismissing in {n}s", ...ngDevMode ? [{ debugName: "countdownLabel" }] : /* istanbul ignore next */ []);
8147
+ this.queueLabel = input("+{n} more", ...ngDevMode ? [{ debugName: "queueLabel" }] : /* istanbul ignore next */ []);
8148
+ this.action = output();
8149
+ this.isRich = computed(() => {
8150
+ const d = this.toast.details();
8151
+ return !!d && (!!d.title || d.actions.length > 0 || d.countdown);
8152
+ }, ...ngDevMode ? [{ debugName: "isRich" }] : /* istanbul ignore next */ []);
8153
+ this.countdownPct = computed(() => {
8154
+ const total = this.toast.totalMs();
8155
+ return total > 0 ? Math.min(100, Math.max(0, this.toast.remainingMs() / total * 100)) : 0;
8156
+ }, ...ngDevMode ? [{ debugName: "countdownPct" }] : /* istanbul ignore next */ []);
8157
+ this.countdownText = computed(() => this.countdownLabel().replace("{n}", String(Math.ceil(this.toast.remainingMs() / 1e3))), ...ngDevMode ? [{ debugName: "countdownText" }] : /* istanbul ignore next */ []);
8158
+ this.queueText = computed(() => this.queueLabel().replace("{n}", String(this.toast.queuedCount())), ...ngDevMode ? [{ debugName: "queueText" }] : /* istanbul ignore next */ []);
8052
8159
  this._adoptedMessage = signal(null, ...ngDevMode ? [{ debugName: "_adoptedMessage" }] : /* istanbul ignore next */ []);
8053
8160
  this.adopted = computed(() => {
8054
8161
  const m = this._adoptedMessage();
@@ -8059,6 +8166,18 @@ var ToastComponent = class ToastComponent {
8059
8166
  this._focus = false;
8060
8167
  this._registry.register(this._id);
8061
8168
  inject(DestroyRef).onDestroy(() => this._registry.unregister(this._id));
8169
+ effect(() => {
8170
+ if (!this.isHost()) return;
8171
+ const q = this.queue(), c = this.countdown(), m = this.maxQueued();
8172
+ untracked(() => {
8173
+ if (q !== null) this.toast.queueDefault.set(q);
8174
+ if (c !== null) this.toast.countdownDefault.set(c);
8175
+ if (m !== null) this.toast.maxQueued.set(Math.max(0, Math.floor(m)));
8176
+ });
8177
+ });
8178
+ this.toast.action$.pipe(takeUntilDestroyed()).subscribe((e) => {
8179
+ if (this.isHost()) this.action.emit(e);
8180
+ });
8062
8181
  effect(() => {
8063
8182
  const host = this.isHost();
8064
8183
  const msg = this.toast.message();
@@ -8109,8 +8228,44 @@ var ToastComponent = class ToastComponent {
8109
8228
  isSignal: true,
8110
8229
  isRequired: false,
8111
8230
  transformFunction: null
8231
+ },
8232
+ queue: {
8233
+ classPropertyName: "queue",
8234
+ publicName: "queue",
8235
+ isSignal: true,
8236
+ isRequired: false,
8237
+ transformFunction: null
8238
+ },
8239
+ countdown: {
8240
+ classPropertyName: "countdown",
8241
+ publicName: "countdown",
8242
+ isSignal: true,
8243
+ isRequired: false,
8244
+ transformFunction: null
8245
+ },
8246
+ maxQueued: {
8247
+ classPropertyName: "maxQueued",
8248
+ publicName: "maxQueued",
8249
+ isSignal: true,
8250
+ isRequired: false,
8251
+ transformFunction: null
8252
+ },
8253
+ countdownLabel: {
8254
+ classPropertyName: "countdownLabel",
8255
+ publicName: "countdownLabel",
8256
+ isSignal: true,
8257
+ isRequired: false,
8258
+ transformFunction: null
8259
+ },
8260
+ queueLabel: {
8261
+ classPropertyName: "queueLabel",
8262
+ publicName: "queueLabel",
8263
+ isSignal: true,
8264
+ isRequired: false,
8265
+ transformFunction: null
8112
8266
  }
8113
8267
  },
8268
+ outputs: { action: "action" },
8114
8269
  ngImport: i0,
8115
8270
  template: `
8116
8271
  @if (isHost() && persistent()) {
@@ -8128,6 +8283,7 @@ var ToastComponent = class ToastComponent {
8128
8283
  <ng-template #card>
8129
8284
  <div
8130
8285
  class="t-toast"
8286
+ [class.t-rich]="isRich()"
8131
8287
  [class.t-error]="toast.type() === 'error'"
8132
8288
  [class.t-info]="toast.type() === 'info'"
8133
8289
  [class.t-adopted]="adopted()"
@@ -8149,7 +8305,14 @@ var ToastComponent = class ToastComponent {
8149
8305
  <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
8150
8306
  </svg>
8151
8307
  }
8152
- <span class="t-text">{{ toast.message() }}</span>
8308
+ @if (isRich()) {
8309
+ <span class="t-content">
8310
+ @if (toast.details()?.title; as title) { <span class="t-title" data-testid="toast-title">{{ title }}</span> }
8311
+ <span class="t-text">{{ toast.message() }}</span>
8312
+ </span>
8313
+ } @else {
8314
+ <span class="t-text">{{ toast.message() }}</span>
8315
+ }
8153
8316
  @if (showClose()) {
8154
8317
  <button type="button" class="t-close" aria-label="Dismiss notification" (click)="toast.dismiss()">
8155
8318
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
@@ -8157,11 +8320,31 @@ var ToastComponent = class ToastComponent {
8157
8320
  </svg>
8158
8321
  </button>
8159
8322
  }
8323
+ @if (toast.queuedCount() > 0) {
8324
+ <span class="t-queue" data-testid="toast-queue">{{ queueText() }}</span>
8325
+ }
8326
+ @if (toast.details(); as d) {
8327
+ @if (d.actions.length > 0) {
8328
+ <div class="t-actions" role="group" aria-label="Actions" data-testid="toast-actions">
8329
+ @for (a of d.actions; track a.id) {
8330
+ <button type="button" class="t-action"
8331
+ [class.t-action-primary]="a.variant === 'primary'" [class.t-action-danger]="a.variant === 'danger'"
8332
+ [disabled]="a.disabled" [attr.data-testid]="'toast-action-' + a.id" (click)="onAction(a.id)">{{ a.label }}</button>
8333
+ }
8334
+ </div>
8335
+ }
8336
+ @if (d.countdown) {
8337
+ <div class="t-countdown" aria-hidden="true">
8338
+ <div class="t-countdown-bar"><div class="t-countdown-fill" [style.width.%]="countdownPct()" data-testid="toast-countdown-fill"></div></div>
8339
+ <div class="t-countdown-label" data-testid="toast-countdown">{{ countdownText() }}</div>
8340
+ </div>
8341
+ }
8342
+ }
8160
8343
  </div>
8161
8344
  </ng-template>
8162
8345
  `,
8163
8346
  isInline: true,
8164
- styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"],
8347
+ styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}.t-rich{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:start;column-gap:8px;row-gap:10px;min-width:min(320px,calc(100vw - 32px))}.t-rich>svg:first-child{margin-top:2px}.t-content{display:flex;flex-direction:column;gap:2px;min-width:0}.t-title{font-weight:700}.t-queue{grid-column:1 / -1;justify-self:end;font-size:.75rem;font-weight:600;padding:1px 8px;border-radius:999px;background:#ffffff38}.t-actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:8px}.t-action{min-height:32px;padding:6px 14px;border-radius:8px;font:inherit;font-size:.8125rem;font-weight:600;cursor:pointer;color:#fff;background:#ffffff2e;border:1px solid rgba(255,255,255,.6)}.t-action:hover:not(:disabled){background:#ffffff4d}.t-action:focus-visible{outline:2px solid #fff;outline-offset:2px}.t-action:disabled{opacity:.5;cursor:not-allowed}.t-action-primary{background:#fff;color:#0f172a;border-color:#fff}.t-action-primary:hover:not(:disabled){background:#f1f5f9}.t-action-danger{background:#7f1d1d;border-color:#ffffffb3}.t-action-danger:hover:not(:disabled){background:#991b1b}.t-countdown{grid-column:1 / -1;display:flex;flex-direction:column;gap:4px}.t-countdown-bar{height:4px;border-radius:2px;background:#ffffff47;overflow:hidden}.t-countdown-fill{height:100%;background:#fff;transition:width .2s linear}.t-countdown-label{font-size:.75rem;opacity:.85;font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.t-countdown-fill{transition:none}}@media(pointer:coarse),(max-width:480px){.t-action{min-height:44px}}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"],
8165
8348
  dependencies: [{
8166
8349
  kind: "directive",
8167
8350
  type: NgTemplateOutlet,
@@ -8204,6 +8387,7 @@ i0.ɵɵngDeclareClassMetadata({
8204
8387
  <ng-template #card>
8205
8388
  <div
8206
8389
  class="t-toast"
8390
+ [class.t-rich]="isRich()"
8207
8391
  [class.t-error]="toast.type() === 'error'"
8208
8392
  [class.t-info]="toast.type() === 'info'"
8209
8393
  [class.t-adopted]="adopted()"
@@ -8225,7 +8409,14 @@ i0.ɵɵngDeclareClassMetadata({
8225
8409
  <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
8226
8410
  </svg>
8227
8411
  }
8228
- <span class="t-text">{{ toast.message() }}</span>
8412
+ @if (isRich()) {
8413
+ <span class="t-content">
8414
+ @if (toast.details()?.title; as title) { <span class="t-title" data-testid="toast-title">{{ title }}</span> }
8415
+ <span class="t-text">{{ toast.message() }}</span>
8416
+ </span>
8417
+ } @else {
8418
+ <span class="t-text">{{ toast.message() }}</span>
8419
+ }
8229
8420
  @if (showClose()) {
8230
8421
  <button type="button" class="t-close" aria-label="Dismiss notification" (click)="toast.dismiss()">
8231
8422
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
@@ -8233,10 +8424,30 @@ i0.ɵɵngDeclareClassMetadata({
8233
8424
  </svg>
8234
8425
  </button>
8235
8426
  }
8427
+ @if (toast.queuedCount() > 0) {
8428
+ <span class="t-queue" data-testid="toast-queue">{{ queueText() }}</span>
8429
+ }
8430
+ @if (toast.details(); as d) {
8431
+ @if (d.actions.length > 0) {
8432
+ <div class="t-actions" role="group" aria-label="Actions" data-testid="toast-actions">
8433
+ @for (a of d.actions; track a.id) {
8434
+ <button type="button" class="t-action"
8435
+ [class.t-action-primary]="a.variant === 'primary'" [class.t-action-danger]="a.variant === 'danger'"
8436
+ [disabled]="a.disabled" [attr.data-testid]="'toast-action-' + a.id" (click)="onAction(a.id)">{{ a.label }}</button>
8437
+ }
8438
+ </div>
8439
+ }
8440
+ @if (d.countdown) {
8441
+ <div class="t-countdown" aria-hidden="true">
8442
+ <div class="t-countdown-bar"><div class="t-countdown-fill" [style.width.%]="countdownPct()" data-testid="toast-countdown-fill"></div></div>
8443
+ <div class="t-countdown-label" data-testid="toast-countdown">{{ countdownText() }}</div>
8444
+ </div>
8445
+ }
8446
+ }
8236
8447
  </div>
8237
8448
  </ng-template>
8238
8449
  `,
8239
- styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"]
8450
+ styles: [".t-live{position:absolute;width:0;height:0;margin:0;padding:0;overflow:visible}.t-toast{position:fixed;bottom:max(24px,env(safe-area-inset-bottom,0px));left:50%;transform:translate(-50%);background:#047857;color:#fff;padding:11px 20px;border-radius:12px;font-size:.875rem;font-weight:500;z-index:9999;box-shadow:0 4px 16px #0000002e;animation:t-in .28s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px - env(safe-area-inset-left,0px) - env(safe-area-inset-right,0px));overflow-wrap:anywhere;word-break:break-word}.t-error{background:#dc2626}.t-info{background:#2563eb}.t-adopted{animation:none}.t-text{min-width:0}.t-close{position:relative;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -10px -4px 0;padding:0;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer}.t-close:after{content:\"\";position:absolute;inset:-8px}.t-close:hover{background:#fff3}.t-close:focus-visible{outline:2px solid #fff;outline-offset:1px}.t-rich{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:start;column-gap:8px;row-gap:10px;min-width:min(320px,calc(100vw - 32px))}.t-rich>svg:first-child{margin-top:2px}.t-content{display:flex;flex-direction:column;gap:2px;min-width:0}.t-title{font-weight:700}.t-queue{grid-column:1 / -1;justify-self:end;font-size:.75rem;font-weight:600;padding:1px 8px;border-radius:999px;background:#ffffff38}.t-actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:8px}.t-action{min-height:32px;padding:6px 14px;border-radius:8px;font:inherit;font-size:.8125rem;font-weight:600;cursor:pointer;color:#fff;background:#ffffff2e;border:1px solid rgba(255,255,255,.6)}.t-action:hover:not(:disabled){background:#ffffff4d}.t-action:focus-visible{outline:2px solid #fff;outline-offset:2px}.t-action:disabled{opacity:.5;cursor:not-allowed}.t-action-primary{background:#fff;color:#0f172a;border-color:#fff}.t-action-primary:hover:not(:disabled){background:#f1f5f9}.t-action-danger{background:#7f1d1d;border-color:#ffffffb3}.t-action-danger:hover:not(:disabled){background:#991b1b}.t-countdown{grid-column:1 / -1;display:flex;flex-direction:column;gap:4px}.t-countdown-bar{height:4px;border-radius:2px;background:#ffffff47;overflow:hidden}.t-countdown-fill{height:100%;background:#fff;transition:width .2s linear}.t-countdown-label{font-size:.75rem;opacity:.85;font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.t-countdown-fill{transition:none}}@media(pointer:coarse),(max-width:480px){.t-action{min-height:44px}}@keyframes t-in{0%{opacity:0;transform:translate(-50%) translateY(10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){.t-toast{animation:none}}@media(max-width:480px){.t-toast{bottom:max(16px,env(safe-area-inset-bottom,16px));font-size:.8125rem;padding:10px 16px}}\n"]
8240
8451
  }]
8241
8452
  }],
8242
8453
  ctorParameters: () => [],
@@ -8256,6 +8467,50 @@ i0.ɵɵngDeclareClassMetadata({
8256
8467
  alias: "persistentLiveRegion",
8257
8468
  required: false
8258
8469
  }]
8470
+ }],
8471
+ queue: [{
8472
+ type: i0.Input,
8473
+ args: [{
8474
+ isSignal: true,
8475
+ alias: "queue",
8476
+ required: false
8477
+ }]
8478
+ }],
8479
+ countdown: [{
8480
+ type: i0.Input,
8481
+ args: [{
8482
+ isSignal: true,
8483
+ alias: "countdown",
8484
+ required: false
8485
+ }]
8486
+ }],
8487
+ maxQueued: [{
8488
+ type: i0.Input,
8489
+ args: [{
8490
+ isSignal: true,
8491
+ alias: "maxQueued",
8492
+ required: false
8493
+ }]
8494
+ }],
8495
+ countdownLabel: [{
8496
+ type: i0.Input,
8497
+ args: [{
8498
+ isSignal: true,
8499
+ alias: "countdownLabel",
8500
+ required: false
8501
+ }]
8502
+ }],
8503
+ queueLabel: [{
8504
+ type: i0.Input,
8505
+ args: [{
8506
+ isSignal: true,
8507
+ alias: "queueLabel",
8508
+ required: false
8509
+ }]
8510
+ }],
8511
+ action: [{
8512
+ type: i0.Output,
8513
+ args: ["action"]
8259
8514
  }]
8260
8515
  }
8261
8516
  });
@@ -13981,7 +14236,16 @@ var DataTableComponent = class DataTableComponent {
13981
14236
  kind: "component",
13982
14237
  type: ToastComponent,
13983
14238
  selector: "ui-toast",
13984
- inputs: ["dismissible", "persistentLiveRegion"]
14239
+ inputs: [
14240
+ "dismissible",
14241
+ "persistentLiveRegion",
14242
+ "queue",
14243
+ "countdown",
14244
+ "maxQueued",
14245
+ "countdownLabel",
14246
+ "queueLabel"
14247
+ ],
14248
+ outputs: ["action"]
13985
14249
  },
13986
14250
  {
13987
14251
  kind: "component",
@@ -29555,6 +29819,9 @@ function provideForgerouteToast(options = {}) {
29555
29819
  return makeEnvironmentProviders([provideEnvironmentInitializer(() => {
29556
29820
  if (options.dismissible) inject(ToastService).dismissible.set(true);
29557
29821
  if (options.persistentLiveRegion) inject(ToastService).persistentLiveRegion.set(true);
29822
+ if (options.queue) inject(ToastService).queueDefault.set(true);
29823
+ if (options.countdown) inject(ToastService).countdownDefault.set(true);
29824
+ if (options.maxQueued !== void 0) inject(ToastService).maxQueued.set(Math.max(0, Math.floor(options.maxQueued)));
29558
29825
  if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
29559
29826
  const doc = inject(DOCUMENT);
29560
29827
  const appRef = inject(ApplicationRef);
@@ -33587,7 +33854,16 @@ var AppShellComponent = class AppShellComponent {
33587
33854
  kind: "component",
33588
33855
  type: ToastComponent,
33589
33856
  selector: "ui-toast",
33590
- inputs: ["dismissible", "persistentLiveRegion"]
33857
+ inputs: [
33858
+ "dismissible",
33859
+ "persistentLiveRegion",
33860
+ "queue",
33861
+ "countdown",
33862
+ "maxQueued",
33863
+ "countdownLabel",
33864
+ "queueLabel"
33865
+ ],
33866
+ outputs: ["action"]
33591
33867
  },
33592
33868
  {
33593
33869
  kind: "component",
@@ -43558,7 +43834,16 @@ var CalendarComponent = class CalendarComponent {
43558
43834
  kind: "component",
43559
43835
  type: ToastComponent,
43560
43836
  selector: "ui-toast",
43561
- inputs: ["dismissible", "persistentLiveRegion"]
43837
+ inputs: [
43838
+ "dismissible",
43839
+ "persistentLiveRegion",
43840
+ "queue",
43841
+ "countdown",
43842
+ "maxQueued",
43843
+ "countdownLabel",
43844
+ "queueLabel"
43845
+ ],
43846
+ outputs: ["action"]
43562
43847
  },
43563
43848
  {
43564
43849
  kind: "directive",
@@ -51243,7 +51528,16 @@ var ShareMenuComponent = class ShareMenuComponent {
51243
51528
  kind: "component",
51244
51529
  type: ToastComponent,
51245
51530
  selector: "ui-toast",
51246
- inputs: ["dismissible", "persistentLiveRegion"]
51531
+ inputs: [
51532
+ "dismissible",
51533
+ "persistentLiveRegion",
51534
+ "queue",
51535
+ "countdown",
51536
+ "maxQueued",
51537
+ "countdownLabel",
51538
+ "queueLabel"
51539
+ ],
51540
+ outputs: ["action"]
51247
51541
  }],
51248
51542
  changeDetection: i0.ChangeDetectionStrategy.OnPush
51249
51543
  });