UNPKG

flexi-toast

Version:

Lightweight, customizable Angular toast notification component with title, message, icon types, auto-dismiss, manual close, animations, theme and positioning support.

336 lines (321 loc) 19.2 kB
import * as i0 from '@angular/core'; import { input, linkedSignal, output, HostBinding, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, ApplicationRef, EnvironmentInjector, createComponent, Injectable } from '@angular/core'; class FlexiSwalComponent { title = input('', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ [])); question = input('', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "question" }] : /* istanbul ignore next */ [])); confirmBtnText = input('Confirm', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "confirmBtnText" }] : /* istanbul ignore next */ [])); cancelBtnText = input('Cancel', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "cancelBtnText" }] : /* istanbul ignore next */ [])); showCloseBtn = input(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "showCloseBtn" }] : /* istanbul ignore next */ [])); themeClass = input('light', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "themeClass" }] : /* istanbul ignore next */ [])); contentThemeClass = input('default', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "contentThemeClass" }] : /* istanbul ignore next */ [])); isVisible = input(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ [])); isVisibleSignal = linkedSignal(() => this.isVisible(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "isVisibleSignal" }] : /* istanbul ignore next */ [])); confirmed = output(); cancelled = output(); closed = output(); get visible() { return this.isVisibleSignal(); } get display() { return this.isVisibleSignal() ? 'block' : 'none'; } onConfirm() { this.confirmed.emit(); this.close(); } onCancel() { this.cancelled.emit(); this.close(); } close() { this.isVisibleSignal.set(false); this.closed.emit(); } show() { this.isVisibleSignal.set(true); // Prevent body scroll document.body.style.overflow = 'hidden'; } hide() { this.isVisibleSignal.set(false); // Restore body scroll document.body.style.overflow = ''; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FlexiSwalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: FlexiSwalComponent, isStandalone: true, selector: "flexi-swal", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, question: { classPropertyName: "question", publicName: "question", isSignal: true, isRequired: false, transformFunction: null }, confirmBtnText: { classPropertyName: "confirmBtnText", publicName: "confirmBtnText", isSignal: true, isRequired: false, transformFunction: null }, cancelBtnText: { classPropertyName: "cancelBtnText", publicName: "cancelBtnText", isSignal: true, isRequired: false, transformFunction: null }, showCloseBtn: { classPropertyName: "showCloseBtn", publicName: "showCloseBtn", isSignal: true, isRequired: false, transformFunction: null }, themeClass: { classPropertyName: "themeClass", publicName: "themeClass", isSignal: true, isRequired: false, transformFunction: null }, contentThemeClass: { classPropertyName: "contentThemeClass", publicName: "contentThemeClass", isSignal: true, isRequired: false, transformFunction: null }, isVisible: { classPropertyName: "isVisible", publicName: "isVisible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmed: "confirmed", cancelled: "cancelled", closed: "closed" }, host: { properties: { "class.visible": "this.visible", "style.display": "this.display" } }, ngImport: i0, template: ` @if(isVisibleSignal()){ <div class="flexi-swal-container" [attr.data-bs-theme]="themeClass()"> <div class="flexi-swal-content" [attr.data-bs-theme]="themeClass()"> <!-- Close button (top right) --> @if (showCloseBtn()) { <span class="flexi-swal-close-btn" (click)="onCancel()">×</span> } <!-- Title container --> <div class="flexi-swal-title-container"> <span>{{ title() }}</span> <button class="flexi-swal-close-button" (click)="onCancel()">×</button> </div> <!-- Question container --> <div class="flexi-swal-question-container"> <span [innerHtml]="question()"></span> </div> <!-- Button container --> <div class="flexi-swal-button-container"> <button class="flexi-swal-button flexi-swal-button-primary" (click)="onConfirm()"> {{ confirmBtnText() }} </button> <button class="flexi-swal-button" (click)="onCancel()"> {{ cancelBtnText() }} </button> </div> </div> </div> } `, isInline: true, styles: [":host{position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;pointer-events:none}:host(.visible){pointer-events:all}.flexi-swal-container{display:flex;position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;justify-content:center;align-items:center;z-index:9999}.flexi-swal-content{animation:fadeIn .3s ease-in-out}.fade-out{animation:fadeOut .2s ease-in-out forwards}@keyframes fadeIn{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}@keyframes fadeOut{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.9)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FlexiSwalComponent, decorators: [{ type: Component, args: [{ selector: 'flexi-swal', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: ` @if(isVisibleSignal()){ <div class="flexi-swal-container" [attr.data-bs-theme]="themeClass()"> <div class="flexi-swal-content" [attr.data-bs-theme]="themeClass()"> <!-- Close button (top right) --> @if (showCloseBtn()) { <span class="flexi-swal-close-btn" (click)="onCancel()">×</span> } <!-- Title container --> <div class="flexi-swal-title-container"> <span>{{ title() }}</span> <button class="flexi-swal-close-button" (click)="onCancel()">×</button> </div> <!-- Question container --> <div class="flexi-swal-question-container"> <span [innerHtml]="question()"></span> </div> <!-- Button container --> <div class="flexi-swal-button-container"> <button class="flexi-swal-button flexi-swal-button-primary" (click)="onConfirm()"> {{ confirmBtnText() }} </button> <button class="flexi-swal-button" (click)="onCancel()"> {{ cancelBtnText() }} </button> </div> </div> </div> } `, styles: [":host{position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;pointer-events:none}:host(.visible){pointer-events:all}.flexi-swal-container{display:flex;position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;justify-content:center;align-items:center;z-index:9999}.flexi-swal-content{animation:fadeIn .3s ease-in-out}.fade-out{animation:fadeOut .2s ease-in-out forwards}@keyframes fadeIn{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}@keyframes fadeOut{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.9)}}\n"] }] }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], question: [{ type: i0.Input, args: [{ isSignal: true, alias: "question", required: false }] }], confirmBtnText: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmBtnText", required: false }] }], cancelBtnText: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelBtnText", required: false }] }], showCloseBtn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCloseBtn", required: false }] }], themeClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeClass", required: false }] }], contentThemeClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentThemeClass", required: false }] }], isVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVisible", required: false }] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], closed: [{ type: i0.Output, args: ["closed"] }], visible: [{ type: HostBinding, args: ['class.visible'] }], display: [{ type: HostBinding, args: ['style.display'] }] } }); class FlexiToastService { options = new FlexiToastOptionsModel(); renderer; appRef = inject(ApplicationRef); injector = inject(EnvironmentInjector); constructor(rendererFactory) { this.renderer = rendererFactory.createRenderer(null, null); } showToast(title, text, icon = "success", options) { if (!options) { options = this.options; } options.icon = icon; let wrapper = document.querySelector(".flexi-toast-wrapper"); if (!wrapper) { wrapper = this.renderer.createElement('div'); this.renderer.addClass(wrapper, `flexi-toast-wrapper`); this.renderer.addClass(wrapper, `flexi-taost-${options.position}`); const body = this.renderer.selectRootElement('body', true); this.renderer.appendChild(body, wrapper); } else { wrapper.className = `flexi-toast-wrapper flexi-taost-${options.position}`; if (options.preventDuplicate) { const allEl = document.querySelectorAll(".flexi-toast"); if (allEl) { for (const el of allEl) { this.renderer.removeChild(wrapper, el); } } } } const toast = this.renderer.createElement('div'); this.renderer.addClass(toast, 'flexi-toast'); this.renderer.addClass(toast, `flexi-toast-${options.icon}`); this.renderer.setAttribute(toast, `data-bs-theme`, options.themeClass); if (!options.showToastCloseBtn) { this.renderer.listen(toast, 'click', () => { this.renderer.removeClass(toast, 'flexi-toast-fade-in'); this.renderer.addClass(toast, 'flexi-toast-fade-out'); setTimeout(() => { this.renderer.removeChild(wrapper, toast); }, 500); }); } const container = this.renderer.createElement('div'); this.renderer.addClass(container, 'flexi-toast-container'); const container1 = this.renderer.createElement('div'); this.renderer.addClass(container1, 'flexi-toast-container-1'); // SVG ikonu eklemek const svgIcon = this.renderer.createElement('svg', 'http://www.w3.org/2000/svg'); this.renderer.setAttribute(svgIcon, 'xmlns', 'http://www.w3.org/2000/svg'); this.renderer.setAttribute(svgIcon, 'viewBox', '0 0 512 512'); const path = this.renderer.createElement('path', 'http://www.w3.org/2000/svg'); if (options.icon === "success") { this.renderer.setAttribute(path, 'd', 'M504 256c0 137-111 248-248 248S8 393 8 256 119 8 256 8s248 111 248 248zM227.3 387.3l184-184c6.2-6.2 6.2-16.4 0-22.6l-22.6-22.6c-6.2-6.2-16.4-6.2-22.6 0L216 308.1l-70.1-70.1c-6.2-6.2-16.4-6.2-22.6 0l-22.6 22.6c-6.2 6.2-6.2 16.4 0 22.6l104 104c6.2 6.2 16.4 6.2 22.6 0z'); } else if (options.icon === "warning") { this.renderer.setAttribute(path, 'd', 'M504 256c0 137-111 248-248 248S8 393 8 256C8 119.1 119 8 256 8s248 111.1 248 248zm-248 50c-25.4 0-46 20.6-46 46s20.6 46 46 46 46-20.6 46-46-20.6-46-46-46zm-43.7-165.3l7.4 136c.3 6.4 5.6 11.3 12 11.3h48.5c6.4 0 11.6-5 12-11.3l7.4-136c.4-6.9-5.1-12.7-12-12.7h-63.4c-6.9 0-12.4 5.8-12 12.7z'); } else if (options.icon === "info") { this.renderer.setAttribute(path, 'd', 'M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z'); } else if (options.icon === "error") { this.renderer.setAttribute(path, 'd', 'M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z'); } this.renderer.appendChild(svgIcon, path); this.renderer.appendChild(container1, svgIcon); const container2 = this.renderer.createElement('div'); this.renderer.addClass(container2, 'flexi-toast-container-2'); const titleEl = this.renderer.createElement('span'); const titleText = this.renderer.createText(title); this.renderer.appendChild(titleEl, titleText); const message = this.renderer.createElement('span'); message.innerHTML = text; this.renderer.appendChild(container2, titleEl); this.renderer.appendChild(container2, message); this.renderer.appendChild(container, container1); this.renderer.appendChild(container, container2); if (options.autoClose && options.showProgressBar) { const progressBar = this.renderer.createElement('div'); this.renderer.addClass(progressBar, 'flexi-toast-progress-bar'); this.renderer.addClass(progressBar, `flex-toast-progress-bar-${options.icon}`); this.renderer.appendChild(toast, progressBar); // Add animation to progress bar this.renderer.setStyle(progressBar, 'transition', `width ${options.timeOut}ms linear`); this.renderer.setStyle(progressBar, 'width', '100%'); setTimeout(() => { this.renderer.setStyle(progressBar, 'width', '0%'); }, 10); } this.renderer.appendChild(toast, container); if (options.showToastCloseBtn) { const closeBtn = this.renderer.createElement('span'); this.renderer.addClass(closeBtn, 'flexi-toast-close-btn'); const closeText = this.renderer.createText('×'); this.renderer.appendChild(closeBtn, closeText); this.renderer.listen(closeBtn, 'click', () => { this.renderer.removeClass(toast, 'flexi-toast-fade-in'); this.renderer.addClass(toast, 'flexi-toast-fade-out'); setTimeout(() => { this.renderer.removeChild(wrapper, toast); }, 500); }); this.renderer.appendChild(toast, closeBtn); } this.renderer.appendChild(wrapper, toast); // Timeout to trigger the fade-in animation setTimeout(() => { this.renderer.addClass(toast, 'flexi-toast-fade-in'); }, 10); if (options.autoClose) { setTimeout(() => { this.renderer.removeClass(toast, 'flexi-toast-fade-in'); this.renderer.addClass(toast, 'flexi-toast-fade-out'); setTimeout(() => { this.renderer.removeChild(wrapper, toast); }, 500); }, options.timeOut); } } showSwal(title, question, confirmBtnText = this.options.confirmBtnText ?? "Delete", callBack, cancelBtnText = this.options.cancelBtnText ?? "Cancel", cancelCallBack) { // Create component const componentRef = createComponent(FlexiSwalComponent, { environmentInjector: this.injector }); // Set inputs componentRef.setInput('title', title); componentRef.setInput('question', question); componentRef.setInput('confirmBtnText', confirmBtnText); componentRef.setInput('cancelBtnText', cancelBtnText); componentRef.setInput('showCloseBtn', this.options.showSwalCloseBtn); componentRef.setInput('themeClass', this.options.themeClass); componentRef.setInput('contentThemeClass', this.options.swalContentThemeClass); componentRef.setInput('isVisible', true); // Subscribe to outputs componentRef.instance.confirmed.subscribe(() => { callBack(); this.destroySwal(componentRef); }); componentRef.instance.cancelled.subscribe(() => { if (cancelCallBack) { cancelCallBack(); } this.destroySwal(componentRef); }); componentRef.instance.closed.subscribe(() => { this.destroySwal(componentRef); }); // Append to body document.body.appendChild(componentRef.location.nativeElement); // Attach to Angular's change detection this.appRef.attachView(componentRef.hostView); componentRef.changeDetectorRef.detectChanges(); return componentRef; } destroySwal(componentRef) { setTimeout(() => { this.appRef.detachView(componentRef.hostView); componentRef.destroy(); }, 200); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FlexiToastService, deps: [{ token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FlexiToastService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FlexiToastService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i0.RendererFactory2 }] }); class FlexiToastOptionsModel { icon = "success"; autoClose = true; timeOut = 3000; showProgressBar = true; showToastCloseBtn = true; showSwalCloseBtn = false; preventDuplicate = false; position = "bottom-right"; themeClass = "light"; cancelBtnText = "Vazgeç"; confirmBtnText = "Sil"; swalContentThemeClass = "default"; } /** * Generated bundle index. Do not edit. */ export { FlexiToastOptionsModel, FlexiToastService }; //# sourceMappingURL=flexi-toast.mjs.map