UNPKG

ng-toast-stack

Version:

Beautiful & easy to use toast stack messages, having auto close, pause, resume functionality and customization.

193 lines (185 loc) 20.1 kB
import * as i0 from '@angular/core'; import { Injectable, Component, Input, NgModule } from '@angular/core'; import { trigger, transition, query, style, stagger, animate } from '@angular/animations'; import * as i3 from '@angular/common'; import { CommonModule } from '@angular/common'; class NgToastStackService { constructor() { this.id = 0; this.toastsBag = []; } push(data) { if (typeof data === 'string') data = { msg: data }; data.id = ++this.id; const toast = new NgToastStack(data); this.toastsBag.push(toast); if (toast.autoClose) { toast.timerRef = new Timer(() => { this.pop(toast.id); }, toast.autoCloseTimer); let index = this.toastsBag.findIndex((n) => n.id === toast.id); this.toastsBag.splice(index, 1, toast); } } pop(id) { if (!id) return; let index = this.toastsBag.findIndex((n) => n.id == id); this.toastsBag.splice(index, 1); } // Sugared methods static(data) { if (typeof data === 'string') { data = { msg: data, autoClose: false }; this.push(data); } else { this.push(Object.assign(Object.assign({}, data), { autoClose: false })); } } success(data) { if (typeof data === 'string') { data = { msg: data, type: 'success' }; this.push(data); } else { this.push(Object.assign(Object.assign({}, data), { type: 'success' })); } } error(data) { if (typeof data === 'string') { data = { msg: data, type: 'error' }; this.push(data); } else { this.push(Object.assign(Object.assign({}, data), { type: 'error' })); } } } NgToastStackService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); NgToastStackService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return []; } }); class NgToastStack { constructor(data) { this.id = data.id || 5000; this.type = data.type || 'success'; this.title = this.getTitle(data); this.msg = data.msg; this.autoClose = (data.hasOwnProperty('autoClose') && data.autoClose !== undefined) ? data.autoClose : true; this.autoCloseTimer = data.autoCloseTimer || 5000; this.icon = (data.hasOwnProperty('icon') && data.icon !== undefined) ? data.icon : true; this.pauseOnHover = (data.hasOwnProperty('pauseOnHover') && data.pauseOnHover !== undefined) ? data.pauseOnHover : true; } getTitle(data) { if (!data.title && data.type == 'error') return 'Error'; if (!data.title && (!data.type || data.type == 'success')) return 'Success'; return data.title || 'Message'; } } // Custom Timer with resume/pause class Timer { constructor(callback, delay) { this.delay = 0; this.start = 0; this.remaining = 0; this.remainingPercentage = 100; this.delay = delay; this.remaining = delay; this.callback = callback; this.resume(); } pause() { window.clearTimeout(this.timeoutId); this.timeoutId = undefined; window.clearInterval(this.intervalId); this.intervalId = undefined; } resume() { if (this.timeoutId && this.intervalId) { return; } this.start = Date.now(); this.timeoutId = window.setTimeout(this.callback, this.remaining); this.intervalId = window.setInterval(() => { if (this.remaining <= 0) window.clearInterval(this.intervalId); this.remaining -= 10; this.remainingPercentage = (this.remaining * 100) / this.delay; }, 10); } } class SvgIconComponent { constructor() { } ngOnInit() { } } SvgIconComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: SvgIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); SvgIconComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.2", type: SvgIconComponent, selector: "app-svg-icon", inputs: { type: "type" }, ngImport: i0, template: "<div class=\"ngx-svg-icon-container\">\n <svg viewBox=\"0 0 100 100\" class=\"ngx-svg-icon-animate\" *ngIf=\"type == 'success'\">\n <filter id=\"dropshadow\" height=\"100%\">\n <feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"3\" result=\"blur\" />\n <feFlood flood-color=\"rgba(76, 175, 80, 1)\" flood-opacity=\"0.5\" result=\"color\" />\n <feComposite in=\"color\" in2=\"blur\" operator=\"in\" result=\"blur\" />\n <feMerge>\n <feMergeNode />\n <feMergeNode in=\"SourceGraphic\" />\n </feMerge>\n </filter>\n <circle cx=\"50\" cy=\"50\" r=\"46.5\" fill=\"none\" stroke=\"rgba(76, 175, 80, 0.5)\" stroke-width=\"5\" />\n <path d=\"M67,93 A46.5,46.5 0,1,0 7,32 L43,67 L88,19\" fill=\"none\" stroke=\"rgba(76, 175, 80, 1)\" stroke-width=\"5\"\n stroke-linecap=\"round\" stroke-dasharray=\"80 1000\" stroke-dashoffset=\"-220\" style=\"filter: url(#dropshadow)\" />\n </svg>\n\n <svg *ngIf=\"type == 'error'\" class=\"ngx-svg-icon-error ngx-svg-icon-animate-pulse\" fill=\"none\" stroke=\"currentColor\"\n viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n</div>", styles: ["svg{width:100%;height:100%;position:absolute;top:0;left:0}svg.ngx-svg-icon-animate path{animation:dash .75s cubic-bezier(.39,.575,.565,1) both;animation-delay:.25s}@keyframes dash{0%{stroke-dashoffset:210}75%{stroke-dashoffset:-220}to{stroke-dashoffset:-205}}.ngx-svg-icon-container{position:relative;width:100%;height:100%}.ngx-svg-icon-error{width:100%;height:100%;color:#dc2626}.ngx-svg-icon-animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}\n"], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: SvgIconComponent, decorators: [{ type: Component, args: [{ selector: 'app-svg-icon', template: "<div class=\"ngx-svg-icon-container\">\n <svg viewBox=\"0 0 100 100\" class=\"ngx-svg-icon-animate\" *ngIf=\"type == 'success'\">\n <filter id=\"dropshadow\" height=\"100%\">\n <feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"3\" result=\"blur\" />\n <feFlood flood-color=\"rgba(76, 175, 80, 1)\" flood-opacity=\"0.5\" result=\"color\" />\n <feComposite in=\"color\" in2=\"blur\" operator=\"in\" result=\"blur\" />\n <feMerge>\n <feMergeNode />\n <feMergeNode in=\"SourceGraphic\" />\n </feMerge>\n </filter>\n <circle cx=\"50\" cy=\"50\" r=\"46.5\" fill=\"none\" stroke=\"rgba(76, 175, 80, 0.5)\" stroke-width=\"5\" />\n <path d=\"M67,93 A46.5,46.5 0,1,0 7,32 L43,67 L88,19\" fill=\"none\" stroke=\"rgba(76, 175, 80, 1)\" stroke-width=\"5\"\n stroke-linecap=\"round\" stroke-dasharray=\"80 1000\" stroke-dashoffset=\"-220\" style=\"filter: url(#dropshadow)\" />\n </svg>\n\n <svg *ngIf=\"type == 'error'\" class=\"ngx-svg-icon-error ngx-svg-icon-animate-pulse\" fill=\"none\" stroke=\"currentColor\"\n viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n</div>", styles: ["svg{width:100%;height:100%;position:absolute;top:0;left:0}svg.ngx-svg-icon-animate path{animation:dash .75s cubic-bezier(.39,.575,.565,1) both;animation-delay:.25s}@keyframes dash{0%{stroke-dashoffset:210}75%{stroke-dashoffset:-220}to{stroke-dashoffset:-205}}.ngx-svg-icon-container{position:relative;width:100%;height:100%}.ngx-svg-icon-error{width:100%;height:100%;color:#dc2626}.ngx-svg-icon-animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}\n"] }] }], ctorParameters: function () { return []; }, propDecorators: { type: [{ type: Input }] } }); const listAnimation = trigger('listAnimation', [ transition('* <=> *', [ query(':enter', [ style({ opacity: 0, transform: 'translateX(32px)' }), stagger('300ms', animate('300ms ease', style({ opacity: 1, transform: 'translateX(0px)' }))), ], { optional: true }), query(':leave', animate('200ms ease-in-out', style({ opacity: 0, transform: 'translateY(32px)' })), { optional: true, }), ]), ]); class NgToastStackComponent { constructor(ns) { this.ns = ns; } ngOnInit() { } } NgToastStackComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackComponent, deps: [{ token: NgToastStackService }], target: i0.ɵɵFactoryTarget.Component }); NgToastStackComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.2", type: NgToastStackComponent, selector: "ng-toast-stack", ngImport: i0, template: "<div class=\"ng-toast-stack-container\" [style]=\"ns.toastsBag.length == 0 ? 'display:none' : ''\">\n <div class=\"ng-toast-stack-list\" [@listAnimation]=\"ns.toastsBag.length\">\n <div class=\"ng-toast-stack\" *ngFor=\"let toast of ns.toastsBag.reverse()\" (click)=\"ns.pop(toast.id)\"\n (mouseover)=\"toast.pauseOnHover? toast.timerRef?.pause():''\"\n (mouseout)=\"toast.pauseOnHover? toast.timerRef?.resume():''\">\n <div class=\"ng-toast-stack-content-container-outer\">\n <div class=\"ng-toast-stack-content-container-inner\">\n <div class=\"ng-toast-stack-icon\" *ngIf=\"toast.icon\">\n <app-svg-icon [type]=\"toast.type\"></app-svg-icon>\n </div>\n\n <div class=\"ng-toast-stack-content\" [ngStyle]=\"{'margin-left': toast.icon?'12px':''}\">\n <h5 class=\"ng-toast-stack-title\">\n {{ toast?.title }}\n </h5>\n <p class=\"ng-toast-stack-message\">\n {{ toast.msg }}\n </p>\n </div>\n <div class=\"ng-toast-stack-icon-close\">\n <svg class=\"ng-toast-stack-icon-close-size\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n </div>\n </div>\n </div>\n <div *ngIf=\"toast.autoClose\" class=\"ng-toast-stack-timer\">\n <div class=\"ng-toast-stack-timer-bar\" [ngStyle]=\"{'width': (toast.timerRef.remainingPercentage)+'%'}\">\n </div>\n </div>\n </div>\n </div>\n</div>", styles: [".ng-toast-stack{position:relative;margin-top:1rem;overflow:hidden;background-color:#fff;border-radius:.5rem;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;cursor:pointer;pointer-events:auto;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\"}.ng-toast-stack-content-container-outer{height:100%;padding:8px 6px 8px 12px;overflow:hidden;border-radius:8px;box-shadow:0 1px 3px #0000001a,0 1px 2px -1px #0000001a}.ng-toast-stack-content-container-inner{display:flex;align-items:flex-start;justify-content:flex-start;flex-shrink:0}.ng-toast-stack-content{width:100%}.ng-toast-stack-title{font-size:14px;line-height:20px;font-weight:500;margin:0;color:#1f2937}.ng-toast-stack-message{height:100%;overflow-y:auto;color:#4b5563;font-size:12px;line-height:16px;max-height:384px;margin:0}.ng-toast-stack-icon-close{display:flex;color:#d1d5db;align-items:center;flex-shrink:0;padding-left:8px;transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.ng-toast-stack-content-container-outer:hover .ng-toast-stack-icon-close{color:#6b7280}.ng-toast-stack-icon{width:40px;height:40px}.ng-toast-stack-icon-close-size{width:1.5rem;height:1.5rem}.ng-toast-stack-timer{position:absolute;bottom:0;width:100%;height:.25rem;background-color:#e5e7ebbf}.ng-toast-stack-timer-bar{height:4px;background-color:#d1d5db}.ng-toast-stack-list{width:100%;max-width:24rem;z-index:1000}.ng-toast-stack-container{position:fixed;top:0px;right:0px;bottom:0px;left:0px;display:flex;align-items:flex-start;justify-content:flex-end;padding:1.5rem 1rem;pointer-events:none;z-index:1000}@media (min-width: 640px){.ng-toast-stack-container{padding:24px}}\n"], components: [{ type: SvgIconComponent, selector: "app-svg-icon", inputs: ["type"] }], directives: [{ type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], animations: [listAnimation] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackComponent, decorators: [{ type: Component, args: [{ selector: 'ng-toast-stack', animations: [listAnimation], template: "<div class=\"ng-toast-stack-container\" [style]=\"ns.toastsBag.length == 0 ? 'display:none' : ''\">\n <div class=\"ng-toast-stack-list\" [@listAnimation]=\"ns.toastsBag.length\">\n <div class=\"ng-toast-stack\" *ngFor=\"let toast of ns.toastsBag.reverse()\" (click)=\"ns.pop(toast.id)\"\n (mouseover)=\"toast.pauseOnHover? toast.timerRef?.pause():''\"\n (mouseout)=\"toast.pauseOnHover? toast.timerRef?.resume():''\">\n <div class=\"ng-toast-stack-content-container-outer\">\n <div class=\"ng-toast-stack-content-container-inner\">\n <div class=\"ng-toast-stack-icon\" *ngIf=\"toast.icon\">\n <app-svg-icon [type]=\"toast.type\"></app-svg-icon>\n </div>\n\n <div class=\"ng-toast-stack-content\" [ngStyle]=\"{'margin-left': toast.icon?'12px':''}\">\n <h5 class=\"ng-toast-stack-title\">\n {{ toast?.title }}\n </h5>\n <p class=\"ng-toast-stack-message\">\n {{ toast.msg }}\n </p>\n </div>\n <div class=\"ng-toast-stack-icon-close\">\n <svg class=\"ng-toast-stack-icon-close-size\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n </div>\n </div>\n </div>\n <div *ngIf=\"toast.autoClose\" class=\"ng-toast-stack-timer\">\n <div class=\"ng-toast-stack-timer-bar\" [ngStyle]=\"{'width': (toast.timerRef.remainingPercentage)+'%'}\">\n </div>\n </div>\n </div>\n </div>\n</div>", styles: [".ng-toast-stack{position:relative;margin-top:1rem;overflow:hidden;background-color:#fff;border-radius:.5rem;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;cursor:pointer;pointer-events:auto;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",Segoe UI Symbol,\"Noto Color Emoji\"}.ng-toast-stack-content-container-outer{height:100%;padding:8px 6px 8px 12px;overflow:hidden;border-radius:8px;box-shadow:0 1px 3px #0000001a,0 1px 2px -1px #0000001a}.ng-toast-stack-content-container-inner{display:flex;align-items:flex-start;justify-content:flex-start;flex-shrink:0}.ng-toast-stack-content{width:100%}.ng-toast-stack-title{font-size:14px;line-height:20px;font-weight:500;margin:0;color:#1f2937}.ng-toast-stack-message{height:100%;overflow-y:auto;color:#4b5563;font-size:12px;line-height:16px;max-height:384px;margin:0}.ng-toast-stack-icon-close{display:flex;color:#d1d5db;align-items:center;flex-shrink:0;padding-left:8px;transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.ng-toast-stack-content-container-outer:hover .ng-toast-stack-icon-close{color:#6b7280}.ng-toast-stack-icon{width:40px;height:40px}.ng-toast-stack-icon-close-size{width:1.5rem;height:1.5rem}.ng-toast-stack-timer{position:absolute;bottom:0;width:100%;height:.25rem;background-color:#e5e7ebbf}.ng-toast-stack-timer-bar{height:4px;background-color:#d1d5db}.ng-toast-stack-list{width:100%;max-width:24rem;z-index:1000}.ng-toast-stack-container{position:fixed;top:0px;right:0px;bottom:0px;left:0px;display:flex;align-items:flex-start;justify-content:flex-end;padding:1.5rem 1rem;pointer-events:none;z-index:1000}@media (min-width: 640px){.ng-toast-stack-container{padding:24px}}\n"] }] }], ctorParameters: function () { return [{ type: NgToastStackService }]; } }); class NgToastStackModule { } NgToastStackModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NgToastStackModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackModule, declarations: [NgToastStackComponent, SvgIconComponent], imports: [CommonModule], exports: [NgToastStackComponent] }); NgToastStackModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: NgToastStackModule, decorators: [{ type: NgModule, args: [{ declarations: [ NgToastStackComponent, SvgIconComponent ], imports: [ CommonModule ], exports: [ NgToastStackComponent ] }] }] }); /* * Public API Surface of ng-toast-stack */ /** * Generated bundle index. Do not edit. */ export { NgToastStack, NgToastStackComponent, NgToastStackModule, NgToastStackService, Timer }; //# sourceMappingURL=ng-toast-stack.mjs.map