UNPKG

48 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { Directive, InjectionToken, inject, Injectable, ComponentFactoryResolver, ApplicationRef, SecurityContext, Injector, Inject, signal, Component, ChangeDetectionStrategy, HostBinding, HostListener, makeEnvironmentProviders, NgModule } from '@angular/core';
3import { trigger, state, style, transition, animate } from '@angular/animations';
4import { DOCUMENT, NgIf } from '@angular/common';
5import { Subject } from 'rxjs';
6import * as i2 from '@angular/platform-browser';
7
8class ToastContainerDirective {
9 el;
10 constructor(el) {
11 this.el = el;
12 }
13 getContainerElement() {
14 return this.el.nativeElement;
15 }
16 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastContainerDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
17 static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.0", type: ToastContainerDirective, isStandalone: true, selector: "[toastContainer]", exportAs: ["toastContainer"], ngImport: i0 });
18}
19i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastContainerDirective, decorators: [{
20 type: Directive,
21 args: [{
22 selector: '[toastContainer]',
23 exportAs: 'toastContainer',
24 standalone: true
25 }]
26 }], ctorParameters: () => [{ type: i0.ElementRef }] });
27
28/**
29 * A `ComponentPortal` is a portal that instantiates some Component upon attachment.
30 */
31class ComponentPortal {
32 _attachedHost;
33 /** The type of the component that will be instantiated for attachment. */
34 component;
35 /**
36 * [Optional] Where the attached component should live in Angular's *logical* component tree.
37 * This is different from where the component *renders*, which is determined by the PortalHost.
38 * The origin necessary when the host is outside of the Angular application context.
39 */
40 viewContainerRef;
41 /** Injector used for the instantiation of the component. */
42 injector;
43 constructor(component, injector) {
44 this.component = component;
45 this.injector = injector;
46 }
47 /** Attach this portal to a host. */
48 attach(host, newestOnTop) {
49 this._attachedHost = host;
50 return host.attach(this, newestOnTop);
51 }
52 /** Detach this portal from its host */
53 detach() {
54 const host = this._attachedHost;
55 if (host) {
56 this._attachedHost = undefined;
57 return host.detach();
58 }
59 }
60 /** Whether this portal is attached to a host. */
61 get isAttached() {
62 return this._attachedHost != null;
63 }
64 /**
65 * Sets the PortalHost reference without performing `attach()`. This is used directly by
66 * the PortalHost when it is performing an `attach()` or `detach()`.
67 */
68 setAttachedHost(host) {
69 this._attachedHost = host;
70 }
71}
72/**
73 * Partial implementation of PortalHost that only deals with attaching a
74 * ComponentPortal
75 */
76class BasePortalHost {
77 /** The portal currently attached to the host. */
78 _attachedPortal;
79 /** A function that will permanently dispose this host. */
80 _disposeFn;
81 attach(portal, newestOnTop) {
82 this._attachedPortal = portal;
83 return this.attachComponentPortal(portal, newestOnTop);
84 }
85 detach() {
86 if (this._attachedPortal) {
87 this._attachedPortal.setAttachedHost();
88 }
89 this._attachedPortal = undefined;
90 if (this._disposeFn) {
91 this._disposeFn();
92 this._disposeFn = undefined;
93 }
94 }
95 setDisposeFn(fn) {
96 this._disposeFn = fn;
97 }
98}
99
100/**
101 * Reference to a toast opened via the Toastr service.
102 */
103class ToastRef {
104 _overlayRef;
105 /** The instance of component opened into the toast. */
106 componentInstance;
107 /** Count of duplicates of this toast */
108 duplicatesCount = 0;
109 /** Subject for notifying the user that the toast has finished closing. */
110 _afterClosed = new Subject();
111 /** triggered when toast is activated */
112 _activate = new Subject();
113 /** notifies the toast that it should close before the timeout */
114 _manualClose = new Subject();
115 /** notifies the toast that it should reset the timeouts */
116 _resetTimeout = new Subject();
117 /** notifies the toast that it should count a duplicate toast */
118 _countDuplicate = new Subject();
119 constructor(_overlayRef) {
120 this._overlayRef = _overlayRef;
121 }
122 manualClose() {
123 this._manualClose.next();
124 this._manualClose.complete();
125 }
126 manualClosed() {
127 return this._manualClose.asObservable();
128 }
129 timeoutReset() {
130 return this._resetTimeout.asObservable();
131 }
132 countDuplicate() {
133 return this._countDuplicate.asObservable();
134 }
135 /**
136 * Close the toast.
137 */
138 close() {
139 this._overlayRef.detach();
140 this._afterClosed.next();
141 this._manualClose.next();
142 this._afterClosed.complete();
143 this._manualClose.complete();
144 this._activate.complete();
145 this._resetTimeout.complete();
146 this._countDuplicate.complete();
147 }
148 /** Gets an observable that is notified when the toast is finished closing. */
149 afterClosed() {
150 return this._afterClosed.asObservable();
151 }
152 isInactive() {
153 return this._activate.isStopped;
154 }
155 activate() {
156 this._activate.next();
157 this._activate.complete();
158 }
159 /** Gets an observable that is notified when the toast has started opening. */
160 afterActivate() {
161 return this._activate.asObservable();
162 }
163 /** Reset the toast timouts and count duplicates */
164 onDuplicate(resetTimeout, countDuplicate) {
165 if (resetTimeout) {
166 this._resetTimeout.next();
167 }
168 if (countDuplicate) {
169 this._countDuplicate.next(++this.duplicatesCount);
170 }
171 }
172}
173
174/**
175 * Everything a toast needs to launch
176 */
177class ToastPackage {
178 toastId;
179 config;
180 message;
181 title;
182 toastType;
183 toastRef;
184 _onTap = new Subject();
185 _onAction = new Subject();
186 constructor(toastId, config, message, title, toastType, toastRef) {
187 this.toastId = toastId;
188 this.config = config;
189 this.message = message;
190 this.title = title;
191 this.toastType = toastType;
192 this.toastRef = toastRef;
193 this.toastRef.afterClosed().subscribe(() => {
194 this._onAction.complete();
195 this._onTap.complete();
196 });
197 }
198 /** Fired on click */
199 triggerTap() {
200 this._onTap.next();
201 if (this.config.tapToDismiss) {
202 this._onTap.complete();
203 }
204 }
205 onTap() {
206 return this._onTap.asObservable();
207 }
208 /** available for use in custom toast */
209 triggerAction(action) {
210 this._onAction.next(action);
211 }
212 onAction() {
213 return this._onAction.asObservable();
214 }
215}
216const DefaultNoComponentGlobalConfig = {
217 maxOpened: 0,
218 autoDismiss: false,
219 newestOnTop: true,
220 preventDuplicates: false,
221 countDuplicates: false,
222 resetTimeoutOnDuplicate: false,
223 includeTitleDuplicates: false,
224 iconClasses: {
225 error: 'toast-error',
226 info: 'toast-info',
227 success: 'toast-success',
228 warning: 'toast-warning',
229 },
230 // Individual
231 closeButton: false,
232 disableTimeOut: false,
233 timeOut: 5000,
234 extendedTimeOut: 1000,
235 enableHtml: false,
236 progressBar: false,
237 toastClass: 'ngx-toastr',
238 positionClass: 'toast-top-right',
239 titleClass: 'toast-title',
240 messageClass: 'toast-message',
241 easing: 'ease-in',
242 easeTime: 300,
243 tapToDismiss: true,
244 onActivateTick: false,
245 progressAnimation: 'decreasing',
246};
247const TOAST_CONFIG = new InjectionToken('ToastConfig');
248
249/**
250 * A PortalHost for attaching portals to an arbitrary DOM element outside of the Angular
251 * application context.
252 *
253 * This is the only part of the portal core that directly touches the DOM.
254 */
255class DomPortalHost extends BasePortalHost {
256 _hostDomElement;
257 _componentFactoryResolver;
258 _appRef;
259 constructor(_hostDomElement, _componentFactoryResolver, _appRef) {
260 super();
261 this._hostDomElement = _hostDomElement;
262 this._componentFactoryResolver = _componentFactoryResolver;
263 this._appRef = _appRef;
264 }
265 /**
266 * Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver.
267 * @param portal Portal to be attached
268 */
269 attachComponentPortal(portal, newestOnTop) {
270 const componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component);
271 let componentRef;
272 // If the portal specifies a ViewContainerRef, we will use that as the attachment point
273 // for the component (in terms of Angular's component tree, not rendering).
274 // When the ViewContainerRef is missing, we use the factory to create the component directly
275 // and then manually attach the ChangeDetector for that component to the application (which
276 // happens automatically when using a ViewContainer).
277 componentRef = componentFactory.create(portal.injector);
278 // When creating a component outside of a ViewContainer, we need to manually register
279 // its ChangeDetector with the application. This API is unfortunately not yet published
280 // in Angular core. The change detector must also be deregistered when the component
281 // is destroyed to prevent memory leaks.
282 this._appRef.attachView(componentRef.hostView);
283 this.setDisposeFn(() => {
284 this._appRef.detachView(componentRef.hostView);
285 componentRef.destroy();
286 });
287 // At this point the component has been instantiated, so we move it to the location in the DOM
288 // where we want it to be rendered.
289 if (newestOnTop) {
290 this._hostDomElement.insertBefore(this._getComponentRootNode(componentRef), this._hostDomElement.firstChild);
291 }
292 else {
293 this._hostDomElement.appendChild(this._getComponentRootNode(componentRef));
294 }
295 return componentRef;
296 }
297 /** Gets the root HTMLElement for an instantiated component. */
298 _getComponentRootNode(componentRef) {
299 return componentRef.hostView.rootNodes[0];
300 }
301}
302
303/** Container inside which all toasts will render. */
304class OverlayContainer {
305 _document = inject(DOCUMENT);
306 _containerElement;
307 ngOnDestroy() {
308 if (this._containerElement && this._containerElement.parentNode) {
309 this._containerElement.parentNode.removeChild(this._containerElement);
310 }
311 }
312 /**
313 * This method returns the overlay container element. It will lazily
314 * create the element the first time it is called to facilitate using
315 * the container in non-browser environments.
316 * @returns the container element
317 */
318 getContainerElement() {
319 if (!this._containerElement) {
320 this._createContainer();
321 }
322 return this._containerElement;
323 }
324 /**
325 * Create the overlay container element, which is simply a div
326 * with the 'cdk-overlay-container' class on the document body
327 * and 'aria-live="polite"'
328 */
329 _createContainer() {
330 const container = this._document.createElement('div');
331 container.classList.add('overlay-container');
332 container.setAttribute('aria-live', 'polite');
333 this._document.body.appendChild(container);
334 this._containerElement = container;
335 }
336 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: OverlayContainer, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
337 static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: OverlayContainer, providedIn: 'root' });
338}
339i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: OverlayContainer, decorators: [{
340 type: Injectable,
341 args: [{ providedIn: 'root' }]
342 }] });
343
344/**
345 * Reference to an overlay that has been created with the Overlay service.
346 * Used to manipulate or dispose of said overlay.
347 */
348class OverlayRef {
349 _portalHost;
350 constructor(_portalHost) {
351 this._portalHost = _portalHost;
352 }
353 attach(portal, newestOnTop = true) {
354 return this._portalHost.attach(portal, newestOnTop);
355 }
356 /**
357 * Detaches an overlay from a portal.
358 * @returns Resolves when the overlay has been detached.
359 */
360 detach() {
361 return this._portalHost.detach();
362 }
363}
364
365/**
366 * Service to create Overlays. Overlays are dynamically added pieces of floating UI, meant to be
367 * used as a low-level building building block for other components. Dialogs, tooltips, menus,
368 * selects, etc. can all be built using overlays. The service should primarily be used by authors
369 * of re-usable components rather than developers building end-user applications.
370 *
371 * An overlay *is* a PortalHost, so any kind of Portal can be loaded into one.
372 */
373class Overlay {
374 _overlayContainer = inject(OverlayContainer);
375 _componentFactoryResolver = inject(ComponentFactoryResolver);
376 _appRef = inject(ApplicationRef);
377 _document = inject(DOCUMENT);
378 // Namespace panes by overlay container
379 _paneElements = new Map();
380 /**
381 * Creates an overlay.
382 * @returns A reference to the created overlay.
383 */
384 create(positionClass, overlayContainer) {
385 // get existing pane if possible
386 return this._createOverlayRef(this.getPaneElement(positionClass, overlayContainer));
387 }
388 getPaneElement(positionClass = '', overlayContainer) {
389 if (!this._paneElements.get(overlayContainer)) {
390 this._paneElements.set(overlayContainer, {});
391 }
392 if (!this._paneElements.get(overlayContainer)[positionClass]) {
393 this._paneElements.get(overlayContainer)[positionClass] = this._createPaneElement(positionClass, overlayContainer);
394 }
395 return this._paneElements.get(overlayContainer)[positionClass];
396 }
397 /**
398 * Creates the DOM element for an overlay and appends it to the overlay container.
399 * @returns Newly-created pane element
400 */
401 _createPaneElement(positionClass, overlayContainer) {
402 const pane = this._document.createElement('div');
403 pane.id = 'toast-container';
404 pane.classList.add(positionClass);
405 pane.classList.add('toast-container');
406 if (!overlayContainer) {
407 this._overlayContainer.getContainerElement().appendChild(pane);
408 }
409 else {
410 overlayContainer.getContainerElement().appendChild(pane);
411 }
412 return pane;
413 }
414 /**
415 * Create a DomPortalHost into which the overlay content can be loaded.
416 * @param pane The DOM element to turn into a portal host.
417 * @returns A portal host for the given DOM element.
418 */
419 _createPortalHost(pane) {
420 return new DomPortalHost(pane, this._componentFactoryResolver, this._appRef);
421 }
422 /**
423 * Creates an OverlayRef for an overlay in the given DOM element.
424 * @param pane DOM element for the overlay
425 */
426 _createOverlayRef(pane) {
427 return new OverlayRef(this._createPortalHost(pane));
428 }
429 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: Overlay, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
430 static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: Overlay, providedIn: 'root' });
431}
432i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: Overlay, decorators: [{
433 type: Injectable,
434 args: [{ providedIn: 'root' }]
435 }] });
436
437class ToastrService {
438 overlay;
439 _injector;
440 sanitizer;
441 ngZone;
442 toastrConfig;
443 currentlyActive = 0;
444 toasts = [];
445 overlayContainer;
446 previousToastMessage;
447 index = 0;
448 constructor(token, overlay, _injector, sanitizer, ngZone) {
449 this.overlay = overlay;
450 this._injector = _injector;
451 this.sanitizer = sanitizer;
452 this.ngZone = ngZone;
453 this.toastrConfig = {
454 ...token.default,
455 ...token.config,
456 };
457 if (token.config.iconClasses) {
458 this.toastrConfig.iconClasses = {
459 ...token.default.iconClasses,
460 ...token.config.iconClasses,
461 };
462 }
463 }
464 /** show toast */
465 show(message, title, override = {}, type = '') {
466 return this._preBuildNotification(type, message, title, this.applyConfig(override));
467 }
468 /** show successful toast */
469 success(message, title, override = {}) {
470 const type = this.toastrConfig.iconClasses.success || '';
471 return this._preBuildNotification(type, message, title, this.applyConfig(override));
472 }
473 /** show error toast */
474 error(message, title, override = {}) {
475 const type = this.toastrConfig.iconClasses.error || '';
476 return this._preBuildNotification(type, message, title, this.applyConfig(override));
477 }
478 /** show info toast */
479 info(message, title, override = {}) {
480 const type = this.toastrConfig.iconClasses.info || '';
481 return this._preBuildNotification(type, message, title, this.applyConfig(override));
482 }
483 /** show warning toast */
484 warning(message, title, override = {}) {
485 const type = this.toastrConfig.iconClasses.warning || '';
486 return this._preBuildNotification(type, message, title, this.applyConfig(override));
487 }
488 /**
489 * Remove all or a single toast by id
490 */
491 clear(toastId) {
492 // Call every toastRef manualClose function
493 for (const toast of this.toasts) {
494 if (toastId !== undefined) {
495 if (toast.toastId === toastId) {
496 toast.toastRef.manualClose();
497 return;
498 }
499 }
500 else {
501 toast.toastRef.manualClose();
502 }
503 }
504 }
505 /**
506 * Remove and destroy a single toast by id
507 */
508 remove(toastId) {
509 const found = this._findToast(toastId);
510 if (!found) {
511 return false;
512 }
513 found.activeToast.toastRef.close();
514 this.toasts.splice(found.index, 1);
515 this.currentlyActive = this.currentlyActive - 1;
516 if (!this.toastrConfig.maxOpened || !this.toasts.length) {
517 return false;
518 }
519 if (this.currentlyActive < this.toastrConfig.maxOpened && this.toasts[this.currentlyActive]) {
520 const p = this.toasts[this.currentlyActive].toastRef;
521 if (!p.isInactive()) {
522 this.currentlyActive = this.currentlyActive + 1;
523 p.activate();
524 }
525 }
526 return true;
527 }
528 /**
529 * Determines if toast message is already shown
530 */
531 findDuplicate(title = '', message = '', resetOnDuplicate, countDuplicates) {
532 const { includeTitleDuplicates } = this.toastrConfig;
533 for (const toast of this.toasts) {
534 const hasDuplicateTitle = includeTitleDuplicates && toast.title === title;
535 if ((!includeTitleDuplicates || hasDuplicateTitle) && toast.message === message) {
536 toast.toastRef.onDuplicate(resetOnDuplicate, countDuplicates);
537 return toast;
538 }
539 }
540 return null;
541 }
542 /** create a clone of global config and apply individual settings */
543 applyConfig(override = {}) {
544 return { ...this.toastrConfig, ...override };
545 }
546 /**
547 * Find toast object by id
548 */
549 _findToast(toastId) {
550 for (let i = 0; i < this.toasts.length; i++) {
551 if (this.toasts[i].toastId === toastId) {
552 return { index: i, activeToast: this.toasts[i] };
553 }
554 }
555 return null;
556 }
557 /**
558 * Determines the need to run inside angular's zone then builds the toast
559 */
560 _preBuildNotification(toastType, message, title, config) {
561 if (config.onActivateTick) {
562 return this.ngZone.run(() => this._buildNotification(toastType, message, title, config));
563 }
564 return this._buildNotification(toastType, message, title, config);
565 }
566 /**
567 * Creates and attaches toast data to component
568 * returns the active toast, or in case preventDuplicates is enabled the original/non-duplicate active toast.
569 */
570 _buildNotification(toastType, message, title, config) {
571 if (!config.toastComponent) {
572 throw new Error('toastComponent required');
573 }
574 // max opened and auto dismiss = true
575 // if timeout = 0 resetting it would result in setting this.hideTime = Date.now(). Hence, we only want to reset timeout if there is
576 // a timeout at all
577 const duplicate = this.findDuplicate(title, message, this.toastrConfig.resetTimeoutOnDuplicate && config.timeOut > 0, this.toastrConfig.countDuplicates);
578 if (((this.toastrConfig.includeTitleDuplicates && title) || message) &&
579 this.toastrConfig.preventDuplicates &&
580 duplicate !== null) {
581 return duplicate;
582 }
583 this.previousToastMessage = message;
584 let keepInactive = false;
585 if (this.toastrConfig.maxOpened && this.currentlyActive >= this.toastrConfig.maxOpened) {
586 keepInactive = true;
587 if (this.toastrConfig.autoDismiss) {
588 this.clear(this.toasts[0].toastId);
589 }
590 }
591 const overlayRef = this.overlay.create(config.positionClass, this.overlayContainer);
592 this.index = this.index + 1;
593 let sanitizedMessage = message;
594 if (message && config.enableHtml) {
595 sanitizedMessage = this.sanitizer.sanitize(SecurityContext.HTML, message);
596 }
597 const toastRef = new ToastRef(overlayRef);
598 const toastPackage = new ToastPackage(this.index, config, sanitizedMessage, title, toastType, toastRef);
599 /** New injector that contains an instance of `ToastPackage`. */
600 const providers = [{ provide: ToastPackage, useValue: toastPackage }];
601 const toastInjector = Injector.create({ providers, parent: this._injector });
602 const component = new ComponentPortal(config.toastComponent, toastInjector);
603 const portal = overlayRef.attach(component, config.newestOnTop);
604 toastRef.componentInstance = portal.instance;
605 const ins = {
606 toastId: this.index,
607 title: title || '',
608 message: message || '',
609 toastRef,
610 onShown: toastRef.afterActivate(),
611 onHidden: toastRef.afterClosed(),
612 onTap: toastPackage.onTap(),
613 onAction: toastPackage.onAction(),
614 portal,
615 };
616 if (!keepInactive) {
617 this.currentlyActive = this.currentlyActive + 1;
618 setTimeout(() => {
619 ins.toastRef.activate();
620 });
621 }
622 this.toasts.push(ins);
623 return ins;
624 }
625 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrService, deps: [{ token: TOAST_CONFIG }, { token: Overlay }, { token: i0.Injector }, { token: i2.DomSanitizer }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
626 static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrService, providedIn: 'root' });
627}
628i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrService, decorators: [{
629 type: Injectable,
630 args: [{ providedIn: 'root' }]
631 }], ctorParameters: () => [{ type: undefined, decorators: [{
632 type: Inject,
633 args: [TOAST_CONFIG]
634 }] }, { type: Overlay }, { type: i0.Injector }, { type: i2.DomSanitizer }, { type: i0.NgZone }] });
635
636class Toast {
637 toastrService;
638 toastPackage;
639 ngZone;
640 message;
641 title;
642 options;
643 duplicatesCount;
644 originalTimeout;
645 /** width of progress bar */
646 width = signal(-1);
647 /** a combination of toast type and options.toastClass */
648 toastClasses = '';
649 state;
650 /** controls animation */
651 get _state() {
652 return this.state();
653 }
654 /** hides component when waiting to be displayed */
655 get displayStyle() {
656 if (this.state().value === 'inactive') {
657 return 'none';
658 }
659 return;
660 }
661 timeout;
662 intervalId;
663 hideTime;
664 sub;
665 sub1;
666 sub2;
667 sub3;
668 constructor(toastrService, toastPackage, ngZone) {
669 this.toastrService = toastrService;
670 this.toastPackage = toastPackage;
671 this.ngZone = ngZone;
672 this.message = toastPackage.message;
673 this.title = toastPackage.title;
674 this.options = toastPackage.config;
675 this.originalTimeout = toastPackage.config.timeOut;
676 this.toastClasses = `${toastPackage.toastType} ${toastPackage.config.toastClass}`;
677 this.sub = toastPackage.toastRef.afterActivate().subscribe(() => {
678 this.activateToast();
679 });
680 this.sub1 = toastPackage.toastRef.manualClosed().subscribe(() => {
681 this.remove();
682 });
683 this.sub2 = toastPackage.toastRef.timeoutReset().subscribe(() => {
684 this.resetTimeout();
685 });
686 this.sub3 = toastPackage.toastRef.countDuplicate().subscribe(count => {
687 this.duplicatesCount = count;
688 });
689 this.state = signal({
690 value: 'inactive',
691 params: {
692 easeTime: this.toastPackage.config.easeTime,
693 easing: 'ease-in',
694 },
695 });
696 }
697 ngOnDestroy() {
698 this.sub.unsubscribe();
699 this.sub1.unsubscribe();
700 this.sub2.unsubscribe();
701 this.sub3.unsubscribe();
702 clearInterval(this.intervalId);
703 clearTimeout(this.timeout);
704 }
705 /**
706 * activates toast and sets timeout
707 */
708 activateToast() {
709 this.state.update(state => ({ ...state, value: 'active' }));
710 if (!(this.options.disableTimeOut === true || this.options.disableTimeOut === 'timeOut') &&
711 this.options.timeOut) {
712 this.outsideTimeout(() => this.remove(), this.options.timeOut);
713 this.hideTime = new Date().getTime() + this.options.timeOut;
714 if (this.options.progressBar) {
715 this.outsideInterval(() => this.updateProgress(), 10);
716 }
717 }
718 }
719 /**
720 * updates progress bar width
721 */
722 updateProgress() {
723 if (this.width() === 0 || this.width() === 100 || !this.options.timeOut) {
724 return;
725 }
726 const now = new Date().getTime();
727 const remaining = this.hideTime - now;
728 this.width.set((remaining / this.options.timeOut) * 100);
729 if (this.options.progressAnimation === 'increasing') {
730 this.width.update(width => 100 - width);
731 }
732 if (this.width() <= 0) {
733 this.width.set(0);
734 }
735 if (this.width() >= 100) {
736 this.width.set(100);
737 }
738 }
739 resetTimeout() {
740 clearTimeout(this.timeout);
741 clearInterval(this.intervalId);
742 this.state.update(state => ({ ...state, value: 'active' }));
743 this.outsideTimeout(() => this.remove(), this.originalTimeout);
744 this.options.timeOut = this.originalTimeout;
745 this.hideTime = new Date().getTime() + (this.options.timeOut || 0);
746 this.width.set(-1);
747 if (this.options.progressBar) {
748 this.outsideInterval(() => this.updateProgress(), 10);
749 }
750 }
751 /**
752 * tells toastrService to remove this toast after animation time
753 */
754 remove() {
755 if (this.state().value === 'removed') {
756 return;
757 }
758 clearTimeout(this.timeout);
759 this.state.update(state => ({ ...state, value: 'removed' }));
760 this.outsideTimeout(() => this.toastrService.remove(this.toastPackage.toastId), +this.toastPackage.config.easeTime);
761 }
762 tapToast() {
763 if (this.state().value === 'removed') {
764 return;
765 }
766 this.toastPackage.triggerTap();
767 if (this.options.tapToDismiss) {
768 this.remove();
769 }
770 }
771 stickAround() {
772 if (this.state().value === 'removed') {
773 return;
774 }
775 if (this.options.disableTimeOut !== 'extendedTimeOut') {
776 clearTimeout(this.timeout);
777 this.options.timeOut = 0;
778 this.hideTime = 0;
779 // disable progressBar
780 clearInterval(this.intervalId);
781 this.width.set(0);
782 }
783 }
784 delayedHideToast() {
785 if ((this.options.disableTimeOut === true || this.options.disableTimeOut === 'extendedTimeOut') ||
786 this.options.extendedTimeOut === 0 ||
787 this.state().value === 'removed') {
788 return;
789 }
790 this.outsideTimeout(() => this.remove(), this.options.extendedTimeOut);
791 this.options.timeOut = this.options.extendedTimeOut;
792 this.hideTime = new Date().getTime() + (this.options.timeOut || 0);
793 this.width.set(-1);
794 if (this.options.progressBar) {
795 this.outsideInterval(() => this.updateProgress(), 10);
796 }
797 }
798 outsideTimeout(func, timeout) {
799 if (this.ngZone) {
800 this.ngZone.runOutsideAngular(() => (this.timeout = setTimeout(() => this.runInsideAngular(func), timeout)));
801 }
802 else {
803 this.timeout = setTimeout(() => func(), timeout);
804 }
805 }
806 outsideInterval(func, timeout) {
807 if (this.ngZone) {
808 this.ngZone.runOutsideAngular(() => (this.intervalId = setInterval(() => this.runInsideAngular(func), timeout)));
809 }
810 else {
811 this.intervalId = setInterval(() => func(), timeout);
812 }
813 }
814 runInsideAngular(func) {
815 if (this.ngZone) {
816 this.ngZone.run(() => func());
817 }
818 else {
819 func();
820 }
821 }
822 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: Toast, deps: [{ token: ToastrService }, { token: ToastPackage }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
823 static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.0", type: Toast, isStandalone: true, selector: "[toast-component]", host: { listeners: { "click": "tapToast()", "mouseenter": "stickAround()", "mouseleave": "delayedHideToast()" }, properties: { "class": "this.toastClasses", "@flyInOut": "this._state", "style.display": "this.displayStyle" } }, ngImport: i0, template: `
824 <button *ngIf="options.closeButton" (click)="remove()" type="button" class="toast-close-button" aria-label="Close">
825 <span aria-hidden="true">&times;</span>
826 </button>
827 <div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
828 {{ title }} <ng-container *ngIf="duplicatesCount">[{{ duplicatesCount + 1 }}]</ng-container>
829 </div>
830 <div *ngIf="message && options.enableHtml" role="alert"
831 [class]="options.messageClass" [innerHTML]="message">
832 </div>
833 <div *ngIf="message && !options.enableHtml" role="alert"
834 [class]="options.messageClass" [attr.aria-label]="message">
835 {{ message }}
836 </div>
837 <div *ngIf="options.progressBar">
838 <div class="toast-progress" [style.width]="width() + '%'"></div>
839 </div>
840 `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], animations: [
841 trigger('flyInOut', [
842 state('inactive', style({ opacity: 0 })),
843 state('active', style({ opacity: 1 })),
844 state('removed', style({ opacity: 0 })),
845 transition('inactive => active', animate('{{ easeTime }}ms {{ easing }}')),
846 transition('active => removed', animate('{{ easeTime }}ms {{ easing }}')),
847 ]),
848 ], changeDetection: i0.ChangeDetectionStrategy.OnPush });
849}
850i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: Toast, decorators: [{
851 type: Component,
852 args: [{
853 selector: '[toast-component]',
854 template: `
855 <button *ngIf="options.closeButton" (click)="remove()" type="button" class="toast-close-button" aria-label="Close">
856 <span aria-hidden="true">&times;</span>
857 </button>
858 <div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
859 {{ title }} <ng-container *ngIf="duplicatesCount">[{{ duplicatesCount + 1 }}]</ng-container>
860 </div>
861 <div *ngIf="message && options.enableHtml" role="alert"
862 [class]="options.messageClass" [innerHTML]="message">
863 </div>
864 <div *ngIf="message && !options.enableHtml" role="alert"
865 [class]="options.messageClass" [attr.aria-label]="message">
866 {{ message }}
867 </div>
868 <div *ngIf="options.progressBar">
869 <div class="toast-progress" [style.width]="width() + '%'"></div>
870 </div>
871 `,
872 animations: [
873 trigger('flyInOut', [
874 state('inactive', style({ opacity: 0 })),
875 state('active', style({ opacity: 1 })),
876 state('removed', style({ opacity: 0 })),
877 transition('inactive => active', animate('{{ easeTime }}ms {{ easing }}')),
878 transition('active => removed', animate('{{ easeTime }}ms {{ easing }}')),
879 ]),
880 ],
881 preserveWhitespaces: false,
882 standalone: true,
883 imports: [NgIf],
884 changeDetection: ChangeDetectionStrategy.OnPush,
885 }]
886 }], ctorParameters: () => [{ type: ToastrService }, { type: ToastPackage }, { type: i0.NgZone }], propDecorators: { toastClasses: [{
887 type: HostBinding,
888 args: ['class']
889 }], _state: [{
890 type: HostBinding,
891 args: ['@flyInOut']
892 }], displayStyle: [{
893 type: HostBinding,
894 args: ['style.display']
895 }], tapToast: [{
896 type: HostListener,
897 args: ['click']
898 }], stickAround: [{
899 type: HostListener,
900 args: ['mouseenter']
901 }], delayedHideToast: [{
902 type: HostListener,
903 args: ['mouseleave']
904 }] } });
905
906const DefaultGlobalConfig = {
907 ...DefaultNoComponentGlobalConfig,
908 toastComponent: Toast,
909};
910/**
911 * @description
912 * Provides the `TOAST_CONFIG` token with the given config.
913 *
914 * @param config The config to configure toastr.
915 * @returns The environment providers.
916 *
917 * @example
918 * ```ts
919 * import { provideToastr } from 'ngx-toastr';
920 *
921 * bootstrap(AppComponent, {
922 * providers: [
923 * provideToastr({
924 * timeOut: 2000,
925 * positionClass: 'toast-top-right',
926 * }),
927 * ],
928 * })
929 */
930const provideToastr = (config = {}) => {
931 const providers = [
932 {
933 provide: TOAST_CONFIG,
934 useValue: {
935 default: DefaultGlobalConfig,
936 config,
937 }
938 }
939 ];
940 return makeEnvironmentProviders(providers);
941};
942
943class ToastrModule {
944 static forRoot(config = {}) {
945 return {
946 ngModule: ToastrModule,
947 providers: [provideToastr(config)],
948 };
949 }
950 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
951 static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.0", ngImport: i0, type: ToastrModule, imports: [Toast], exports: [Toast] });
952 static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrModule });
953}
954i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrModule, decorators: [{
955 type: NgModule,
956 args: [{
957 imports: [Toast],
958 exports: [Toast],
959 }]
960 }] });
961class ToastrComponentlessModule {
962 static forRoot(config = {}) {
963 return {
964 ngModule: ToastrModule,
965 providers: [
966 {
967 provide: TOAST_CONFIG,
968 useValue: {
969 default: DefaultNoComponentGlobalConfig,
970 config,
971 },
972 },
973 ],
974 };
975 }
976 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrComponentlessModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
977 static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.0", ngImport: i0, type: ToastrComponentlessModule });
978 static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrComponentlessModule });
979}
980i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastrComponentlessModule, decorators: [{
981 type: NgModule,
982 args: [{}]
983 }] });
984
985class ToastNoAnimation {
986 toastrService;
987 toastPackage;
988 appRef;
989 message;
990 title;
991 options;
992 duplicatesCount;
993 originalTimeout;
994 /** width of progress bar */
995 width = signal(-1);
996 /** a combination of toast type and options.toastClass */
997 toastClasses = '';
998 /** hides component when waiting to be displayed */
999 get displayStyle() {
1000 if (this.state() === 'inactive') {
1001 return 'none';
1002 }
1003 return null;
1004 }
1005 /** controls animation */
1006 state = signal('inactive');
1007 timeout;
1008 intervalId;
1009 hideTime;
1010 sub;
1011 sub1;
1012 sub2;
1013 sub3;
1014 constructor(toastrService, toastPackage, appRef) {
1015 this.toastrService = toastrService;
1016 this.toastPackage = toastPackage;
1017 this.appRef = appRef;
1018 this.message = toastPackage.message;
1019 this.title = toastPackage.title;
1020 this.options = toastPackage.config;
1021 this.originalTimeout = toastPackage.config.timeOut;
1022 this.toastClasses = `${toastPackage.toastType} ${toastPackage.config.toastClass}`;
1023 this.sub = toastPackage.toastRef.afterActivate().subscribe(() => {
1024 this.activateToast();
1025 });
1026 this.sub1 = toastPackage.toastRef.manualClosed().subscribe(() => {
1027 this.remove();
1028 });
1029 this.sub2 = toastPackage.toastRef.timeoutReset().subscribe(() => {
1030 this.resetTimeout();
1031 });
1032 this.sub3 = toastPackage.toastRef.countDuplicate().subscribe(count => {
1033 this.duplicatesCount = count;
1034 });
1035 }
1036 ngOnDestroy() {
1037 this.sub.unsubscribe();
1038 this.sub1.unsubscribe();
1039 this.sub2.unsubscribe();
1040 this.sub3.unsubscribe();
1041 clearInterval(this.intervalId);
1042 clearTimeout(this.timeout);
1043 }
1044 /**
1045 * activates toast and sets timeout
1046 */
1047 activateToast() {
1048 this.state.set('active');
1049 if (!(this.options.disableTimeOut === true || this.options.disableTimeOut === 'timeOut') && this.options.timeOut) {
1050 this.timeout = setTimeout(() => {
1051 this.remove();
1052 }, this.options.timeOut);
1053 this.hideTime = new Date().getTime() + this.options.timeOut;
1054 if (this.options.progressBar) {
1055 this.intervalId = setInterval(() => this.updateProgress(), 10);
1056 }
1057 }
1058 if (this.options.onActivateTick) {
1059 this.appRef.tick();
1060 }
1061 }
1062 /**
1063 * updates progress bar width
1064 */
1065 updateProgress() {
1066 if (this.width() === 0 || this.width() === 100 || !this.options.timeOut) {
1067 return;
1068 }
1069 const now = new Date().getTime();
1070 const remaining = this.hideTime - now;
1071 this.width.set((remaining / this.options.timeOut) * 100);
1072 if (this.options.progressAnimation === 'increasing') {
1073 this.width.update(width => 100 - width);
1074 }
1075 if (this.width() <= 0) {
1076 this.width.set(0);
1077 }
1078 if (this.width() >= 100) {
1079 this.width.set(100);
1080 }
1081 }
1082 resetTimeout() {
1083 clearTimeout(this.timeout);
1084 clearInterval(this.intervalId);
1085 this.state.set('active');
1086 this.options.timeOut = this.originalTimeout;
1087 this.timeout = setTimeout(() => this.remove(), this.originalTimeout);
1088 this.hideTime = new Date().getTime() + (this.originalTimeout || 0);
1089 this.width.set(-1);
1090 if (this.options.progressBar) {
1091 this.intervalId = setInterval(() => this.updateProgress(), 10);
1092 }
1093 }
1094 /**
1095 * tells toastrService to remove this toast after animation time
1096 */
1097 remove() {
1098 if (this.state() === 'removed') {
1099 return;
1100 }
1101 clearTimeout(this.timeout);
1102 this.state.set('removed');
1103 this.timeout = setTimeout(() => this.toastrService.remove(this.toastPackage.toastId));
1104 }
1105 tapToast() {
1106 if (this.state() === 'removed') {
1107 return;
1108 }
1109 this.toastPackage.triggerTap();
1110 if (this.options.tapToDismiss) {
1111 this.remove();
1112 }
1113 }
1114 stickAround() {
1115 if (this.state() === 'removed') {
1116 return;
1117 }
1118 clearTimeout(this.timeout);
1119 this.options.timeOut = 0;
1120 this.hideTime = 0;
1121 // disable progressBar
1122 clearInterval(this.intervalId);
1123 this.width.set(0);
1124 }
1125 delayedHideToast() {
1126 if ((this.options.disableTimeOut === true || this.options.disableTimeOut === 'extendedTimeOut') ||
1127 this.options.extendedTimeOut === 0 ||
1128 this.state() === 'removed') {
1129 return;
1130 }
1131 this.timeout = setTimeout(() => this.remove(), this.options.extendedTimeOut);
1132 this.options.timeOut = this.options.extendedTimeOut;
1133 this.hideTime = new Date().getTime() + (this.options.timeOut || 0);
1134 this.width.set(-1);
1135 if (this.options.progressBar) {
1136 this.intervalId = setInterval(() => this.updateProgress(), 10);
1137 }
1138 }
1139 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastNoAnimation, deps: [{ token: ToastrService }, { token: ToastPackage }, { token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Component });
1140 static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.0", type: ToastNoAnimation, isStandalone: true, selector: "[toast-component]", host: { listeners: { "click": "tapToast()", "mouseenter": "stickAround()", "mouseleave": "delayedHideToast()" }, properties: { "class": "this.toastClasses", "style.display": "this.displayStyle" } }, ngImport: i0, template: `
1141 <button *ngIf="options.closeButton" (click)="remove()" type="button" class="toast-close-button" aria-label="Close">
1142 <span aria-hidden="true">&times;</span>
1143 </button>
1144 <div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
1145 {{ title }} <ng-container *ngIf="duplicatesCount">[{{ duplicatesCount + 1 }}]</ng-container>
1146 </div>
1147 <div *ngIf="message && options.enableHtml" role="alert"
1148 [class]="options.messageClass" [innerHTML]="message">
1149 </div>
1150 <div *ngIf="message && !options.enableHtml" role="alert"
1151 [class]="options.messageClass" [attr.aria-label]="message">
1152 {{ message }}
1153 </div>
1154 <div *ngIf="options.progressBar">
1155 <div class="toast-progress" [style.width]="width() + '%'"></div>
1156 </div>
1157 `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1158}
1159i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastNoAnimation, decorators: [{
1160 type: Component,
1161 args: [{
1162 selector: '[toast-component]',
1163 template: `
1164 <button *ngIf="options.closeButton" (click)="remove()" type="button" class="toast-close-button" aria-label="Close">
1165 <span aria-hidden="true">&times;</span>
1166 </button>
1167 <div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
1168 {{ title }} <ng-container *ngIf="duplicatesCount">[{{ duplicatesCount + 1 }}]</ng-container>
1169 </div>
1170 <div *ngIf="message && options.enableHtml" role="alert"
1171 [class]="options.messageClass" [innerHTML]="message">
1172 </div>
1173 <div *ngIf="message && !options.enableHtml" role="alert"
1174 [class]="options.messageClass" [attr.aria-label]="message">
1175 {{ message }}
1176 </div>
1177 <div *ngIf="options.progressBar">
1178 <div class="toast-progress" [style.width]="width() + '%'"></div>
1179 </div>
1180 `,
1181 standalone: true,
1182 imports: [NgIf],
1183 changeDetection: ChangeDetectionStrategy.OnPush,
1184 }]
1185 }], ctorParameters: () => [{ type: ToastrService }, { type: ToastPackage }, { type: i0.ApplicationRef }], propDecorators: { toastClasses: [{
1186 type: HostBinding,
1187 args: ['class']
1188 }], displayStyle: [{
1189 type: HostBinding,
1190 args: ['style.display']
1191 }], tapToast: [{
1192 type: HostListener,
1193 args: ['click']
1194 }], stickAround: [{
1195 type: HostListener,
1196 args: ['mouseenter']
1197 }], delayedHideToast: [{
1198 type: HostListener,
1199 args: ['mouseleave']
1200 }] } });
1201const DefaultNoAnimationsGlobalConfig = {
1202 ...DefaultNoComponentGlobalConfig,
1203 toastComponent: ToastNoAnimation,
1204};
1205class ToastNoAnimationModule {
1206 static forRoot(config = {}) {
1207 return {
1208 ngModule: ToastNoAnimationModule,
1209 providers: [
1210 {
1211 provide: TOAST_CONFIG,
1212 useValue: {
1213 default: DefaultNoAnimationsGlobalConfig,
1214 config,
1215 },
1216 },
1217 ],
1218 };
1219 }
1220 static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastNoAnimationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1221 static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.0", ngImport: i0, type: ToastNoAnimationModule, imports: [ToastNoAnimation], exports: [ToastNoAnimation] });
1222 static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastNoAnimationModule });
1223}
1224i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: ToastNoAnimationModule, decorators: [{
1225 type: NgModule,
1226 args: [{
1227 imports: [ToastNoAnimation],
1228 exports: [ToastNoAnimation],
1229 }]
1230 }] });
1231
1232/**
1233 * Generated bundle index. Do not edit.
1234 */
1235
1236export { BasePortalHost, ComponentPortal, DefaultGlobalConfig, DefaultNoAnimationsGlobalConfig, DefaultNoComponentGlobalConfig, Overlay, OverlayContainer, OverlayRef, TOAST_CONFIG, Toast, ToastContainerDirective, ToastNoAnimation, ToastNoAnimationModule, ToastPackage, ToastRef, ToastrComponentlessModule, ToastrModule, ToastrService, provideToastr };
1237//# sourceMappingURL=ngx-toastr.mjs.map