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
JavaScript
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