UNPKG

ngx-modal-dialog

Version:
417 lines (407 loc) 14.4 kB
import { Component, ElementRef, ComponentFactoryResolver, ViewChild, ViewContainerRef, HostListener, Injectable, Inject, InjectionToken, Optional, SkipSelf, NgModule } from '@angular/core'; import { Subject, from } from 'rxjs'; import { CommonModule } from '@angular/common'; /** * Modal dialog component */ class ModalDialogComponent { /** * CTOR * @param _element * @param componentFactoryResolver */ constructor(_element, componentFactoryResolver) { this._element = _element; this.componentFactoryResolver = componentFactoryResolver; /** Modal dialog style settings */ this.settings = { overlayClass: 'modal-backdrop fade', overlayAnimationTriggerClass: 'show', modalClass: 'modal ngx-modal fade', modalAnimationTriggerClass: 'show', modalDialogClass: 'modal-dialog modal-dialog-centered', contentClass: 'modal-content', headerClass: 'modal-header', headerTitleClass: 'modal-title', closeButtonClass: 'close glyphicon glyphicon-remove', closeButtonTitle: 'CLOSE', bodyClass: 'modal-body', footerClass: 'modal-footer', alertClass: 'ngx-modal-shake', alertDuration: 250, notifyWithAlert: true, buttonClass: 'btn btn-primary' }; this.showAlert = false; this.animateOverlayClass = ''; this.animateModalClass = ''; this.showOverlay = false; this._inProgress = false; } onClick(event) { if (event.target !== this.dialogElement.nativeElement) { return; } this.close(); } /** * Initialize dialog with reference to instance and options * @param reference * @param options */ dialogInit(reference, options = {}) { this.reference = reference; // inject component if (options.childComponent) { let factory = this.componentFactoryResolver.resolveComponentFactory(options.childComponent); let componentRef = this.dynamicComponentTarget.createComponent(factory); this._childInstance = componentRef.instance; this._closeDialog$ = new Subject(); this._closeDialog$.subscribe(() => { this._finalizeAndDestroy(); }); options.closeDialogSubject = this._closeDialog$; this._childInstance['dialogInit'](componentRef, options); document.activeElement != null ? document.activeElement.blur() : document.body.blur(); } // set options this._setOptions(options); } ngOnInit() { // a trick to defer css animations setTimeout(() => { this.animateOverlayClass = this.settings.overlayAnimationTriggerClass; this.animateModalClass = this.settings.modalAnimationTriggerClass; }, 0); } /** * Cleanup on destroy */ ngOnDestroy() { // run animations this.animateOverlayClass = ''; this.animateModalClass = ''; // cleanup listeners if (this._alertTimeout) { clearTimeout(this._alertTimeout); this._alertTimeout = null; } if (this._closeDialog$) { this._closeDialog$.unsubscribe(); } } /** * Run action defined on action button * @param action */ doAction(action) { // disable multi clicks if (this._inProgress) { return; } this._inProgress = true; this._closeIfSuccessful(action); } /** * Method to run on close * if action buttons are defined, it will not close */ close() { if (this._inProgress) { return; } if (this.actionButtons && this.actionButtons.length) { return; } this._inProgress = true; if (this.onClose) { this._closeIfSuccessful(this.onClose); return; } this._finalizeAndDestroy(); } /** * Pass options from dialog setup to component * @param {IModalDialogOptions} options? */ _setOptions(options) { if (options.onClose && options.actionButtons && options.actionButtons.length) { throw new Error(`OnClose callback and ActionButtons are not allowed to be defined on the same dialog.`); } // set references this.title = (options && options.title) || ''; this.onClose = (options && options.onClose) || null; this.actionButtons = (this._childInstance && this._childInstance['actionButtons']) || (options && options.actionButtons) || null; if (options && options.settings) { Object.assign(this.settings, options.settings); } } /** * Close if successful * @param callback */ _closeIfSuccessful(callback) { if (!callback) { return this._finalizeAndDestroy(); } let response = callback(); if (typeof response === 'boolean') { if (response) { return this._finalizeAndDestroy(); } return this._triggerAlert(); } if (this.isPromise(response)) { response = from(response); } if (this.isObservable(response)) { response.subscribe(() => { this._finalizeAndDestroy(); }, () => { this._triggerAlert(); }); } else { this._inProgress = false; } } _finalizeAndDestroy() { this._inProgress = false; this.reference.destroy(); } _triggerAlert() { if (this.settings.notifyWithAlert) { this.showAlert = true; this._alertTimeout = window.setTimeout(() => { this.showAlert = false; this._inProgress = false; clearTimeout(this._alertTimeout); this._alertTimeout = null; }, this.settings.alertDuration); } } isPromise(value) { return value && typeof value.subscribe !== 'function' && typeof value.then === 'function'; } isObservable(value) { return value && typeof value.subscribe === 'function'; } } ModalDialogComponent.decorators = [ { type: Component, args: [{ selector: 'modal-dialog', template: ` <div *ngIf="settings.overlayClass && showOverlay" [ngClass]="[settings.overlayClass, animateOverlayClass]"></div> <div [ngClass]="[settings.modalClass, animateModalClass]" #dialog> <div [ngClass]="settings.modalDialogClass"> <div [ngClass]="[ showAlert ? settings.alertClass : '', settings.contentClass]"> <div [ngClass]="settings.headerClass"> <h4 [ngClass]="settings.headerTitleClass">{{title}}</h4> <button (click)="close()" *ngIf="!actionButtons || !actionButtons.length" type="button" [title]="settings.closeButtonTitle" [ngClass]="settings.closeButtonClass"> </button> </div> <div [ngClass]="settings.bodyClass"> <i #modalDialogBody></i> </div> <div [ngClass]="settings.footerClass" *ngIf="actionButtons && actionButtons.length"> <button *ngFor="let button of actionButtons" (click)="doAction(button.onAction)" [ngClass]="button.buttonClass || settings.buttonClass">{{button.text}} </button> </div> </div> </div> </div> `, styles: [` @-moz-keyframes shake { from, to { transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { transform: translate3d(-2rem, 0, 0); } 20%, 40%, 60%, 80% { transform: translate3d(2rem, 0, 0); } } @-webkit-keyframes shake { from, to { transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { transform: translate3d(-2rem, 0, 0); } 20%, 40%, 60%, 80% { transform: translate3d(2rem, 0, 0); } } @keyframes shake { from, to { transform: translate3d(0, 0, 0); } 10%, 30%, 50%, 70%, 90% { transform: translate3d(-2rem, 0, 0); } 20%, 40%, 60%, 80% { transform: translate3d(2rem, 0, 0); } } .ngx-modal { display: flex; } .ngx-modal-shake { backface-visibility: hidden; -webkit-animation-duration: 0.5s; -moz-animation-duration: 0.5s; animation-duration: 0.5s; -webkit-animation-fill-mode: both; -moz-animation-fill-mode: both; animation-fill-mode: both; -webkit-animation-iteration-count: infinite; -moz-animation-iteration-count: infinite; animation-iteration-count: infinite; -webkit-animation-name: shake; -moz-animation-name: shake; animation-name: shake; } `] },] } ]; ModalDialogComponent.ctorParameters = () => [ { type: ElementRef }, { type: ComponentFactoryResolver } ]; ModalDialogComponent.propDecorators = { dynamicComponentTarget: [{ type: ViewChild, args: ['modalDialogBody', { read: ViewContainerRef, static: true },] }], dialogElement: [{ type: ViewChild, args: ['dialog',] }], onClick: [{ type: HostListener, args: ['click', ['$event'],] }] }; class ModalDialogInstanceService { constructor() { /** * Used to make sure there is exactly one instance of Modal Dialog */ this.componentRefs = []; } /** * Closes existing modal dialog */ closeAnyExistingModalDialog() { while (this.componentRefs.length) { this.componentRefs[this.componentRefs.length - 1].destroy(); } } /** * Save component ref for future comparison * @param componentRef */ saveExistingModalDialog(componentRef) { // remove overlay from top element this.setOverlayForTopDialog(false); // add new component this.componentRefs = [...this.componentRefs, componentRef]; componentRef.instance.showOverlay = true; componentRef.onDestroy(() => { this.componentRefs.pop(); this.setOverlayForTopDialog(true); }); } setOverlayForTopDialog(value) { if (this.componentRefs.length) { this.componentRefs[this.componentRefs.length - 1].instance.showOverlay = value; } } } ModalDialogInstanceService.decorators = [ { type: Injectable } ]; class ModalDialogService { /** * CTOR * @param componentFactoryResolver * @param modalDialogInstanceService */ constructor(componentFactoryResolver, modalDialogInstanceService) { this.componentFactoryResolver = componentFactoryResolver; this.modalDialogInstanceService = modalDialogInstanceService; } /** * Open dialog in given target element with given options * @param {ViewContainerRef} target * @param {IModalDialogOptions} options? */ openDialog(target, options = {}) { if (!options.placeOnTop) { this.modalDialogInstanceService.closeAnyExistingModalDialog(); } const factory = this.componentFactoryResolver.resolveComponentFactory(ModalDialogComponent); const componentRef = target.createComponent(factory); componentRef.instance.dialogInit(componentRef, options); this.modalDialogInstanceService.saveExistingModalDialog(componentRef); } } ModalDialogService.decorators = [ { type: Injectable } ]; ModalDialogService.ctorParameters = () => [ { type: ComponentFactoryResolver, decorators: [{ type: Inject, args: [ComponentFactoryResolver,] }] }, { type: ModalDialogInstanceService, decorators: [{ type: Inject, args: [ModalDialogInstanceService,] }] } ]; class SimpleModalComponent { /** * Initialize dialog with simple HTML content * @param {ComponentRef<IModalDialog>} reference * @param {Partial<IModalDialogOptions>} options */ dialogInit(reference, options) { if (!options.data) { throw new Error(`Data information for simple modal dialog is missing`); } this.text = options.data.text; } } SimpleModalComponent.decorators = [ { type: Component, args: [{ selector: 'simple-modal-dialog', template: ``, host: { '[innerHTML]': 'text' }, styles: [':host { display: block; }'] },] } ]; // components and directives /** * Guard to make sure we have single initialization of forRoot * @type {InjectionToken<ModalDialogModule>} */ const MODAL_DIALOG_FORROOT_GUARD = new InjectionToken('MODAL_DIALOG_FORROOT_GUARD'); class ModalDialogModule { static forRoot() { return { ngModule: ModalDialogModule, providers: [ { provide: MODAL_DIALOG_FORROOT_GUARD, useFactory: provideForRootGuard, deps: [ModalDialogModule, new Optional(), new SkipSelf()] }, ModalDialogInstanceService ] }; } } ModalDialogModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], declarations: [ModalDialogComponent, SimpleModalComponent], entryComponents: [ModalDialogComponent, SimpleModalComponent], exports: [ModalDialogComponent, SimpleModalComponent], providers: [ModalDialogService, ModalDialogInstanceService] },] } ]; /** * @param dialogModule * @returns {string} */ function provideForRootGuard(dialogModule) { if (dialogModule) { throw new Error(`ModalDialogModule.forRoot() called twice.`); } return 'guarded'; } /* * Public API Surface of ngx-modal-dialog */ /** * Generated bundle index. Do not edit. */ export { MODAL_DIALOG_FORROOT_GUARD, ModalDialogComponent, ModalDialogInstanceService, ModalDialogModule, ModalDialogService, SimpleModalComponent, provideForRootGuard }; //# sourceMappingURL=ngx-modal-dialog.js.map