otimus-library 0.4.91 → 0.4.92

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,5 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, Injectable, Input, Directive, input, inject, ElementRef, model, output, HostListener, EventEmitter, Output, ContentChildren, forwardRef, ChangeDetectionStrategy, effect, ContentChild, ViewChildren, ViewChild, ViewEncapsulation, HostBinding, PLATFORM_ID, RendererFactory2, signal, ViewContainerRef, computed, viewChild, booleanAttribute } from '@angular/core';
2
+ import { Component, Injectable, Input, Directive, input, inject, ElementRef, model, output, HostListener, EventEmitter, Output, ContentChildren, forwardRef, ChangeDetectionStrategy, effect, ContentChild, ViewChildren, ViewChild, ViewEncapsulation, HostBinding, PLATFORM_ID, RendererFactory2, signal, ViewContainerRef, computed, viewChild, booleanAttribute, DestroyRef } from '@angular/core';
3
+ import { Subject, filter } from 'rxjs';
3
4
  import * as i2$1 from '@angular/cdk/menu';
4
5
  import { CdkMenuModule } from '@angular/cdk/menu';
5
6
  import * as i1 from '@angular/common';
@@ -12,10 +13,10 @@ import * as i3 from '@angular/cdk/overlay';
12
13
  import { Overlay, OverlayPositionBuilder } from '@angular/cdk/overlay';
13
14
  import * as i1$2 from '@angular/router';
14
15
  import { RouterModule } from '@angular/router';
15
- import { filter } from 'rxjs';
16
16
  import * as i2$2 from '@angular/platform-browser';
17
17
  import * as i2$3 from '@angular/cdk/table';
18
18
  import { CdkTableModule } from '@angular/cdk/table';
19
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
19
20
 
20
21
  class OtimusLibraryComponent {
21
22
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: OtimusLibraryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
@@ -48,73 +49,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
48
49
 
49
50
  class OcToastService {
50
51
  constructor() {
51
- this.toastList = [];
52
- this.isRunning = false;
52
+ this.nextId = 1;
53
+ /** Emits each toast opened via `openToast`. Rendered by `OcToastComponent`. */
54
+ this.opened = new Subject();
55
+ this.opened$ = this.opened.asObservable();
53
56
  this.openToast = (toastStatus, toastMessage, toastCounter, promiseKey) => {
54
57
  const toast = {
55
- id: this.toastList.length === 0 ? 1 : this.toastList[this.toastList.length - 1].id + 1,
58
+ id: this.nextId++,
56
59
  counter: toastCounter || 5,
57
60
  status: toastStatus,
58
61
  message: toastMessage,
59
62
  promiseKey,
60
63
  hasAnimation: true,
61
64
  };
62
- const toastExists = this.toastList.some((to) => {
63
- return to.status === toastStatus && to.message === toastMessage;
64
- });
65
- if (toastExists) {
66
- return;
67
- }
68
- const keyExists = this.toastList.find((to) => to.promiseKey === toast.promiseKey);
69
- if (keyExists) {
70
- this.toastList = this.toastList.map((to) => {
71
- if (to.promiseKey === promiseKey) {
72
- return { ...toast, hasAnimation: false };
73
- }
74
- return to;
75
- });
76
- }
77
- this.toastList.push(toast);
78
- if (!this.isRunning) {
79
- this.renderToasts();
80
- }
65
+ this.opened.next(toast);
81
66
  };
82
67
  }
83
- closeToast(id) {
84
- this.toastList = this.toastList.map((to) => {
85
- if (to.id === id) {
86
- return { ...to, counter: 0 };
87
- }
88
- return to;
89
- });
90
- this.currentToast = this.toastList[0];
91
- }
92
- renderToasts() {
93
- if (this.toastList.length === 0) {
94
- this.isRunning = false;
95
- return;
96
- }
97
- this.isRunning = true;
98
- this.currentToast = this.toastList[0];
99
- const toastInterval = setInterval(() => {
100
- if (this.toastList.length === 0) {
101
- clearInterval(toastInterval);
102
- this.isRunning = false;
103
- return;
104
- }
105
- this.currentToast = this.toastList[0];
106
- if (this.currentToast.status !== 'carregando') {
107
- this.currentToast.counter--;
108
- }
109
- if (this.currentToast.counter <= 0) {
110
- this.toastList = this.toastList.filter((to) => {
111
- return to.status !== this.currentToast.status && to.message !== this.currentToast.message;
112
- });
113
- clearInterval(toastInterval);
114
- this.renderToasts();
115
- }
116
- }, 1000);
117
- }
118
68
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: OcToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
119
69
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: OcToastService, providedIn: 'root' }); }
120
70
  }
@@ -4606,17 +4556,137 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
4606
4556
  args: [{ selector: 'oc-notification', standalone: true, imports: [CommonModule], template: "<div\n #card\n class=\"oc-notif\"\n [ngClass]=\"{\n closing: closing(),\n 'swiped-out': swipedOut(),\n dragging: drag().active,\n clickable: ocClickable()\n }\"\n [ngStyle]=\"styleVars()\"\n (click)=\"onClick()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n>\n <div class=\"oc-notif-avatar-slot\">\n <ng-content select=\"[ocNotificationAvatar]\"></ng-content>\n </div>\n\n <div class=\"oc-notif-body\">\n <div class=\"oc-notif-kind\">\n <ng-content select=\"[ocNotificationKind]\"></ng-content>\n </div>\n <div class=\"oc-notif-row1\">\n <span class=\"oc-notif-name\">\n <ng-content select=\"[ocNotificationTitle]\"></ng-content>\n </span>\n </div>\n <div class=\"oc-notif-msg\">\n <ng-content></ng-content>\n </div>\n <div class=\"oc-notif-badge-slot\">\n <ng-content select=\"[ocNotificationBadge]\"></ng-content>\n </div>\n </div>\n\n <div class=\"oc-notif-actions\">\n <span class=\"oc-notif-meta\">\n <ng-content select=\"[ocNotificationTime]\"></ng-content>\n </span>\n @if (ocClosable()) {\n <button\n class=\"oc-notif-close\"\n type=\"button\"\n [attr.aria-label]=\"ocCloseLabel()\"\n [title]=\"ocCloseLabel()\"\n (click)=\"onCloseClick($event)\"\n >\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n }\n </div>\n</div>\n", styles: [":host{display:block}.oc-notif{position:relative;display:flex;align-items:flex-start;gap:10px;background:#f8f9ff;border:1px solid rgba(85,5,162,.25);border-radius:var(--notif-radius, 14px);padding:10px 72px 10px 10px;box-shadow:0 1px 3px #1e08320a,0 1px 1px #1e083205;-webkit-user-select:none;user-select:none;transition:transform .2s cubic-bezier(.2,.9,.2,1.05),box-shadow .2s ease,background .2s ease;touch-action:pan-y}.oc-notif.clickable{cursor:pointer}.oc-notif:hover{transform:translateY(-1px);background:#f8f9ff;box-shadow:0 3px 8px -2px #1e08320f,0 1px 2px #1e083208}.oc-notif.closing{animation:ocnSlideOut .28s cubic-bezier(.4,0,1,1) forwards;pointer-events:none}.oc-notif.dragging{transition:none}.oc-notif.swiped-out{animation:ocnSwipeOut .24s cubic-bezier(.4,0,1,1) forwards;pointer-events:none}@keyframes ocnSlideOut{to{opacity:0;transform:translate(60px) scale(.94)}}@keyframes ocnSwipeOut{to{opacity:0;transform:translate(var(--swipe-out-x, -120%)) scale(.9)}}.oc-notif-avatar-slot{flex:0 0 auto;display:flex;align-items:center;justify-content:center}.oc-notif-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}.oc-notif-kind{font-size:10px;font-weight:400;color:#8f9596;letter-spacing:.1px;line-height:1.2;margin-bottom:1px}.oc-notif-kind:empty{display:none}.oc-notif-row1{display:flex;align-items:baseline;gap:6px;min-width:0}.oc-notif-name{font-size:12.5px;font-weight:700;color:#1e0832;line-height:1.25;letter-spacing:-.1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto;min-width:0}.oc-notif-meta{font-size:10.5px;font-weight:500;color:#8f9596;white-space:nowrap;line-height:1}.oc-notif-meta:empty{display:none}.oc-notif-msg{font-size:12px;color:#353535;line-height:1.4;margin-top:3px;word-break:break-word;display:-webkit-box;-webkit-line-clamp:var(--notif-line-clamp, 2);line-clamp:var(--notif-line-clamp, 2);-webkit-box-orient:vertical;overflow:hidden}.oc-notif-msg:empty{display:none}.oc-notif-badge-slot:empty{display:none}.oc-notif-actions{position:absolute;top:8px;right:8px;display:flex;align-items:center;gap:6px;z-index:2}.oc-notif-close{width:20px;height:20px;border-radius:6px;border:0;background:transparent;color:#8f9596;cursor:pointer;display:grid;place-items:center;transition:background .15s ease,color .15s ease}.oc-notif-close:hover{background:#f7f7f7;color:#353535}.oc-notif-close .material-symbols-outlined{font-size:13px}\n"] }]
4607
4557
  }], propDecorators: { ocClosable: [{ type: i0.Input, args: [{ isSignal: true, alias: "ocClosable", required: false }] }], ocClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "ocClickable", required: false }] }], ocSwipeable: [{ type: i0.Input, args: [{ isSignal: true, alias: "ocSwipeable", required: false }] }], ocSwipeDirection: [{ type: i0.Input, args: [{ isSignal: true, alias: "ocSwipeDirection", required: false }] }], ocBorderRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "ocBorderRadius", required: false }] }], ocLineClamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "ocLineClamp", required: false }] }], ocCloseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ocCloseLabel", required: false }] }], ocClose: [{ type: i0.Output, args: ["ocClose"] }], ocClick: [{ type: i0.Output, args: ["ocClick"] }], card: [{ type: i0.ViewChild, args: ['card', { isSignal: true }] }] } });
4608
4558
 
4559
+ const STATUS_CONF = {
4560
+ sucesso: { cssKey: 'sucesso', icon: 'check_circle' },
4561
+ success: { cssKey: 'sucesso', icon: 'check_circle' },
4562
+ éxito: { cssKey: 'sucesso', icon: 'check_circle' },
4563
+ erro: { cssKey: 'erro', icon: 'error' },
4564
+ error: { cssKey: 'erro', icon: 'error' },
4565
+ informação: { cssKey: 'info', icon: 'info' },
4566
+ informacao: { cssKey: 'info', icon: 'info' },
4567
+ información: { cssKey: 'info', icon: 'info' },
4568
+ information: { cssKey: 'info', icon: 'info' },
4569
+ carregando: { cssKey: 'loading', icon: 'progress_activity' },
4570
+ cargando: { cssKey: 'loading', icon: 'progress_activity' },
4571
+ loading: { cssKey: 'loading', icon: 'progress_activity' },
4572
+ };
4573
+ const DEFAULT_CONF = { cssKey: 'info', icon: 'info' };
4574
+ const MAX_VISIBLE = 3;
4575
+ /** Must match the `oc-toast-out` animation duration in the scss. */
4576
+ const LEAVE_MS = 240;
4577
+ /**
4578
+ * Center-bottom toast stack, drawer-safe (renders above the `oc-drawer` scrim
4579
+ * at `z-index: 1100`). Mount a single instance at the application root, e.g.
4580
+ * `<oc-toast />`.
4581
+ *
4582
+ * Toasts are triggered through `OcToastService.openToast(...)`. Renders a pill
4583
+ * with the status-tint palette, stacks up to 3 (oldest on top), auto-dismisses
4584
+ * after the per-toast countdown (loading persists), pauses the countdown while
4585
+ * hovered, replaces a `promiseKey` toast in place (loading → result) and
4586
+ * de-dupes identical status+message toasts by renewing their timer.
4587
+ */
4609
4588
  class OcToastComponent {
4610
- constructor(toast) {
4611
- this.toast = toast;
4589
+ constructor() {
4590
+ this.toastService = inject(OcToastService);
4591
+ this.destroyRef = inject(DestroyRef);
4592
+ /** Visible + leaving toasts, ordered oldest → newest. */
4593
+ this.toasts = signal([], ...(ngDevMode ? [{ debugName: "toasts" }] : []));
4594
+ /** True while the pointer hovers the stack — freezes every countdown. */
4595
+ this.paused = signal(false, ...(ngDevMode ? [{ debugName: "paused" }] : []));
4596
+ /** Removal timers for toasts playing their leave animation. */
4597
+ this.leaveTimers = new Map();
4598
+ /** Single 1s tick drives every countdown so hover-pause is trivial. */
4599
+ this.tick = setInterval(() => this.onTick(), 1000);
4600
+ this.toastService.opened$
4601
+ .pipe(takeUntilDestroyed(this.destroyRef))
4602
+ .subscribe((toast) => this.onOpened(toast));
4603
+ }
4604
+ ngOnDestroy() {
4605
+ clearInterval(this.tick);
4606
+ this.leaveTimers.forEach((timer) => clearTimeout(timer));
4607
+ this.leaveTimers.clear();
4608
+ }
4609
+ /** Maps any status alias to its icon + css key (defaults to info). */
4610
+ resolve(status) {
4611
+ return STATUS_CONF[(status ?? '').toLowerCase()] ?? DEFAULT_CONF;
4612
+ }
4613
+ isLoading(status) {
4614
+ return this.resolve(status).cssKey === 'loading';
4615
+ }
4616
+ /** Older toasts dim slightly; clamps the css `data-stack-pos` to 0..2. */
4617
+ stackPos(index) {
4618
+ return Math.min(this.toasts().length - 1 - index, 2);
4612
4619
  }
4613
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: OcToastComponent, deps: [{ token: OcToastService }], target: i0.ɵɵFactoryTarget.Component }); }
4614
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: OcToastComponent, isStandalone: true, selector: "oc-toast", ngImport: i0, template: "<div\n class=\"toast\"\n [ngClass]=\"{\n 'close-toast': toast.currentToast.counter === 1,\n error: ['erro', 'error'].includes(toast.currentToast.status.toLowerCase()),\n success: ['sucesso', 'success', '\u00E9xito'].includes(toast.currentToast.status.toLowerCase()),\n loading: ['carregando', 'cargando', 'loading'].includes(toast.currentToast.status.toLowerCase()),\n animation: toast.currentToast.hasAnimation\n }\"\n *ngIf=\"toast.currentToast && toast.currentToast.counter > 0\"\n>\n <header>\n <ng-container [ngTemplateOutlet]=\"iconTemplate\"></ng-container>\n <h4 class=\"status\">\n {{ toast.currentToast.status[0].toUpperCase() + toast.currentToast.status.slice(1) }}\n </h4>\n <button (click)=\"toast.closeToast(toast.currentToast.id)\">\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n </header>\n <p>{{ toast.currentToast.message }}</p>\n</div>\n\n<ng-template #iconTemplate>\n <span\n class=\"material-symbols-outlined\"\n *ngIf=\"['erro', 'error'].includes(toast.currentToast.status.toLowerCase())\"\n >error</span\n >\n\n <span\n class=\"material-symbols-outlined\"\n *ngIf=\"['sucesso', 'success', '\u00E9xito'].includes(toast.currentToast.status.toLowerCase())\"\n >check_circle</span\n >\n\n <span\n class=\"material-symbols-outlined\"\n *ngIf=\"['information', 'informa\u00E7\u00E3o', 'informaci\u00F3n'].includes(toast.currentToast.status.toLowerCase())\"\n >info</span\n >\n\n <svg *ngIf=\"['carregando', 'cargando', 'loading'].includes(toast.currentToast.status.toLowerCase())\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><style>rect{fill:#00DDA3}.spinner_zWVm{animation:spinner_5QiW 1.2s linear infinite,spinner_PnZo 1.2s linear infinite}.spinner_gfyD{animation:spinner_5QiW 1.2s linear infinite,spinner_4j7o 1.2s linear infinite;animation-delay:.1s}.spinner_T5JJ{animation:spinner_5QiW 1.2s linear infinite,spinner_fLK4 1.2s linear infinite;animation-delay:.1s}.spinner_E3Wz{animation:spinner_5QiW 1.2s linear infinite,spinner_tDji 1.2s linear infinite;animation-delay:.2s}.spinner_g2vs{animation:spinner_5QiW 1.2s linear infinite,spinner_CMiT 1.2s linear infinite;animation-delay:.2s}.spinner_ctYB{animation:spinner_5QiW 1.2s linear infinite,spinner_cHKR 1.2s linear infinite;animation-delay:.2s}.spinner_BDNj{animation:spinner_5QiW 1.2s linear infinite,spinner_Re6e 1.2s linear infinite;animation-delay:.3s}.spinner_rCw3{animation:spinner_5QiW 1.2s linear infinite,spinner_EJmJ 1.2s linear infinite;animation-delay:.3s}.spinner_Rszm{animation:spinner_5QiW 1.2s linear infinite,spinner_YJOP 1.2s linear infinite;animation-delay:.4s}@keyframes spinner_5QiW{0%,50%{width:7.33px;height:7.33px}25%{width:1.33px;height:1.33px}}@keyframes spinner_PnZo{0%,50%{x:1px;y:1px}25%{x:4px;y:4px}}@keyframes spinner_4j7o{0%,50%{x:8.33px;y:1px}25%{x:11.33px;y:4px}}@keyframes spinner_fLK4{0%,50%{x:1px;y:8.33px}25%{x:4px;y:11.33px}}@keyframes spinner_tDji{0%,50%{x:15.66px;y:1px}25%{x:18.66px;y:4px}}@keyframes spinner_CMiT{0%,50%{x:8.33px;y:8.33px}25%{x:11.33px;y:11.33px}}@keyframes spinner_cHKR{0%,50%{x:1px;y:15.66px}25%{x:4px;y:18.66px}}@keyframes spinner_Re6e{0%,50%{x:15.66px;y:8.33px}25%{x:18.66px;y:11.33px}}@keyframes spinner_EJmJ{0%,50%{x:8.33px;y:15.66px}25%{x:11.33px;y:18.66px}}@keyframes spinner_YJOP{0%,50%{x:15.66px;y:15.66px}25%{x:18.66px;y:18.66px}}</style><rect class=\"spinner_zWVm\" x=\"1\" y=\"1\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_gfyD\" x=\"8.33\" y=\"1\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_T5JJ\" x=\"1\" y=\"8.33\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_E3Wz\" x=\"15.66\" y=\"1\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_g2vs\" x=\"8.33\" y=\"8.33\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_ctYB\" x=\"1\" y=\"15.66\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_BDNj\" x=\"15.66\" y=\"8.33\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_rCw3\" x=\"8.33\" y=\"15.66\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_Rszm\" x=\"15.66\" y=\"15.66\" width=\"7.33\" height=\"7.33\"/></svg>\n \n\n</ng-template>\n", styles: [".toast{position:fixed;bottom:30px;right:30px;width:370px!important;min-height:116px!important;display:flex;flex-direction:column;justify-content:space-between;border-radius:17px;padding:1rem 1rem 1.5rem;box-shadow:8px 16px 32px #0000004d;background-color:#fff;z-index:50}.toast p{color:#625270;font-size:13px}.toast>header{color:#5505a2;display:flex;align-items:center;gap:.5rem}.toast>header>button{position:absolute;right:20px;border:none;background-color:transparent;color:#d1d5db;padding:10px 0 10px 10px;cursor:pointer;transition:.3s ease}.toast>header>button:hover{color:#ed3a3a}.toast>header>.icon{border:4px solid #5505a2;border-radius:50%;width:30px;height:30px}.toast>header h4{margin:0;padding:0;font-size:20px}.animation{animation:show-up .5s ease}.error>header{color:#ed3a3a}.error>header h4{color:#ed3a3a}.success>header{color:#4ab858}.success>header h4{color:#4ab858}.loading>header h4{color:#786b84}.loading>header>img,.loading>header svg{transform:rotate(45deg)}.close-toast{animation:close 1.1s ease}@keyframes show-up{0%{opacity:0;transform:translateY(100vw)}to{opacity:1;transform:translate(0)}}@keyframes close{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(100vw)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
4620
+ /** Plays the leave animation, then drops the toast from the stack. */
4621
+ dismiss(id) {
4622
+ this.toasts.update((list) => list.map((toast) => (toast.id === id ? { ...toast, leaving: true } : toast)));
4623
+ if (this.leaveTimers.has(id)) {
4624
+ return;
4625
+ }
4626
+ const timer = setTimeout(() => {
4627
+ this.toasts.update((list) => list.filter((toast) => toast.id !== id));
4628
+ this.leaveTimers.delete(id);
4629
+ }, LEAVE_MS);
4630
+ this.leaveTimers.set(id, timer);
4631
+ }
4632
+ onOpened(toast) {
4633
+ // A loading toast carrying a promiseKey is later replaced in place by its
4634
+ // result (e.g. carregando → sucesso) — keep its slot rather than stacking.
4635
+ if (toast.promiseKey) {
4636
+ const existing = this.toasts().find((item) => item.promiseKey === toast.promiseKey && !item.leaving);
4637
+ if (existing) {
4638
+ this.toasts.update((list) => list.map((item) => item.id === existing.id
4639
+ ? { ...item, status: toast.status, message: toast.message, counter: toast.counter }
4640
+ : item));
4641
+ return;
4642
+ }
4643
+ }
4644
+ // De-dupe: an identical (status + message) toast already visible just has
4645
+ // its countdown renewed instead of stacking a copy.
4646
+ const duplicate = this.toasts().find((item) => item.status === toast.status && item.message === toast.message && !item.leaving);
4647
+ if (duplicate) {
4648
+ this.toasts.update((list) => list.map((item) => (item.id === duplicate.id ? { ...item, counter: toast.counter } : item)));
4649
+ return;
4650
+ }
4651
+ // Evict the oldest visible toast once the stack is at capacity.
4652
+ const visible = this.toasts().filter((item) => !item.leaving);
4653
+ if (visible.length >= MAX_VISIBLE) {
4654
+ this.dismiss(visible[0].id);
4655
+ }
4656
+ const item = {
4657
+ id: toast.id,
4658
+ status: toast.status,
4659
+ message: toast.message,
4660
+ counter: toast.counter,
4661
+ promiseKey: toast.promiseKey,
4662
+ leaving: false,
4663
+ };
4664
+ this.toasts.update((list) => [...list, item]);
4665
+ }
4666
+ onTick() {
4667
+ if (this.paused()) {
4668
+ return;
4669
+ }
4670
+ const expired = [];
4671
+ this.toasts.update((list) => list.map((toast) => {
4672
+ if (toast.leaving || this.isLoading(toast.status)) {
4673
+ return toast;
4674
+ }
4675
+ const counter = toast.counter - 1;
4676
+ if (counter <= 0) {
4677
+ expired.push(toast.id);
4678
+ }
4679
+ return { ...toast, counter };
4680
+ }));
4681
+ expired.forEach((id) => this.dismiss(id));
4682
+ }
4683
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: OcToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4684
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: OcToastComponent, isStandalone: true, selector: "oc-toast", ngImport: i0, template: "<div\n class=\"oc-toast-stack\"\n (mouseenter)=\"paused.set(true)\"\n (mouseleave)=\"paused.set(false)\"\n>\n @for (toast of toasts(); track toast.id; let i = $index) {\n <div\n class=\"oc-toast status-{{ resolve(toast.status).cssKey }}\"\n [class.is-leaving]=\"toast.leaving\"\n [attr.data-stack-pos]=\"stackPos(i)\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <span class=\"oc-toast-icon\">\n @if (isLoading(toast.status)) {\n <span\n class=\"oc-toast-spinner\"\n aria-hidden=\"true\"\n ></span>\n } @else {\n <span class=\"material-symbols-outlined\">{{ resolve(toast.status).icon }}</span>\n }\n </span>\n\n <span class=\"oc-toast-msg\">{{ toast.message }}</span>\n\n <button\n class=\"oc-toast-close\"\n type=\"button\"\n aria-label=\"Fechar\"\n (click)=\"dismiss(toast.id)\"\n >\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.oc-toast-stack{position:fixed;left:50%;bottom:24px;transform:translate(-50%);display:flex;flex-direction:column;align-items:center;gap:10px;z-index:1100;pointer-events:none;width:max-content;max-width:calc(100% - 48px)}.oc-toast-stack>*{pointer-events:auto}@keyframes oc-toast-in{0%{opacity:0;transform:translateY(28px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes oc-toast-out{0%{opacity:1;transform:translateY(0) scale(1);max-height:200px;margin-top:0}to{opacity:0;transform:translateY(16px) scale(.96);max-height:0;margin-top:-10px}}.oc-toast{display:flex;align-items:center;gap:10px;min-width:280px;max-width:520px;padding:10px 6px 10px 14px;border:1px solid;border-radius:999px;box-shadow:0 12px 28px #1e08321a,0 3px 8px #1e08320d;font-family:Ubuntu,sans-serif;-webkit-user-select:none;user-select:none;animation:oc-toast-in .32s cubic-bezier(.2,.9,.2,1)}.oc-toast.is-leaving{animation:oc-toast-out .24s ease forwards;pointer-events:none}.oc-toast[data-stack-pos=\"1\"]{opacity:.94}.oc-toast[data-stack-pos=\"2\"]{opacity:.84}.oc-toast.status-sucesso{background:#e6fbf4;border-color:#b7efdb;color:#08643d}.oc-toast.status-erro{background:#fde9e9;border-color:#f5bfbf;color:#7a1717}.oc-toast.status-info,.oc-toast.status-loading{background:#f2eaf9;border-color:#dcc8ee;color:#3d0676}.oc-toast-icon{flex-shrink:0;display:grid;place-items:center}.oc-toast-icon .material-symbols-outlined{font-size:20px}.oc-toast-msg{flex:1;min-width:0;font-size:14px;line-height:1.4;font-weight:400;text-wrap:pretty;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.oc-toast-close{flex-shrink:0;display:grid;place-items:center;width:28px;height:28px;border:0;border-radius:50%;background:transparent;color:inherit;opacity:.55;cursor:pointer;transition:opacity .15s ease,background .15s ease}.oc-toast-close:hover{opacity:1;background:#0000000f}.oc-toast-close .material-symbols-outlined{font-size:18px}.oc-toast-spinner{width:20px;height:20px;border:2.5px solid currentColor;border-right-color:transparent;border-radius:50%;opacity:.9;animation:oc-toast-spin .85s linear infinite}@keyframes oc-toast-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.oc-toast,.oc-toast.is-leaving{animation:none}.oc-toast-spinner{animation-duration:1.6s}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4615
4685
  }
4616
4686
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: OcToastComponent, decorators: [{
4617
4687
  type: Component,
4618
- args: [{ selector: 'oc-toast', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.Default, template: "<div\n class=\"toast\"\n [ngClass]=\"{\n 'close-toast': toast.currentToast.counter === 1,\n error: ['erro', 'error'].includes(toast.currentToast.status.toLowerCase()),\n success: ['sucesso', 'success', '\u00E9xito'].includes(toast.currentToast.status.toLowerCase()),\n loading: ['carregando', 'cargando', 'loading'].includes(toast.currentToast.status.toLowerCase()),\n animation: toast.currentToast.hasAnimation\n }\"\n *ngIf=\"toast.currentToast && toast.currentToast.counter > 0\"\n>\n <header>\n <ng-container [ngTemplateOutlet]=\"iconTemplate\"></ng-container>\n <h4 class=\"status\">\n {{ toast.currentToast.status[0].toUpperCase() + toast.currentToast.status.slice(1) }}\n </h4>\n <button (click)=\"toast.closeToast(toast.currentToast.id)\">\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n </header>\n <p>{{ toast.currentToast.message }}</p>\n</div>\n\n<ng-template #iconTemplate>\n <span\n class=\"material-symbols-outlined\"\n *ngIf=\"['erro', 'error'].includes(toast.currentToast.status.toLowerCase())\"\n >error</span\n >\n\n <span\n class=\"material-symbols-outlined\"\n *ngIf=\"['sucesso', 'success', '\u00E9xito'].includes(toast.currentToast.status.toLowerCase())\"\n >check_circle</span\n >\n\n <span\n class=\"material-symbols-outlined\"\n *ngIf=\"['information', 'informa\u00E7\u00E3o', 'informaci\u00F3n'].includes(toast.currentToast.status.toLowerCase())\"\n >info</span\n >\n\n <svg *ngIf=\"['carregando', 'cargando', 'loading'].includes(toast.currentToast.status.toLowerCase())\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><style>rect{fill:#00DDA3}.spinner_zWVm{animation:spinner_5QiW 1.2s linear infinite,spinner_PnZo 1.2s linear infinite}.spinner_gfyD{animation:spinner_5QiW 1.2s linear infinite,spinner_4j7o 1.2s linear infinite;animation-delay:.1s}.spinner_T5JJ{animation:spinner_5QiW 1.2s linear infinite,spinner_fLK4 1.2s linear infinite;animation-delay:.1s}.spinner_E3Wz{animation:spinner_5QiW 1.2s linear infinite,spinner_tDji 1.2s linear infinite;animation-delay:.2s}.spinner_g2vs{animation:spinner_5QiW 1.2s linear infinite,spinner_CMiT 1.2s linear infinite;animation-delay:.2s}.spinner_ctYB{animation:spinner_5QiW 1.2s linear infinite,spinner_cHKR 1.2s linear infinite;animation-delay:.2s}.spinner_BDNj{animation:spinner_5QiW 1.2s linear infinite,spinner_Re6e 1.2s linear infinite;animation-delay:.3s}.spinner_rCw3{animation:spinner_5QiW 1.2s linear infinite,spinner_EJmJ 1.2s linear infinite;animation-delay:.3s}.spinner_Rszm{animation:spinner_5QiW 1.2s linear infinite,spinner_YJOP 1.2s linear infinite;animation-delay:.4s}@keyframes spinner_5QiW{0%,50%{width:7.33px;height:7.33px}25%{width:1.33px;height:1.33px}}@keyframes spinner_PnZo{0%,50%{x:1px;y:1px}25%{x:4px;y:4px}}@keyframes spinner_4j7o{0%,50%{x:8.33px;y:1px}25%{x:11.33px;y:4px}}@keyframes spinner_fLK4{0%,50%{x:1px;y:8.33px}25%{x:4px;y:11.33px}}@keyframes spinner_tDji{0%,50%{x:15.66px;y:1px}25%{x:18.66px;y:4px}}@keyframes spinner_CMiT{0%,50%{x:8.33px;y:8.33px}25%{x:11.33px;y:11.33px}}@keyframes spinner_cHKR{0%,50%{x:1px;y:15.66px}25%{x:4px;y:18.66px}}@keyframes spinner_Re6e{0%,50%{x:15.66px;y:8.33px}25%{x:18.66px;y:11.33px}}@keyframes spinner_EJmJ{0%,50%{x:8.33px;y:15.66px}25%{x:11.33px;y:18.66px}}@keyframes spinner_YJOP{0%,50%{x:15.66px;y:15.66px}25%{x:18.66px;y:18.66px}}</style><rect class=\"spinner_zWVm\" x=\"1\" y=\"1\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_gfyD\" x=\"8.33\" y=\"1\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_T5JJ\" x=\"1\" y=\"8.33\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_E3Wz\" x=\"15.66\" y=\"1\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_g2vs\" x=\"8.33\" y=\"8.33\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_ctYB\" x=\"1\" y=\"15.66\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_BDNj\" x=\"15.66\" y=\"8.33\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_rCw3\" x=\"8.33\" y=\"15.66\" width=\"7.33\" height=\"7.33\"/><rect class=\"spinner_Rszm\" x=\"15.66\" y=\"15.66\" width=\"7.33\" height=\"7.33\"/></svg>\n \n\n</ng-template>\n", styles: [".toast{position:fixed;bottom:30px;right:30px;width:370px!important;min-height:116px!important;display:flex;flex-direction:column;justify-content:space-between;border-radius:17px;padding:1rem 1rem 1.5rem;box-shadow:8px 16px 32px #0000004d;background-color:#fff;z-index:50}.toast p{color:#625270;font-size:13px}.toast>header{color:#5505a2;display:flex;align-items:center;gap:.5rem}.toast>header>button{position:absolute;right:20px;border:none;background-color:transparent;color:#d1d5db;padding:10px 0 10px 10px;cursor:pointer;transition:.3s ease}.toast>header>button:hover{color:#ed3a3a}.toast>header>.icon{border:4px solid #5505a2;border-radius:50%;width:30px;height:30px}.toast>header h4{margin:0;padding:0;font-size:20px}.animation{animation:show-up .5s ease}.error>header{color:#ed3a3a}.error>header h4{color:#ed3a3a}.success>header{color:#4ab858}.success>header h4{color:#4ab858}.loading>header h4{color:#786b84}.loading>header>img,.loading>header svg{transform:rotate(45deg)}.close-toast{animation:close 1.1s ease}@keyframes show-up{0%{opacity:0;transform:translateY(100vw)}to{opacity:1;transform:translate(0)}}@keyframes close{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(100vw)}}\n"] }]
4619
- }], ctorParameters: () => [{ type: OcToastService }] });
4688
+ args: [{ selector: 'oc-toast', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"oc-toast-stack\"\n (mouseenter)=\"paused.set(true)\"\n (mouseleave)=\"paused.set(false)\"\n>\n @for (toast of toasts(); track toast.id; let i = $index) {\n <div\n class=\"oc-toast status-{{ resolve(toast.status).cssKey }}\"\n [class.is-leaving]=\"toast.leaving\"\n [attr.data-stack-pos]=\"stackPos(i)\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <span class=\"oc-toast-icon\">\n @if (isLoading(toast.status)) {\n <span\n class=\"oc-toast-spinner\"\n aria-hidden=\"true\"\n ></span>\n } @else {\n <span class=\"material-symbols-outlined\">{{ resolve(toast.status).icon }}</span>\n }\n </span>\n\n <span class=\"oc-toast-msg\">{{ toast.message }}</span>\n\n <button\n class=\"oc-toast-close\"\n type=\"button\"\n aria-label=\"Fechar\"\n (click)=\"dismiss(toast.id)\"\n >\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.oc-toast-stack{position:fixed;left:50%;bottom:24px;transform:translate(-50%);display:flex;flex-direction:column;align-items:center;gap:10px;z-index:1100;pointer-events:none;width:max-content;max-width:calc(100% - 48px)}.oc-toast-stack>*{pointer-events:auto}@keyframes oc-toast-in{0%{opacity:0;transform:translateY(28px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes oc-toast-out{0%{opacity:1;transform:translateY(0) scale(1);max-height:200px;margin-top:0}to{opacity:0;transform:translateY(16px) scale(.96);max-height:0;margin-top:-10px}}.oc-toast{display:flex;align-items:center;gap:10px;min-width:280px;max-width:520px;padding:10px 6px 10px 14px;border:1px solid;border-radius:999px;box-shadow:0 12px 28px #1e08321a,0 3px 8px #1e08320d;font-family:Ubuntu,sans-serif;-webkit-user-select:none;user-select:none;animation:oc-toast-in .32s cubic-bezier(.2,.9,.2,1)}.oc-toast.is-leaving{animation:oc-toast-out .24s ease forwards;pointer-events:none}.oc-toast[data-stack-pos=\"1\"]{opacity:.94}.oc-toast[data-stack-pos=\"2\"]{opacity:.84}.oc-toast.status-sucesso{background:#e6fbf4;border-color:#b7efdb;color:#08643d}.oc-toast.status-erro{background:#fde9e9;border-color:#f5bfbf;color:#7a1717}.oc-toast.status-info,.oc-toast.status-loading{background:#f2eaf9;border-color:#dcc8ee;color:#3d0676}.oc-toast-icon{flex-shrink:0;display:grid;place-items:center}.oc-toast-icon .material-symbols-outlined{font-size:20px}.oc-toast-msg{flex:1;min-width:0;font-size:14px;line-height:1.4;font-weight:400;text-wrap:pretty;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.oc-toast-close{flex-shrink:0;display:grid;place-items:center;width:28px;height:28px;border:0;border-radius:50%;background:transparent;color:inherit;opacity:.55;cursor:pointer;transition:opacity .15s ease,background .15s ease}.oc-toast-close:hover{opacity:1;background:#0000000f}.oc-toast-close .material-symbols-outlined{font-size:18px}.oc-toast-spinner{width:20px;height:20px;border:2.5px solid currentColor;border-right-color:transparent;border-radius:50%;opacity:.9;animation:oc-toast-spin .85s linear infinite}@keyframes oc-toast-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.oc-toast,.oc-toast.is-leaving{animation:none}.oc-toast-spinner{animation-duration:1.6s}}\n"] }]
4689
+ }], ctorParameters: () => [] });
4620
4690
 
4621
4691
  class OcToggleComponent {
4622
4692
  constructor() {