UNPKG

@hyahfoufi/ng-snotify

Version:

Angular 2+ alternative notifications center

1,013 lines (994 loc) 40.1 kB
import * as i0 from '@angular/core'; import { Injectable, Inject, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, Pipe, EventEmitter, Output, NgModule } from '@angular/core'; import { __decorate } from 'tslib'; import { Subject, from } from 'rxjs'; import * as i2 from '@angular/common'; import { CommonModule } from '@angular/common'; /** * Toast style. */ var SnotifyStyle; (function (SnotifyStyle) { SnotifyStyle["simple"] = "simple"; SnotifyStyle["success"] = "success"; SnotifyStyle["error"] = "error"; SnotifyStyle["warning"] = "warning"; SnotifyStyle["info"] = "info"; SnotifyStyle["async"] = "async"; SnotifyStyle["confirm"] = "confirm"; SnotifyStyle["prompt"] = "prompt"; })(SnotifyStyle || (SnotifyStyle = {})); /** * Transform arguments to Snotify object * @param target any * @param propertyKey SnotifyTypeType * @param descriptor PropertyDescriptor * @returns Snotify */ function TransformArgument(target, propertyKey, descriptor) { if (propertyKey === SnotifyStyle.async) { return { value(...args) { let result; if (args.length === 2) { result = { title: null, body: args[0], config: null, action: args[1] }; } else if (args.length === 3) { if (typeof args[1] === 'string') { result = { title: args[1], body: args[0], config: null, action: args[2] }; } else { result = { title: null, body: args[0], config: args[2], action: args[1] }; } } else { result = { title: args[1], body: args[0], config: args[3], action: args[2] }; } return descriptor.value.apply(this, [result]); } }; } else { return { value(...args) { let result; if (args.length === 1) { result = { title: null, body: args[0], config: null }; } else if (args.length === 3) { result = { title: args[1], body: args[0], config: args[2] }; } else { result = { title: null, config: null, body: args[0], [typeof args[1] === 'string' ? 'title' : 'config']: args[1] }; } return descriptor.value.apply(this, [result]); } }; } } /** * Generates random id * @return number */ function uuid() { return Math.floor(Math.random() * (Date.now() - 1)) + 1; } /** * Simple is object check. * @param item Object<any> * @returns boolean */ function isObject(item) { return item && typeof item === 'object' && !Array.isArray(item); } /** * Deep merge objects. * @param sources Array<Object<any>> * @returns Object<any> */ function mergeDeep(...sources) { const target = {}; if (!sources.length) { return target; } while (sources.length > 0) { const source = sources.shift(); if (isObject(source)) { for (const key in source) { if (isObject(source[key])) { target[key] = mergeDeep(target[key], source[key]); } else { Object.assign(target, { [key]: source[key] }); } } } } return target; } function animate(start, duration, callback) { let endTime; requestAnimationFrame(timestamp => (endTime = timestamp + duration)); const calculate = () => { requestAnimationFrame(timestamp => { const runtime = timestamp - endTime; const progress = Math.min(runtime / duration, 1) + start; if (runtime < duration) { if (callback(+(100 * progress).toFixed(2), progress)) { calculate(); } } }); }; } /** * Defines toast style depending on method name * @param target any * @param propertyKey SnotifyTypeType * @param descriptor PropertyDescriptor * @returns value: ((...args: any[]) => any) */ function SetToastType(target, propertyKey, descriptor) { return { value(...args) { args[0].config = { ...args[0].config, type: propertyKey }; return descriptor.value.apply(this, args); } }; } // @TODO remove method in observable way /** * Toast main model */ class SnotifyToast { id; title; body; config; /** * Emits SnotifyEventType */ eventEmitter = new Subject(); /** * Holds all subscribers because we need to unsubscribe from all before toast get destroyed */ eventsHolder = []; /** * Toast prompt value */ value; /** * Toast validator */ valid; constructor(id, title, body, config) { this.id = id; this.title = title; this.body = body; this.config = config; if (this.config.type === SnotifyStyle.prompt) { this.value = ''; } this.on('hidden', () => { this.eventsHolder.forEach((subscription) => { subscription.unsubscribe(); }); }); } /** * Subscribe to toast events * @returns this * @param event SnotifyEventType * @param action (toast: this) => void */ on(event, action) { this.eventsHolder.push(this.eventEmitter.subscribe((e) => { if (e === event) { action(this); } })); return this; } /** * Tests if a toast equals this toast. * @returns boolean true then equals else false. * @param toast SnotifyToast */ equals(toast) { return this.body === toast.body && this.title === toast.title && this.config.type === toast.config.type; } } /** * SnotifyService - create, remove, config toasts */ // tslint:disable:unified-signatures class SnotifyService { config; emitter = new Subject(); toastChanged = new Subject(); toastDeleted = new Subject(); notifications = []; constructor(config) { this.config = config; } /** * emit changes in notifications array */ emit() { this.emitter.next(this.notifications.slice()); } /** * returns SnotifyToast object * @param id Number * @return SnotifyToast|undefined */ get(id) { return this.notifications.find(toast => toast.id === id); } /** * add SnotifyToast to notifications array * @param toast SnotifyToast */ add(toast) { if (this.config.global.filterDuplicates && this.containsToast(toast)) { return; } if (this.config.global.newOnTop) { this.notifications.unshift(toast); } else { this.notifications.push(toast); } this.emit(); } /** * checks if the toast is in the collection. * @param inToast SnotifyToast * @returns boolean */ containsToast(inToast) { return this.notifications.some(toast => toast.equals(inToast)); } /** * If ID passed, emits toast animation remove, if ID & REMOVE passed, removes toast from notifications array * @param id number * @param remove boolean */ remove(id, remove) { if (!id) { return this.clear(); } else if (remove) { this.notifications = this.notifications.filter(toast => toast.id !== id); return this.emit(); } this.toastDeleted.next(id); } /** * Clear notifications array */ clear() { this.notifications = []; this.emit(); } /** * Creates toast and add it to array, returns toast id * @param snotify Snotify * @return number */ create(snotify) { const config = mergeDeep(this.config.toast, this.config.type[snotify.config.type], snotify.config); const toast = new SnotifyToast(uuid(), snotify.title, snotify.body, config); this.add(toast); return toast; } setDefaults(defaults) { return (this.config = mergeDeep(this.config, defaults)); } /** * Transform toast arguments into Snotify object */ simple(args) { return this.create(args); } /** * Transform toast arguments into Snotify object */ success(args) { return this.create(args); } /** * Transform toast arguments into Snotify object */ error(args) { return this.create(args); } /** * Transform toast arguments into Snotify object */ info(args) { return this.create(args); } /** * Transform toast arguments into Snotify object */ warning(args) { return this.create(args); } /** * Transform toast arguments into Snotify object */ confirm(args) { return this.create(args); } /** * Transform toast arguments into Snotify object */ prompt(args) { return this.create(args); } /** * Transform toast arguments into Snotify object */ async(args) { let async; if (args.action instanceof Promise) { async = from(args.action); } else { async = args.action; } const toast = this.create(args); toast.on('mounted', () => { const subscription = async.subscribe((next) => { this.mergeToast(toast, next); }, (error) => { this.mergeToast(toast, error, SnotifyStyle.error); subscription.unsubscribe(); }, () => { this.mergeToast(toast, {}, SnotifyStyle.success); subscription.unsubscribe(); }); }); return toast; } mergeToast(toast, next, type) { if (next.body) { toast.body = next.body; } if (next.title) { toast.title = next.title; } if (type) { toast.config = mergeDeep(toast.config, this.config.global, this.config.toast[type], { type }, next.config); } else { toast.config = mergeDeep(toast.config, next.config); } if (next.html) { toast.config.html = next.html; } this.emit(); this.toastChanged.next(toast); } /** * Creates empty toast with html string inside * @param html string | SafeHtml * @param config SnotifyToastConfig * @returns number */ html(html, config) { return this.create({ title: null, body: null, config: { ...config, ...{ html } } }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyService, deps: [{ token: 'SnotifyToastConfig' }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyService }); } __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "simple", null); __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "success", null); __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "error", null); __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "info", null); __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "warning", null); __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "confirm", null); __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "prompt", null); __decorate([ TransformArgument /** * Determines current toast type and collects default configuration */ , SetToastType ], SnotifyService.prototype, "async", null); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: ['SnotifyToastConfig'] }] }], propDecorators: { simple: [], success: [], error: [], info: [], warning: [], confirm: [], prompt: [], async: [] } }); /** * Buttons component */ class ButtonsComponent { service; /** * Get buttons Array */ toast; constructor(service) { this.service = service; } /** * remove toast */ remove() { this.service.remove(this.toast.id); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ButtonsComponent, deps: [{ token: SnotifyService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ButtonsComponent, selector: "ng-snotify-button", inputs: { toast: "toast" }, ngImport: i0, template: "<div class=\"snotifyToast__buttons\">\n <button\n type=\"button\"\n *ngFor=\"let button of toast.config.buttons\"\n [ngClass]=\"{ 'snotifyToast__buttons--bold': button.bold }\"\n (click)=\"button.action ? button.action(toast) : remove()\"\n >\n {{ button.text }}\n </button>\n</div>\n", dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ButtonsComponent, decorators: [{ type: Component, args: [{ selector: 'ng-snotify-button', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"snotifyToast__buttons\">\n <button\n type=\"button\"\n *ngFor=\"let button of toast.config.buttons\"\n [ngClass]=\"{ 'snotifyToast__buttons--bold': button.bold }\"\n (click)=\"button.action ? button.action(toast) : remove()\"\n >\n {{ button.text }}\n </button>\n</div>\n" }] }], ctorParameters: () => [{ type: SnotifyService }], propDecorators: { toast: [{ type: Input }] } }); /** * Truncate toast text pipe */ class TruncatePipe { transform(value, ...args) { let limit = 40; let trail = '...'; if (args.length > 0) { limit = args.length > 0 ? parseInt(args[0], 10) : limit; trail = args.length > 1 ? args[1] : trail; } return value.length > limit ? value.substring(0, limit) + trail : value; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: TruncatePipe, name: "truncate" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TruncatePipe, decorators: [{ type: Pipe, args: [{ name: 'truncate' }] }] }); /** * Prompt component. Part of PROMPT type */ class PromptComponent { /** * Get PROMPT placeholder */ toast; /** * Is PROMPT focused */ isPromptFocused = false; handleInput($event) { this.toast.value = $event.target.value; this.toast.eventEmitter.next('input'); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PromptComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: PromptComponent, selector: "ng-snotify-prompt", inputs: { toast: "toast" }, ngImport: i0, template: "<span class=\"snotifyToast__input\" [ngClass]=\"{ 'snotifyToast__input--filled': isPromptFocused }\">\n <input\n (input)=\"handleInput($event)\"\n autofocus\n class=\"snotifyToast__input__field\"\n type=\"text\"\n [id]=\"toast.id\"\n (focus)=\"isPromptFocused = true\"\n (blur)=\"isPromptFocused = !!toast.value.length\"\n />\n <label class=\"snotifyToast__input__label\" [for]=\"toast.id\">\n <span class=\"snotifyToast__input__labelContent\">{{ toast.config.placeholder | truncate }}</span>\n </label>\n</span>\n", dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: TruncatePipe, name: "truncate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PromptComponent, decorators: [{ type: Component, args: [{ selector: 'ng-snotify-prompt', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<span class=\"snotifyToast__input\" [ngClass]=\"{ 'snotifyToast__input--filled': isPromptFocused }\">\n <input\n (input)=\"handleInput($event)\"\n autofocus\n class=\"snotifyToast__input__field\"\n type=\"text\"\n [id]=\"toast.id\"\n (focus)=\"isPromptFocused = true\"\n (blur)=\"isPromptFocused = !!toast.value.length\"\n />\n <label class=\"snotifyToast__input__label\" [for]=\"toast.id\">\n <span class=\"snotifyToast__input__labelContent\">{{ toast.config.placeholder | truncate }}</span>\n </label>\n</span>\n" }] }], propDecorators: { toast: [{ type: Input }] } }); /** * Toast position */ var SnotifyPosition; (function (SnotifyPosition) { SnotifyPosition["leftTop"] = "leftTop"; SnotifyPosition["leftCenter"] = "leftCenter"; SnotifyPosition["leftBottom"] = "leftBottom"; SnotifyPosition["rightTop"] = "rightTop"; SnotifyPosition["rightCenter"] = "rightCenter"; SnotifyPosition["rightBottom"] = "rightBottom"; SnotifyPosition["centerTop"] = "centerTop"; SnotifyPosition["centerCenter"] = "centerCenter"; SnotifyPosition["centerBottom"] = "centerBottom"; })(SnotifyPosition || (SnotifyPosition = {})); class ToastComponent { service; /** * Get toast from notifications array */ toast; stateChanged = new EventEmitter(); toastDeletedSubscription; toastChangedSubscription; /** * requestAnimationFrame id */ animationFrame; /** * Toast state */ state = { paused: false, progress: 0, animation: '', isDestroying: false, promptType: SnotifyStyle.prompt }; constructor(service) { this.service = service; } // Lifecycles /** * Init base options. Subscribe to toast changed, toast deleted */ ngOnInit() { this.toastChangedSubscription = this.service.toastChanged.subscribe((toast) => { if (this.toast.id === toast.id) { this.initToast(); } }); this.toastDeletedSubscription = this.service.toastDeleted.subscribe(id => { if (this.toast.id === id) { this.onRemove(); } }); if (!this.toast.config.timeout) { this.toast.config.showProgressBar = false; } this.toast.eventEmitter.next('mounted'); this.state.animation = 'snotifyToast--in'; } ngAfterContentInit() { setTimeout(() => { this.stateChanged.emit('beforeShow'); this.toast.eventEmitter.next('beforeShow'); this.state.animation = this.toast.config.animation.enter; }, this.service.config.toast.animation.time / 5); // time to show toast push animation (snotifyToast--in) } /** * Unsubscribe subscriptions */ ngOnDestroy() { cancelAnimationFrame(this.animationFrame); this.toast.eventEmitter.next('destroyed'); this.toastChangedSubscription.unsubscribe(); this.toastDeletedSubscription.unsubscribe(); } /* Event hooks */ /** * Trigger OnClick lifecycle */ onClick() { this.toast.eventEmitter.next('click'); if (this.toast.config.closeOnClick) { this.service.remove(this.toast.id); } } /** * Trigger beforeDestroy lifecycle. Removes toast */ onRemove() { this.state.isDestroying = true; this.toast.eventEmitter.next('beforeHide'); this.stateChanged.emit('beforeHide'); this.state.animation = this.toast.config.animation.exit; setTimeout(() => { this.stateChanged.emit('hidden'); this.state.animation = 'snotifyToast--out'; this.toast.eventEmitter.next('hidden'); setTimeout(() => this.service.remove(this.toast.id, true), this.toast.config.animation.time / 2); }, this.toast.config.animation.time / 2); } /** * Trigger onHoverEnter lifecycle */ onMouseEnter() { this.toast.eventEmitter.next('mouseenter'); if (this.toast.config.pauseOnHover) { this.state.paused = true; } } /** * Trigger onHoverLeave lifecycle */ onMouseLeave() { if (this.toast.config.pauseOnHover && this.toast.config.timeout) { this.state.paused = false; this.startTimeout(this.toast.config.timeout * this.state.progress); } this.toast.eventEmitter.next('mouseleave'); } /** * Remove toast completely after animation */ onExitTransitionEnd() { if (this.state.isDestroying) { return; } this.initToast(); this.toast.eventEmitter.next('shown'); } /* Common */ /** * Initialize base toast config * */ initToast() { if (this.toast.config.timeout > 0) { this.startTimeout(0); } } /** * Start progress bar * @param startTime number */ startTimeout(startTime = 0) { const start = performance.now(); const calculate = () => { this.animationFrame = requestAnimationFrame(timestamp => { const runtime = timestamp + startTime - start; const progress = Math.min(runtime / this.toast.config.timeout, 1); if (this.state.paused) { cancelAnimationFrame(this.animationFrame); } else if (runtime < this.toast.config.timeout) { this.state.progress = progress; calculate(); } else { this.state.progress = 1; cancelAnimationFrame(this.animationFrame); this.service.remove(this.toast.id); } }); }; calculate(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToastComponent, deps: [{ token: SnotifyService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ToastComponent, selector: "ng-snotify-toast", inputs: { toast: "toast" }, outputs: { stateChanged: "stateChanged" }, ngImport: i0, template: "<div\n [attr.role]=\"toast.config.type === state.promptType ? 'dialog' : 'alert'\"\n [attr.aria-labelledby]=\"'snotify_' + toast.id\"\n [attr.aria-modal]=\"toast.config.type === state.promptType\"\n [ngClass]=\"[\n 'snotifyToast animated',\n 'snotify-' + toast.config.type,\n state.animation,\n toast.valid === undefined ? '' : toast.valid ? 'snotifyToast--valid' : 'snotifyToast--invalid'\n ]\"\n [ngStyle]=\"{\n '-webkit-transition': toast.config.animation.time + 'ms',\n transition: toast.config.animation.time + 'ms',\n '-webkit-animation-duration': toast.config.animation.time + 'ms',\n 'animation-duration': toast.config.animation.time + 'ms'\n }\"\n (animationend)=\"onExitTransitionEnd()\"\n (click)=\"onClick()\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n>\n <div class=\"snotifyToast__progressBar\" *ngIf=\"toast.config.showProgressBar\">\n <span class=\"snotifyToast__progressBar__percentage\" [ngStyle]=\"{ width: state.progress * 100 + '%' }\"></span>\n </div>\n <div class=\"snotifyToast__inner\" *ngIf=\"!toast.config.html; else toastHTML\">\n <div class=\"snotifyToast__title\" [attr.id]=\"'snotify_' + toast.id\" *ngIf=\"toast.title\">\n {{ toast.title | truncate: toast.config.titleMaxLength }}\n </div>\n <div class=\"snotifyToast__body\" *ngIf=\"toast.body\">{{ toast.body | truncate: toast.config.bodyMaxLength }}</div>\n <ng-snotify-prompt *ngIf=\"toast.config.type === state.promptType\" [toast]=\"toast\"> </ng-snotify-prompt>\n <div\n *ngIf=\"!toast.config.icon; else elseBlock\"\n [ngClass]=\"['snotify-icon', toast.config.iconClass || 'snotify-icon--' + toast.config.type]\"\n ></div>\n <ng-template #elseBlock>\n <img class=\"snotify-icon\" [src]=\"toast.config.icon\" />\n </ng-template>\n </div>\n <ng-template #toastHTML>\n <div class=\"snotifyToast__inner\" [innerHTML]=\"toast.config.html\"></div>\n </ng-template>\n <ng-snotify-button *ngIf=\"toast.config.buttons\" [toast]=\"toast\"></ng-snotify-button>\n</div>\n", dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: ButtonsComponent, selector: "ng-snotify-button", inputs: ["toast"] }, { kind: "component", type: PromptComponent, selector: "ng-snotify-prompt", inputs: ["toast"] }, { kind: "pipe", type: TruncatePipe, name: "truncate" }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ToastComponent, decorators: [{ type: Component, args: [{ selector: 'ng-snotify-toast', encapsulation: ViewEncapsulation.None, template: "<div\n [attr.role]=\"toast.config.type === state.promptType ? 'dialog' : 'alert'\"\n [attr.aria-labelledby]=\"'snotify_' + toast.id\"\n [attr.aria-modal]=\"toast.config.type === state.promptType\"\n [ngClass]=\"[\n 'snotifyToast animated',\n 'snotify-' + toast.config.type,\n state.animation,\n toast.valid === undefined ? '' : toast.valid ? 'snotifyToast--valid' : 'snotifyToast--invalid'\n ]\"\n [ngStyle]=\"{\n '-webkit-transition': toast.config.animation.time + 'ms',\n transition: toast.config.animation.time + 'ms',\n '-webkit-animation-duration': toast.config.animation.time + 'ms',\n 'animation-duration': toast.config.animation.time + 'ms'\n }\"\n (animationend)=\"onExitTransitionEnd()\"\n (click)=\"onClick()\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n>\n <div class=\"snotifyToast__progressBar\" *ngIf=\"toast.config.showProgressBar\">\n <span class=\"snotifyToast__progressBar__percentage\" [ngStyle]=\"{ width: state.progress * 100 + '%' }\"></span>\n </div>\n <div class=\"snotifyToast__inner\" *ngIf=\"!toast.config.html; else toastHTML\">\n <div class=\"snotifyToast__title\" [attr.id]=\"'snotify_' + toast.id\" *ngIf=\"toast.title\">\n {{ toast.title | truncate: toast.config.titleMaxLength }}\n </div>\n <div class=\"snotifyToast__body\" *ngIf=\"toast.body\">{{ toast.body | truncate: toast.config.bodyMaxLength }}</div>\n <ng-snotify-prompt *ngIf=\"toast.config.type === state.promptType\" [toast]=\"toast\"> </ng-snotify-prompt>\n <div\n *ngIf=\"!toast.config.icon; else elseBlock\"\n [ngClass]=\"['snotify-icon', toast.config.iconClass || 'snotify-icon--' + toast.config.type]\"\n ></div>\n <ng-template #elseBlock>\n <img class=\"snotify-icon\" [src]=\"toast.config.icon\" />\n </ng-template>\n </div>\n <ng-template #toastHTML>\n <div class=\"snotifyToast__inner\" [innerHTML]=\"toast.config.html\"></div>\n </ng-template>\n <ng-snotify-button *ngIf=\"toast.config.buttons\" [toast]=\"toast\"></ng-snotify-button>\n</div>\n" }] }], ctorParameters: () => [{ type: SnotifyService }], propDecorators: { toast: [{ type: Input }], stateChanged: [{ type: Output }] } }); /** * Extract object keys pipe */ class KeysPipe { transform(value, args = null) { if (!value) { return value; } return Object.keys(value); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: KeysPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: KeysPipe, name: "keys", pure: false }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: KeysPipe, decorators: [{ type: Pipe, args: [{ name: 'keys', pure: false }] }] }); class SnotifyComponent { service; /** * Toasts array */ notifications; /** * Toasts emitter */ emitter; /** * Helper for slice pipe (maxOnScreen) */ dockSizeA; /** * Helper for slice pipe (maxOnScreen) */ dockSizeB; /** * Helper for slice pipe (maxAtPosition) */ blockSizeA; /** * Helper for slice pipe (maxAtPosition) */ blockSizeB; /** * Backdrop Opacity */ backdrop = -1; /** * How many toasts with backdrop in current queue */ withBackdrop; constructor(service) { this.service = service; } /** * Init base options. Subscribe to options, lifecycle change */ ngOnInit() { this.emitter = this.service.emitter.subscribe((toasts) => { if (this.service.config.global.newOnTop) { this.dockSizeA = -this.service.config.global.maxOnScreen; this.dockSizeB = undefined; this.blockSizeA = -this.service.config.global.maxAtPosition; this.blockSizeB = undefined; this.withBackdrop = toasts.filter(toast => toast.config.backdrop >= 0); } else { this.dockSizeA = 0; this.dockSizeB = this.service.config.global.maxOnScreen; this.blockSizeA = 0; this.blockSizeB = this.service.config.global.maxAtPosition; this.withBackdrop = toasts.filter(toast => toast.config.backdrop >= 0).reverse(); } this.notifications = this.splitToasts(toasts.slice(this.dockSizeA, this.dockSizeB)); this.stateChanged('mounted'); }); } // TODO: fix backdrop if more than one toast called in a row /** * Changes the backdrop opacity * @param event SnotifyEventType */ stateChanged(event) { if (!this.withBackdrop.length) { if (this.backdrop >= 0) { this.backdrop = -1; } return; } switch (event) { case 'mounted': if (this.backdrop < 0) { this.backdrop = 0; } break; case 'beforeShow': this.backdrop = this.withBackdrop[this.withBackdrop.length - 1].config.backdrop; break; case 'beforeHide': if (this.withBackdrop.length === 1) { this.backdrop = 0; } break; case 'hidden': if (this.withBackdrop.length === 1) { this.backdrop = -1; } break; } } /** * Split toasts toasts into different objects * @param toasts SnotifyToast[] * @returns SnotifyNotifications */ splitToasts(toasts) { const result = {}; for (const property in SnotifyPosition) { if (SnotifyPosition.hasOwnProperty(property)) { result[SnotifyPosition[property]] = []; } } toasts.forEach((toast) => { result[toast.config.position].push(toast); }); return result; } /** * Unsubscribe subscriptions */ ngOnDestroy() { this.emitter.unsubscribe(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyComponent, deps: [{ token: SnotifyService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SnotifyComponent, selector: "ng-snotify", ngImport: i0, template: "<div class=\"snotify-backdrop\" *ngIf=\"backdrop >= 0\" [style.opacity]=\"backdrop\"></div>\n<div *ngFor=\"let position of notifications | keys\" class=\"snotify snotify-{{ position }}\">\n <ng-snotify-toast\n *ngFor=\"let notification of notifications[position] | slice: blockSizeA:blockSizeB\"\n [toast]=\"notification\"\n (stateChanged)=\"stateChanged($event)\"\n >\n </ng-snotify-toast>\n</div>\n", dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ToastComponent, selector: "ng-snotify-toast", inputs: ["toast"], outputs: ["stateChanged"] }, { kind: "pipe", type: i2.SlicePipe, name: "slice" }, { kind: "pipe", type: KeysPipe, name: "keys" }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyComponent, decorators: [{ type: Component, args: [{ selector: 'ng-snotify', encapsulation: ViewEncapsulation.None, template: "<div class=\"snotify-backdrop\" *ngIf=\"backdrop >= 0\" [style.opacity]=\"backdrop\"></div>\n<div *ngFor=\"let position of notifications | keys\" class=\"snotify snotify-{{ position }}\">\n <ng-snotify-toast\n *ngFor=\"let notification of notifications[position] | slice: blockSizeA:blockSizeB\"\n [toast]=\"notification\"\n (stateChanged)=\"stateChanged($event)\"\n >\n </ng-snotify-toast>\n</div>\n" }] }], ctorParameters: () => [{ type: SnotifyService }] }); class SnotifyModule { static forRoot() { return { ngModule: SnotifyModule, providers: [SnotifyService] }; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: SnotifyModule, declarations: [SnotifyComponent, ToastComponent, TruncatePipe, ButtonsComponent, PromptComponent, KeysPipe], imports: [CommonModule], exports: [SnotifyComponent, TruncatePipe, KeysPipe] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyModule, imports: [CommonModule] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SnotifyModule, decorators: [{ type: NgModule, args: [{ imports: [CommonModule], declarations: [SnotifyComponent, ToastComponent, TruncatePipe, ButtonsComponent, PromptComponent, KeysPipe], exports: [SnotifyComponent, TruncatePipe, KeysPipe] }] }] }); /** * Snotify default configuration object */ const ToastDefaults = { global: { newOnTop: true, maxOnScreen: 8, maxAtPosition: 8, filterDuplicates: false }, toast: { type: SnotifyStyle.simple, showProgressBar: true, timeout: 2000, closeOnClick: true, pauseOnHover: true, bodyMaxLength: 150, titleMaxLength: 16, backdrop: -1, icon: null, iconClass: null, html: null, position: SnotifyPosition.rightBottom, animation: { enter: 'fadeIn', exit: 'fadeOut', time: 400 } }, type: { [SnotifyStyle.prompt]: { timeout: 0, closeOnClick: false, buttons: [ { text: 'Ok', action: null, bold: true }, { text: 'Cancel', action: null, bold: false } ], placeholder: 'Enter answer here...', type: SnotifyStyle.prompt }, [SnotifyStyle.confirm]: { timeout: 0, closeOnClick: false, buttons: [ { text: 'Ok', action: null, bold: true }, { text: 'Cancel', action: null, bold: false } ], type: SnotifyStyle.confirm }, [SnotifyStyle.simple]: { type: SnotifyStyle.simple }, [SnotifyStyle.success]: { type: SnotifyStyle.success }, [SnotifyStyle.error]: { type: SnotifyStyle.error }, [SnotifyStyle.warning]: { type: SnotifyStyle.warning }, [SnotifyStyle.info]: { type: SnotifyStyle.info }, [SnotifyStyle.async]: { pauseOnHover: false, closeOnClick: false, timeout: 0, showProgressBar: false, type: SnotifyStyle.async } } }; /* * Public API Surface of ng-snotify */ /** * Generated bundle index. Do not edit. */ export { ButtonsComponent, KeysPipe, PromptComponent, SnotifyComponent, SnotifyModule, SnotifyPosition, SnotifyService, SnotifyStyle, SnotifyToast, ToastComponent, ToastDefaults, TruncatePipe }; //# sourceMappingURL=hyahfoufi-ng-snotify.mjs.map