UNPKG

ng-angular-popup

Version:

A modern, lightweight, and customizable toast notification library for Angular applications

289 lines (278 loc) 23.6 kB
import * as i0 from '@angular/core'; import { Injectable, input, inject, ElementRef, Directive, signal, Component, NgModule } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { trigger, transition, style, animate } from '@angular/animations'; import * as i2 from '@angular/common'; import { CommonModule } from '@angular/common'; class ToastMessage { constructor(message, type, title, duration = 2000) { this.message = message; this.type = type; this.title = title; this.duration = duration; this.id = new Date().getTime(); } } var ToastType; (function (ToastType) { ToastType["PRIMARY"] = "toast-primary"; ToastType["SECONDARY"] = "toast-secondary"; ToastType["SUCCESS"] = "toast-success"; ToastType["INFO"] = "toast-info"; ToastType["WARNING"] = "toast-warning"; ToastType["DANGER"] = "toast-danger"; })(ToastType || (ToastType = {})); /** * Service for displaying toast messages. */ class NgToastService { #defaultDuration; #toastMessageSource; /** * Constructs a new NgToastService instance. */ constructor() { this.#defaultDuration = 2000; this.#toastMessageSource = new Subject(); } /** * Displays a toast message. * @param message The message to display. * @param type The type of the toast message. * @param title The optional title of the toast message. * @param duration The duration in milliseconds for which the toast message should be displayed. Defaults to the default duration. */ toast(message, type, title, duration = this.#defaultDuration) { this.#toastMessageSource.next(new ToastMessage(message, type, title, duration)); } /** * Displays a success toast message. * @param message The message to display. * @param title The optional title of the toast message. * @param duration The duration in milliseconds for which the toast message should be displayed. Defaults to the default duration. */ success(message, title, duration = this.#defaultDuration) { this.toast(message, ToastType.SUCCESS, title, duration); } /** * Displays an info toast message. * @param message The message to display. * @param title The optional title of the toast message. * @param duration The duration in milliseconds for which the toast message should be displayed. Defaults to the default duration. */ info(message, title, duration = this.#defaultDuration) { this.toast(message, ToastType.INFO, title, duration); } /** * Displays a warning toast message. * @param message The message to display. * @param title The optional title of the toast message. * @param duration The duration in milliseconds for which the toast message should be displayed. Defaults to the default duration. */ warning(message, title, duration = this.#defaultDuration) { this.toast(message, ToastType.WARNING, title, duration); } /** * Displays a danger/error toast message. * @param message The message to display. * @param title The optional title of the toast message. * @param duration The duration in milliseconds for which the toast message should be displayed. Defaults to the default duration. */ danger(message, title, duration = this.#defaultDuration) { this.toast(message, ToastType.DANGER, title, duration); } /** * Returns an observable that emits the toast messages. * @returns An observable that emits the toast messages. */ onToastMessage() { return this.#toastMessageSource.asObservable(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); var ToasterPosition; (function (ToasterPosition) { ToasterPosition["TOP_LEFT"] = "toaster-top-left"; ToasterPosition["TOP_CENTER"] = "toaster-top-center"; ToasterPosition["TOP_RIGHT"] = "toaster-top-right"; ToasterPosition["BOTTOM_LEFT"] = "toaster-bottom-left"; ToasterPosition["BOTTOM_CENTER"] = "toaster-bottom-center"; ToasterPosition["BOTTOM_RIGHT"] = "toaster-bottom-right"; })(ToasterPosition || (ToasterPosition = {})); class ToastIconDirective { constructor() { this.type = input.required({ alias: 'toastIcon' }); this.#el = inject(ElementRef); } #el; ngOnInit() { this.setIcon(); } setIcon() { let svgContent; switch (this.type()) { case 'toast-success': svgContent = ` <svg width="14" height="14" viewBox="0 0 448 512"> <path fill="#ffffff" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/> </svg>`; break; case 'toast-danger': svgContent = ` <svg width="14" height="14" viewBox="0 0 384 512"> <path fill="#ffffff" d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/> </svg>`; break; case 'toast-info': svgContent = ` <svg width="14" height="14" viewBox="0 0 192 512"> <path fill="#ffffff" d="M48 80a48 48 0 1 1 96 0A48 48 0 1 1 48 80zM0 224c0-17.7 14.3-32 32-32H96c17.7 0 32 14.3 32 32V448h32c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H64V256H32c-17.7 0-32-14.3-32-32z"/> </svg>`; break; case 'toast-warning': svgContent = ` <svg width="14" height="14" viewBox="0 0 64 512"> <path fill="#ffffff" d="M64 64c0-17.7-14.3-32-32-32S0 46.3 0 64V320c0 17.7 14.3 32 32 32s32-14.3 32-32V64zM32 480a40 40 0 1 0 0-80 40 40 0 1 0 0 80z"/> </svg>`; break; default: svgContent = ''; } this.#el.nativeElement.innerHTML = svgContent; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: ToastIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.3", type: ToastIconDirective, isStandalone: false, selector: "[toastIcon]", inputs: { type: { classPropertyName: "type", publicName: "toastIcon", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: ToastIconDirective, decorators: [{ type: Directive, args: [{ selector: '[toastIcon]', standalone: false }] }] }); class NgToastComponent { constructor(toastService) { this.toastService = toastService; this.position = input(ToasterPosition.BOTTOM_RIGHT); this.width = input(350); this.messages = signal([]); this._toasterSubject$ = new Subject(); } ngOnInit() { this.toastService.onToastMessage() .pipe(takeUntil(this._toasterSubject$)) .subscribe(message => this._handleToastMessage(message)); } _handleToastMessage(message) { if (this._isToasterPositionTop()) { const messages = this.messages(); messages.unshift(message); this.messages.set(messages); } else { const messages = this.messages(); messages.push(message); this.messages.set(messages); } setTimeout(() => this._removeMessage(message), message.duration); } _isToasterPositionTop() { return this.position() === ToasterPosition.TOP_LEFT || this.position() === ToasterPosition.TOP_CENTER || this.position() === ToasterPosition.TOP_RIGHT; } _removeMessage(message) { const index = this.messages().findIndex(e => e.id === message.id); if (index > -1) { const messages = this.messages(); messages.splice(index, 1); this.messages.set(messages); } } remove(message) { this._removeMessage(message); } ngOnDestroy() { this._toasterSubject$.next(); this._toasterSubject$.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastComponent, deps: [{ token: NgToastService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: NgToastComponent, isStandalone: false, selector: "ng-toast", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div style.min-width=\"{{width()}}px\" style.max-width=\"{{width()}}px\" class=\"toaster\" [ngClass]=\"position()\">\n\n <div class=\"toast-message\" *ngFor=\"let message of messages()\" [ngClass]=\"message.type\" [@showHide]>\n <div class=\"flex-start-center gap-3\">\n <div [ngClass]=\"message.type\" class=\"toast-icon\">\n <div [toastIcon]=\"message.type\" class=\"toast-icon\"></div>\n </div>\n <div class=\"flex-col\">\n @if (message.title && message.title !== '') {\n <span class=\"msg-title\">{{message.title}}</span>\n }\n <span style.max-width=\"{{width() - 30}}px\" class=\"msg-summary\">{{message.message}}</span>\n </div>\n </div>\n <button (click)=\"remove(message)\" [ngClass]=\"message.type\" class=\"cross-icon\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 14 14\" fill=\"none\" class=\"p-icon p-toast-icon-close-icon\"\n aria-hidden=\"true\">\n <path\n d=\"M8.01186 7.00933L12.27 2.75116C12.341 2.68501 12.398 2.60524 12.4375 2.51661C12.4769 2.42798 12.4982 2.3323 12.4999 2.23529C12.5016 2.13827 12.4838 2.0419 12.4474 1.95194C12.4111 1.86197 12.357 1.78024 12.2884 1.71163C12.2198 1.64302 12.138 1.58893 12.0481 1.55259C11.9581 1.51625 11.8617 1.4984 11.7647 1.50011C11.6677 1.50182 11.572 1.52306 11.4834 1.56255C11.3948 1.60204 11.315 1.65898 11.2488 1.72997L6.99067 5.98814L2.7325 1.72997C2.59553 1.60234 2.41437 1.53286 2.22718 1.53616C2.03999 1.53946 1.8614 1.61529 1.72901 1.74767C1.59663 1.88006 1.5208 2.05865 1.5175 2.24584C1.5142 2.43303 1.58368 2.61419 1.71131 2.75116L5.96948 7.00933L1.71131 11.2675C1.576 11.403 1.5 11.5866 1.5 11.7781C1.5 11.9696 1.576 12.1532 1.71131 12.2887C1.84679 12.424 2.03043 12.5 2.2219 12.5C2.41338 12.5 2.59702 12.424 2.7325 12.2887L6.99067 8.03052L11.2488 12.2887C11.3843 12.424 11.568 12.5 11.7594 12.5C11.9509 12.5 12.1346 12.424 12.27 12.2887C12.4053 12.1532 12.4813 11.9696 12.4813 11.7781C12.4813 11.5866 12.4053 11.403 12.27 11.2675L8.01186 7.00933Z\"\n fill=\"currentColor\"></path>\n </svg></button>\n </div>\n</div>\n", styles: [".toaster{position:fixed;z-index:9999;min-width:300px;max-width:400px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.toaster .toast-message{padding:.75rem 1rem;margin-bottom:.75rem;border-radius:6px;background:#fff;box-shadow:0 1px 3px #0000001a;display:flex;justify-content:space-between;align-items:flex-start;word-break:break-word;animation:slideIn .2s ease-out;border:1px solid rgba(0,0,0,.05)}.toaster .toast-message.toast-primary{border-left:3px solid #3b82f6}.toaster .toast-message.toast-secondary{border-left:3px solid #475569}.toaster .toast-message.toast-success{border-left:3px solid #22c55e}.toaster .toast-message.toast-info{border-left:3px solid #0ea5e9}.toaster .toast-message.toast-warning{border-left:3px solid #eab308}.toaster .toast-message.toast-danger{border-left:3px solid #ef4444}.toaster .toast-message .content-wrapper{flex:1;margin-right:.75rem}.toaster .toast-message .msg-title{font-size:.875rem;color:#1e293b;font-weight:500;margin-bottom:.125rem;line-height:1.25}.toaster .toast-message .msg-summary{font-size:.8125rem;color:#64748b;font-weight:400;line-height:1.25}.toaster .toast-message .close-btn{background:transparent;border:none;color:#94a3b8;cursor:pointer;padding:2px;margin:-2px;display:flex;align-items:center;justify-content:center}.toaster.toaster-top-left{margin:1rem;top:0;left:0}.toaster.toaster-top-center{margin-top:1rem;top:0;left:50%;transform:translate(-50%)}.toaster.toaster-top-right{margin:1rem;top:0;right:0}.toaster.toaster-bottom-left{margin:1rem;bottom:0;left:0}.toaster.toaster-bottom-center{margin-bottom:1rem;bottom:0;left:50%;transform:translate(-50%)}.toaster.toaster-bottom-right{margin:1rem;bottom:0;right:0}@keyframes slideIn{0%{transform:translateY(-8px);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes fadeOut{to{opacity:0}}.toast-message.fade-out{animation:fadeOut .15s ease forwards}@media (max-width: 480px){.toaster{min-width:calc(100vw - 2rem);max-width:calc(100vw - 2rem);margin:.75rem}.toaster .toast-message{padding:.625rem .875rem}}.flex-start-center{display:flex;justify-content:flex-start;align-items:center}.gap-3{gap:10px}.p-icon{width:1rem;height:1rem}.toast-icon{border-radius:50%;color:#fff;padding:2px;display:flex;justify-content:center;align-items:center}.toast-icon.toast-success,.toast-icon.toast-primary,.toast-icon.toast-secondary{background:#34b189}.toast-icon.toast-info{background:#3b82f6}.toast-icon.toast-warning{background:#f59e0b}.toast-icon.toast-danger{background:#ff6767}.cross-icon{background:transparent;outline:none;border:none;cursor:pointer}.cross-icon.toast-success,.cross-icon.toast-primary,.cross-icon.toast-secondary{color:#34b189}.cross-icon.toast-info{color:#3b82f6}.cross-icon.toast-warning{color:#f59e0b}.cross-icon.toast-danger{color:#ff6767}.flex-col{display:flex;flex-direction:column;gap:.375rem}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: ToastIconDirective, selector: "[toastIcon]", inputs: ["toastIcon"] }], animations: [trigger('showHide', [ transition(':enter', [ style({ opacity: 0, transform: 'scaleX(0.98) scaleY(0)', position: 'relative' }), animate('250ms', style({ opacity: 1, transform: 'scale(1)' })) ]), transition(':leave', [ style({ opacity: 1, transform: 'scale(1)' }), animate('250ms', style({ opacity: 0, transform: 'scaleX(0.98) scaleY(0)' })) ]) ])] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastComponent, decorators: [{ type: Component, args: [{ selector: 'ng-toast', animations: [trigger('showHide', [ transition(':enter', [ style({ opacity: 0, transform: 'scaleX(0.98) scaleY(0)', position: 'relative' }), animate('250ms', style({ opacity: 1, transform: 'scale(1)' })) ]), transition(':leave', [ style({ opacity: 1, transform: 'scale(1)' }), animate('250ms', style({ opacity: 0, transform: 'scaleX(0.98) scaleY(0)' })) ]) ])], standalone: false, template: "<div style.min-width=\"{{width()}}px\" style.max-width=\"{{width()}}px\" class=\"toaster\" [ngClass]=\"position()\">\n\n <div class=\"toast-message\" *ngFor=\"let message of messages()\" [ngClass]=\"message.type\" [@showHide]>\n <div class=\"flex-start-center gap-3\">\n <div [ngClass]=\"message.type\" class=\"toast-icon\">\n <div [toastIcon]=\"message.type\" class=\"toast-icon\"></div>\n </div>\n <div class=\"flex-col\">\n @if (message.title && message.title !== '') {\n <span class=\"msg-title\">{{message.title}}</span>\n }\n <span style.max-width=\"{{width() - 30}}px\" class=\"msg-summary\">{{message.message}}</span>\n </div>\n </div>\n <button (click)=\"remove(message)\" [ngClass]=\"message.type\" class=\"cross-icon\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 14 14\" fill=\"none\" class=\"p-icon p-toast-icon-close-icon\"\n aria-hidden=\"true\">\n <path\n d=\"M8.01186 7.00933L12.27 2.75116C12.341 2.68501 12.398 2.60524 12.4375 2.51661C12.4769 2.42798 12.4982 2.3323 12.4999 2.23529C12.5016 2.13827 12.4838 2.0419 12.4474 1.95194C12.4111 1.86197 12.357 1.78024 12.2884 1.71163C12.2198 1.64302 12.138 1.58893 12.0481 1.55259C11.9581 1.51625 11.8617 1.4984 11.7647 1.50011C11.6677 1.50182 11.572 1.52306 11.4834 1.56255C11.3948 1.60204 11.315 1.65898 11.2488 1.72997L6.99067 5.98814L2.7325 1.72997C2.59553 1.60234 2.41437 1.53286 2.22718 1.53616C2.03999 1.53946 1.8614 1.61529 1.72901 1.74767C1.59663 1.88006 1.5208 2.05865 1.5175 2.24584C1.5142 2.43303 1.58368 2.61419 1.71131 2.75116L5.96948 7.00933L1.71131 11.2675C1.576 11.403 1.5 11.5866 1.5 11.7781C1.5 11.9696 1.576 12.1532 1.71131 12.2887C1.84679 12.424 2.03043 12.5 2.2219 12.5C2.41338 12.5 2.59702 12.424 2.7325 12.2887L6.99067 8.03052L11.2488 12.2887C11.3843 12.424 11.568 12.5 11.7594 12.5C11.9509 12.5 12.1346 12.424 12.27 12.2887C12.4053 12.1532 12.4813 11.9696 12.4813 11.7781C12.4813 11.5866 12.4053 11.403 12.27 11.2675L8.01186 7.00933Z\"\n fill=\"currentColor\"></path>\n </svg></button>\n </div>\n</div>\n", styles: [".toaster{position:fixed;z-index:9999;min-width:300px;max-width:400px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.toaster .toast-message{padding:.75rem 1rem;margin-bottom:.75rem;border-radius:6px;background:#fff;box-shadow:0 1px 3px #0000001a;display:flex;justify-content:space-between;align-items:flex-start;word-break:break-word;animation:slideIn .2s ease-out;border:1px solid rgba(0,0,0,.05)}.toaster .toast-message.toast-primary{border-left:3px solid #3b82f6}.toaster .toast-message.toast-secondary{border-left:3px solid #475569}.toaster .toast-message.toast-success{border-left:3px solid #22c55e}.toaster .toast-message.toast-info{border-left:3px solid #0ea5e9}.toaster .toast-message.toast-warning{border-left:3px solid #eab308}.toaster .toast-message.toast-danger{border-left:3px solid #ef4444}.toaster .toast-message .content-wrapper{flex:1;margin-right:.75rem}.toaster .toast-message .msg-title{font-size:.875rem;color:#1e293b;font-weight:500;margin-bottom:.125rem;line-height:1.25}.toaster .toast-message .msg-summary{font-size:.8125rem;color:#64748b;font-weight:400;line-height:1.25}.toaster .toast-message .close-btn{background:transparent;border:none;color:#94a3b8;cursor:pointer;padding:2px;margin:-2px;display:flex;align-items:center;justify-content:center}.toaster.toaster-top-left{margin:1rem;top:0;left:0}.toaster.toaster-top-center{margin-top:1rem;top:0;left:50%;transform:translate(-50%)}.toaster.toaster-top-right{margin:1rem;top:0;right:0}.toaster.toaster-bottom-left{margin:1rem;bottom:0;left:0}.toaster.toaster-bottom-center{margin-bottom:1rem;bottom:0;left:50%;transform:translate(-50%)}.toaster.toaster-bottom-right{margin:1rem;bottom:0;right:0}@keyframes slideIn{0%{transform:translateY(-8px);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes fadeOut{to{opacity:0}}.toast-message.fade-out{animation:fadeOut .15s ease forwards}@media (max-width: 480px){.toaster{min-width:calc(100vw - 2rem);max-width:calc(100vw - 2rem);margin:.75rem}.toaster .toast-message{padding:.625rem .875rem}}.flex-start-center{display:flex;justify-content:flex-start;align-items:center}.gap-3{gap:10px}.p-icon{width:1rem;height:1rem}.toast-icon{border-radius:50%;color:#fff;padding:2px;display:flex;justify-content:center;align-items:center}.toast-icon.toast-success,.toast-icon.toast-primary,.toast-icon.toast-secondary{background:#34b189}.toast-icon.toast-info{background:#3b82f6}.toast-icon.toast-warning{background:#f59e0b}.toast-icon.toast-danger{background:#ff6767}.cross-icon{background:transparent;outline:none;border:none;cursor:pointer}.cross-icon.toast-success,.cross-icon.toast-primary,.cross-icon.toast-secondary{color:#34b189}.cross-icon.toast-info{color:#3b82f6}.cross-icon.toast-warning{color:#f59e0b}.cross-icon.toast-danger{color:#ff6767}.flex-col{display:flex;flex-direction:column;gap:.375rem}\n"] }] }], ctorParameters: () => [{ type: NgToastService }] }); class NgToastModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.3", ngImport: i0, type: NgToastModule, declarations: [NgToastComponent, ToastIconDirective], imports: [CommonModule], exports: [NgToastComponent, ToastIconDirective] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastModule, imports: [CommonModule] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgToastModule, decorators: [{ type: NgModule, args: [{ declarations: [NgToastComponent, ToastIconDirective], imports: [CommonModule], exports: [NgToastComponent, ToastIconDirective], }] }] }); /* * Public API Surface of ng-toast */ /** * Generated bundle index. Do not edit. */ export { NgToastComponent, NgToastModule, NgToastService, ToastIconDirective, ToastMessage, ToastType, ToasterPosition }; //# sourceMappingURL=ng-angular-popup.mjs.map