UNPKG

ngx-smart-modal

Version:

Smart modal handler to manage modals and data everywhere in Angular apps.

1,060 lines (1,051 loc) 41.8 kB
import * as i0 from '@angular/core'; import { EventEmitter, PLATFORM_ID, ViewContainerRef, Component, Inject, Input, Output, ViewChildren, ViewChild, HostListener, Injectable, Type, TemplateRef, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common'; const NgxSmartModalConfig = { bodyClassOpen: 'dialog-open', prefixEvent: 'ngx-smart-modal.' }; class NgxSmartModalComponent { constructor(_renderer, _changeDetectorRef, _viewContainerRef, elementRef, _document, _platformId) { this._renderer = _renderer; this._changeDetectorRef = _changeDetectorRef; this._viewContainerRef = _viewContainerRef; this.elementRef = elementRef; this._document = _document; this._platformId = _platformId; this.closable = true; this.escapable = true; this.dismissable = true; this.identifier = ''; this.customClass = 'nsm-dialog-animation-fade'; this.visible = false; this.backdrop = true; this.force = true; this.hideDelay = 500; this.autostart = false; this.target = ''; this.ariaLabel = null; this.ariaLabelledBy = null; this.ariaDescribedBy = null; this.refocus = true; this.visibleChange = new EventEmitter(); this.onClose = new EventEmitter(); this.onCloseFinished = new EventEmitter(); this.onDismiss = new EventEmitter(); this.onDismissFinished = new EventEmitter(); this.onAnyCloseEvent = new EventEmitter(); this.onAnyCloseEventFinished = new EventEmitter(); this.onOpen = new EventEmitter(); this.onOpenFinished = new EventEmitter(); this.onEscape = new EventEmitter(); this.onDataAdded = new EventEmitter(); this.onDataRemoved = new EventEmitter(); this.layerPosition = 1041; this.overlayVisible = false; this.openedClass = false; this.createFrom = 'html'; } ngOnInit() { if (!this.identifier || !this.identifier.length) { throw new Error('identifier field isn’t set. Please set one before calling <ngx-smart-modal> in a template.'); } this._sendEvent('create'); } ngAfterViewChecked() { if (this.overlayVisible && this.contentComponent && this.dynamicContentContainer && this.dynamicContentContainer.length === 0) { this.createDynamicContent(); } } ngOnDestroy() { this._sendEvent('delete'); } /** * Open the modal instance * * @param top open the modal top of all other * @returns the modal component */ open(top) { this._sendEvent('open', { top: top }); return this; } /** * Close the modal instance * * @returns the modal component */ close() { this._sendEvent('close'); return this; } /** * Dismiss the modal instance * * @param e the event sent by the browser * @returns the modal component */ dismiss(e) { if (!this.dismissable || !e?.target?.classList.contains('overlay')) { return this; } this._sendEvent('dismiss'); return this; } /** * Toggle visibility of the modal instance * * @param top open the modal top of all other * @returns the modal component */ toggle(top) { this._sendEvent('toggle', { top: top }); return this; } /** * Add a custom class to the modal instance * * @param className the class to add * @returns the modal component */ addCustomClass(className) { if (!this.customClass.length) { this.customClass = className; } else { this.customClass += ' ' + className; } return this; } /** * Remove a custom class to the modal instance * * @param className the class to remove * @returns the modal component */ removeCustomClass(className) { if (className) { this.customClass = this.customClass.replace(className, '').trim(); } else { this.customClass = ''; } return this; } /** * Returns the visibility state of the modal instance */ isVisible() { return this.visible; } /** * Checks if data is attached to the modal instance */ hasData() { return this._data !== undefined; } /** * Attach data to the modal instance * * @param data the data to attach * @param force override potentially attached data * @returns the modal component */ setData(data, force) { if (!this.hasData() || (this.hasData() && force)) { this._data = data; this.assignModalDataToComponentData(this._componentRef); this.onDataAdded.emit(this._data); this.markForCheck(); } return this; } /** * Retrieve the data attached to the modal instance */ getData() { this.assignComponentDataToModalData(this._componentRef); return this._data; } /** * Remove the data attached to the modal instance * * @returns the modal component */ removeData() { this._data = undefined; this.onDataRemoved.emit(true); this.markForCheck(); return this; } /** * Add body class modal opened * * @returns the modal component */ addBodyClass() { this._renderer.addClass(this._document.body, NgxSmartModalConfig.bodyClassOpen); return this; } /** * Add body class modal opened * * @returns the modal component */ removeBodyClass() { this._renderer.removeClass(this._document.body, NgxSmartModalConfig.bodyClassOpen); return this; } markForCheck() { try { this._changeDetectorRef.detectChanges(); } catch (e) { /* empty */ } this._changeDetectorRef.markForCheck(); } /** * Listens for window resize event and recalculates modal instance position if it is element-relative */ targetPlacement() { if (!this.isBrowser || !this.nsmDialog.length || !this.nsmContent.length || !this.nsmOverlay.length || !this.target) { return false; } const targetElement = this._document.querySelector(this.target); if (!targetElement) { return false; } const targetElementRect = targetElement.getBoundingClientRect(); const bodyRect = this.nsmOverlay.first.nativeElement.getBoundingClientRect(); const nsmContentRect = this.nsmContent.first.nativeElement.getBoundingClientRect(); const nsmDialogRect = this.nsmDialog.first.nativeElement.getBoundingClientRect(); const marginLeft = parseInt(getComputedStyle(this.nsmContent.first.nativeElement).marginLeft, 10); const marginTop = parseInt(getComputedStyle(this.nsmContent.first.nativeElement).marginTop, 10); let offsetTop = targetElementRect.top - nsmDialogRect.top - ((nsmContentRect.height - targetElementRect.height) / 2); let offsetLeft = targetElementRect.left - nsmDialogRect.left - ((nsmContentRect.width - targetElementRect.width) / 2); if (offsetLeft + nsmDialogRect.left + nsmContentRect.width + (marginLeft * 2) > bodyRect.width) { offsetLeft = bodyRect.width - (nsmDialogRect.left + nsmContentRect.width) - (marginLeft * 2); } else if (offsetLeft + nsmDialogRect.left < 0) { offsetLeft = -nsmDialogRect.left; } if (offsetTop + nsmDialogRect.top + nsmContentRect.height + marginTop > bodyRect.height) { offsetTop = bodyRect.height - (nsmDialogRect.top + nsmContentRect.height) - marginTop; } this._renderer.setStyle(this.nsmContent.first.nativeElement, 'top', (offsetTop < 0 ? 0 : offsetTop) + 'px'); this._renderer.setStyle(this.nsmContent.first.nativeElement, 'left', offsetLeft + 'px'); } _sendEvent(name, extraData) { if (!this.isBrowser) { return false; } const data = { extraData: extraData, instance: { id: this.identifier, modal: this } }; const event = new CustomEvent(NgxSmartModalConfig.prefixEvent + name, { detail: data }); return window.dispatchEvent(event); } /** * Is current platform browser */ get isBrowser() { return isPlatformBrowser(this._platformId); } /** * Creates content inside provided ViewContainerRef */ createDynamicContent() { this.dynamicContentContainer.clear(); this._componentRef = this.dynamicContentContainer.createComponent(this.contentComponent); this.assignModalDataToComponentData(this._componentRef); this.markForCheck(); } /** * Assigns the modal data to the ComponentRef instance properties */ assignModalDataToComponentData(componentRef) { if (componentRef) { Object.assign(componentRef.instance, this._data); } } /** * Assigns the ComponentRef instance properties to the modal data object */ assignComponentDataToModalData(componentRef) { if (componentRef) { Object.assign(this._data, componentRef.instance); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: i0.ViewContainerRef }, { token: i0.ElementRef }, { token: DOCUMENT }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.10", type: NgxSmartModalComponent, selector: "ngx-smart-modal", inputs: { closable: "closable", escapable: "escapable", dismissable: "dismissable", identifier: "identifier", customClass: "customClass", visible: "visible", backdrop: "backdrop", force: "force", hideDelay: "hideDelay", autostart: "autostart", target: "target", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaDescribedBy: "ariaDescribedBy", refocus: "refocus" }, outputs: { visibleChange: "visibleChange", onClose: "onClose", onCloseFinished: "onCloseFinished", onDismiss: "onDismiss", onDismissFinished: "onDismissFinished", onAnyCloseEvent: "onAnyCloseEvent", onAnyCloseEventFinished: "onAnyCloseEventFinished", onOpen: "onOpen", onOpenFinished: "onOpenFinished", onEscape: "onEscape", onDataAdded: "onDataAdded", onDataRemoved: "onDataRemoved" }, host: { listeners: { "window:resize": "targetPlacement()" } }, viewQueries: [{ propertyName: "dynamicContentContainer", first: true, predicate: ["dynamicContent"], descendants: true, read: ViewContainerRef }, { propertyName: "nsmContent", predicate: ["nsmContent"], descendants: true }, { propertyName: "nsmDialog", predicate: ["nsmDialog"], descendants: true }, { propertyName: "nsmOverlay", predicate: ["nsmOverlay"], descendants: true }], ngImport: i0, template: ` <div *ngIf="overlayVisible" [style.z-index]="visible ? layerPosition-1 : -1" [ngClass]="{'transparent':!backdrop, 'overlay':true, 'nsm-overlay-open':openedClass}" (click)="dismiss($event)" #nsmOverlay> <div [style.z-index]="visible ? layerPosition : -1" [ngClass]="['nsm-dialog', customClass, openedClass ? 'nsm-dialog-open': 'nsm-dialog-close']" #nsmDialog [attr.aria-hidden]="openedClass ? false : true" [attr.aria-label]="ariaLabel" [attr.aria-labelledby]="ariaLabelledBy" [attr.aria-describedby]="ariaDescribedBy"> <div class="nsm-content" #nsmContent> <div class="nsm-body"> <ng-template #dynamicContent></ng-template> <ng-content></ng-content> </div> <button type="button" *ngIf="closable" (click)="close()" aria-label="Close" class="nsm-dialog-btn-close"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" xml:space="preserve" width="16px" height="16px" role="img" aria-labelledby="closeIconTitle closeIconDesc"> <title id="closeIconTitle">Close Icon</title> <desc id="closeIconDesc">A light-gray close icon used to close the modal</desc> <g> <path d="M505.943,6.058c-8.077-8.077-21.172-8.077-29.249,0L6.058,476.693c-8.077,8.077-8.077,21.172,0,29.249 C10.096,509.982,15.39,512,20.683,512c5.293,0,10.586-2.019,14.625-6.059L505.943,35.306 C514.019,27.23,514.019,14.135,505.943,6.058z" fill="currentColor"/> </g> <g> <path d="M505.942,476.694L35.306,6.059c-8.076-8.077-21.172-8.077-29.248,0c-8.077,8.076-8.077,21.171,0,29.248l470.636,470.636 c4.038,4.039,9.332,6.058,14.625,6.058c5.293,0,10.587-2.019,14.624-6.057C514.018,497.866,514.018,484.771,505.942,476.694z" fill="currentColor"/> </g> </svg> </button> </div> </div> </div> `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-smart-modal', template: ` <div *ngIf="overlayVisible" [style.z-index]="visible ? layerPosition-1 : -1" [ngClass]="{'transparent':!backdrop, 'overlay':true, 'nsm-overlay-open':openedClass}" (click)="dismiss($event)" #nsmOverlay> <div [style.z-index]="visible ? layerPosition : -1" [ngClass]="['nsm-dialog', customClass, openedClass ? 'nsm-dialog-open': 'nsm-dialog-close']" #nsmDialog [attr.aria-hidden]="openedClass ? false : true" [attr.aria-label]="ariaLabel" [attr.aria-labelledby]="ariaLabelledBy" [attr.aria-describedby]="ariaDescribedBy"> <div class="nsm-content" #nsmContent> <div class="nsm-body"> <ng-template #dynamicContent></ng-template> <ng-content></ng-content> </div> <button type="button" *ngIf="closable" (click)="close()" aria-label="Close" class="nsm-dialog-btn-close"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" xml:space="preserve" width="16px" height="16px" role="img" aria-labelledby="closeIconTitle closeIconDesc"> <title id="closeIconTitle">Close Icon</title> <desc id="closeIconDesc">A light-gray close icon used to close the modal</desc> <g> <path d="M505.943,6.058c-8.077-8.077-21.172-8.077-29.249,0L6.058,476.693c-8.077,8.077-8.077,21.172,0,29.249 C10.096,509.982,15.39,512,20.683,512c5.293,0,10.586-2.019,14.625-6.059L505.943,35.306 C514.019,27.23,514.019,14.135,505.943,6.058z" fill="currentColor"/> </g> <g> <path d="M505.942,476.694L35.306,6.059c-8.076-8.077-21.172-8.077-29.248,0c-8.077,8.076-8.077,21.171,0,29.248l470.636,470.636 c4.038,4.039,9.332,6.058,14.625,6.058c5.293,0,10.587-2.019,14.624-6.057C514.018,497.866,514.018,484.771,505.942,476.694z" fill="currentColor"/> </g> </svg> </button> </div> </div> </div> ` }] }], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i0.ViewContainerRef }, { type: i0.ElementRef }, { type: Document, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }]; }, propDecorators: { closable: [{ type: Input }], escapable: [{ type: Input }], dismissable: [{ type: Input }], identifier: [{ type: Input }], customClass: [{ type: Input }], visible: [{ type: Input }], backdrop: [{ type: Input }], force: [{ type: Input }], hideDelay: [{ type: Input }], autostart: [{ type: Input }], target: [{ type: Input }], ariaLabel: [{ type: Input }], ariaLabelledBy: [{ type: Input }], ariaDescribedBy: [{ type: Input }], refocus: [{ type: Input }], visibleChange: [{ type: Output }], onClose: [{ type: Output }], onCloseFinished: [{ type: Output }], onDismiss: [{ type: Output }], onDismissFinished: [{ type: Output }], onAnyCloseEvent: [{ type: Output }], onAnyCloseEventFinished: [{ type: Output }], onOpen: [{ type: Output }], onOpenFinished: [{ type: Output }], onEscape: [{ type: Output }], onDataAdded: [{ type: Output }], onDataRemoved: [{ type: Output }], nsmContent: [{ type: ViewChildren, args: ['nsmContent'] }], nsmDialog: [{ type: ViewChildren, args: ['nsmDialog'] }], nsmOverlay: [{ type: ViewChildren, args: ['nsmOverlay'] }], dynamicContentContainer: [{ type: ViewChild, args: ['dynamicContent', { read: ViewContainerRef }] }], targetPlacement: [{ type: HostListener, args: ['window:resize'] }] } }); class NgxSmartModalStackService { constructor() { this._modalStack = []; } /** * Add a new modal instance. This step is essential and allows to retrieve any modal at any time. * It stores an object that contains the given modal identifier and the modal itself directly in the `modalStack`. * * @param modalInstance The object that contains the given modal identifier and the modal itself. * @param force Optional parameter that forces the overriding of modal instance if it already exists. * @returns nothing special. */ addModal(modalInstance, force) { if (force) { const i = this._modalStack.findIndex((o) => o.id === modalInstance.id); if (i > -1) { this._modalStack[i].modal = modalInstance.modal; } else { this._modalStack.push(modalInstance); } return; } this._modalStack.push(modalInstance); } /** * Retrieve a modal instance by its identifier. * * @param id The modal identifier used at creation time. */ getModal(id) { const i = this._modalStack.find((o) => o.id === id); if (i !== undefined) { return i.modal; } else { throw new Error(`Cannot find modal with identifier ${id}`); } } /** * Retrieve all the created modals. * * @returns an array that contains all modal instances. */ getModalStack() { return this._modalStack; } /** * Retrieve all the opened modals. It looks for all modal instances with their `visible` property set to `true`. * * @returns an array that contains all the opened modals. */ getOpenedModals() { return this._modalStack.filter((o) => o.modal.visible); } /** * Retrieve the opened modal with highest z-index. * * @returns the opened modal with highest z-index. */ getTopOpenedModal() { if (!this.getOpenedModals().length) { throw new Error('No modal is opened'); } return this.getOpenedModals() .map((o) => o.modal) .reduce((highest, item) => item.layerPosition > highest.layerPosition ? item : highest, this.getOpenedModals()[0].modal); } /** * Get the higher `z-index` value between all the modal instances. It iterates over the `ModalStack` array and * calculates a higher value (it takes the highest index value between all the modal instances and adds 1). * Use it to make a modal appear foreground. * * @returns a higher index from all the existing modal instances. */ getHigherIndex() { return Math.max(...this._modalStack.map((o) => o.modal.layerPosition), 1041) + 1; } /** * It gives the number of modal instances. It's helpful to know if the modal stack is empty or not. * * @returns the number of modal instances. */ getModalStackCount() { return this._modalStack.length; } /** * Remove a modal instance from the modal stack. * Returns the removed modal instance or undefined if no modal was found * * @param id The modal identifier. * @returns the removed modal instance. */ removeModal(id) { const i = this._modalStack.findIndex((o) => o.id === id); if (i < 0) { return; } const modalInstance = this._modalStack.splice(i, 1)[0]; return modalInstance; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalStackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalStackService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalStackService, decorators: [{ type: Injectable }], ctorParameters: function () { return []; } }); class NgxSmartModalService { constructor(_appRef, _injector, _modalStack, applicationRef, _document, _platformId) { this._appRef = _appRef; this._injector = _injector; this._modalStack = _modalStack; this.applicationRef = applicationRef; this._document = _document; this._platformId = _platformId; /** * Close the latest opened modal if escape key event is emitted * @param event The Keyboard Event */ this._escapeKeyboardEvent = (event) => { if (event.key === 'Escape') { try { const modal = this.getTopOpenedModal(); if (!modal.escapable) { return false; } modal.onEscape.emit(modal); this.closeLatestModal(); return true; } catch (e) { return false; } } return false; }; /** * While modal is open, the focus stay on it * @param event The Keyboar dEvent */ this._trapFocusModal = (event) => { if (event.key === 'Tab') { try { const modal = this.getTopOpenedModal(); if (!modal.nsmDialog.first.nativeElement.contains(document.activeElement)) { event.preventDefault(); event.stopPropagation(); modal.nsmDialog.first.nativeElement.focus(); } return true; } catch (e) { return false; } } return false; }; this._addEvents(); } /** * Add a new modal instance. This step is essential and allows to retrieve any modal at any time. * It stores an object that contains the given modal identifier and the modal itself directly in the `modalStack`. * * @param modalInstance The object that contains the given modal identifier and the modal itself. * @param force Optional parameter that forces the overriding of modal instance if it already exists. * @returns nothing special. */ addModal(modalInstance, force) { this._modalStack.addModal(modalInstance, force); } /** * Retrieve a modal instance by its identifier. * * @param id The modal identifier used at creation time. */ getModal(id) { return this._modalStack.getModal(id); } /** * Alias of `getModal` to retrieve a modal instance by its identifier. * * @param id The modal identifier used at creation time. */ get(id) { return this.getModal(id); } /** * Open a given modal * * @param id The modal identifier used at creation time. * @param force Tell the modal to open top of all other opened modals */ open(id, force = false) { return this._openModal(this.get(id), force); } /** * Close a given modal * * @param id The modal identifier used at creation time. */ close(id) { return this._closeModal(this.get(id)); } /** * Close all opened modals */ closeAll() { this.getOpenedModals().forEach((instance) => { this._closeModal(instance.modal); }); } /** * Toggles a given modal * If the retrieved modal is opened it closes it, else it opens it. * * @param id The modal identifier used at creation time. * @param force Tell the modal to open top of all other opened modals */ toggle(id, force = false) { return this._toggleModal(this.get(id), force); } /** * Retrieve all the created modals. * * @returns an array that contains all modal instances. */ getModalStack() { return this._modalStack.getModalStack(); } /** * Retrieve all the opened modals. It looks for all modal instances with their `visible` property set to `true`. * * @returns an array that contains all the opened modals. */ getOpenedModals() { return this._modalStack.getOpenedModals(); } /** * Retrieve the opened modal with highest z-index. * * @returns the opened modal with highest z-index. */ getTopOpenedModal() { return this._modalStack.getTopOpenedModal(); } /** * Get the higher `z-index` value between all the modal instances. It iterates over the `ModalStack` array and * calculates a higher value (it takes the highest index value between all the modal instances and adds 1). * Use it to make a modal appear foreground. * * @returns a higher index from all the existing modal instances. */ getHigherIndex() { return this._modalStack.getHigherIndex(); } /** * It gives the number of modal instances. It's helpful to know if the modal stack is empty or not. * * @returns the number of modal instances. */ getModalStackCount() { return this._modalStack.getModalStackCount(); } /** * Remove a modal instance from the modal stack. * * @param id The modal identifier. * @returns the removed modal instance. */ removeModal(id) { const modalInstance = this._modalStack.removeModal(id); if (modalInstance) { this._destroyModal(modalInstance.modal); } } /** * Associate data to an identified modal. If the modal isn't already associated to some data, it creates a new * entry in the `modalData` array with its `id` and the given `data`. If the modal already has data, it rewrites * them with the new ones. Finally if no modal found it returns an error message in the console and false value * as method output. * * @param data The data you want to associate to the modal. * @param id The modal identifier. * @param force If true, overrides the previous stored data if there was. * @returns true if the given modal exists and the process has been tried, either false. */ setModalData(data, id, force) { const modal = this.get(id); if (modal) { modal.setData(data, force); return true; } else { return false; } } /** * Retrieve modal data by its identifier. * * @param id The modal identifier used at creation time. * @returns the associated modal data. */ getModalData(id) { const modal = this.get(id); if (modal) { return modal.getData(); } return null; } /** * Reset the data attached to a given modal. * * @param id The modal identifier used at creation time. * @returns the removed data or false if modal doesn't exist. */ resetModalData(id) { if (this._modalStack.getModalStack().find((o) => o.id === id)) { const removed = this.getModal(id).getData(); this.getModal(id).removeData(); return removed; } else { return false; } } /** * Close the latest opened modal */ closeLatestModal() { this.getTopOpenedModal().close(); } /** * Create dynamic NgxSmartModalComponent * @param vcr A ViewContainerRef reference * @param id The modal identifier used at creation time * @param content The modal content (string, templateRef or Component) * @param options Any NgxSmartModalComponent available options */ create(id, content, vcr, options = {}) { try { return this.getModal(id); } catch (e) { const ngContent = this._resolveNgContent(content); const componentRef = vcr.createComponent(NgxSmartModalComponent, { injector: this._injector, projectableNodes: ngContent }); if (content instanceof Type) { componentRef.instance.contentComponent = content; } componentRef.instance.identifier = id; componentRef.instance.createFrom = 'service'; if (typeof options.closable === 'boolean') { componentRef.instance.closable = options.closable; } if (typeof options.escapable === 'boolean') { componentRef.instance.escapable = options.escapable; } if (typeof options.dismissable === 'boolean') { componentRef.instance.dismissable = options.dismissable; } if (typeof options.customClass === 'string') { componentRef.instance.customClass = options.customClass; } if (typeof options.backdrop === 'boolean') { componentRef.instance.backdrop = options.backdrop; } if (typeof options.force === 'boolean') { componentRef.instance.force = options.force; } if (typeof options.hideDelay === 'number') { componentRef.instance.hideDelay = options.hideDelay; } if (typeof options.autostart === 'boolean') { componentRef.instance.autostart = options.autostart; } if (typeof options.target === 'string') { componentRef.instance.target = options.target; } if (typeof options.ariaLabel === 'string') { componentRef.instance.ariaLabel = options.ariaLabel; } if (typeof options.ariaLabelledBy === 'string') { componentRef.instance.ariaLabelledBy = options.ariaLabelledBy; } if (typeof options.ariaDescribedBy === 'string') { componentRef.instance.ariaDescribedBy = options.ariaDescribedBy; } if (typeof options.refocus === 'boolean') { componentRef.instance.refocus = options.refocus; } const domElem = componentRef.hostView.rootNodes[0]; this._document.body.appendChild(domElem); return componentRef.instance; } } _addEvents() { if (!this.isBrowser) { return false; } window.addEventListener(NgxSmartModalConfig.prefixEvent + 'create', ((e) => { this._initModal(e.detail.instance); })); window.addEventListener(NgxSmartModalConfig.prefixEvent + 'delete', ((e) => { this._deleteModal(e.detail.instance); })); window.addEventListener(NgxSmartModalConfig.prefixEvent + 'open', ((e) => { this._openModal(e.detail.instance.modal, e.detail.extraData.top); })); window.addEventListener(NgxSmartModalConfig.prefixEvent + 'toggle', ((e) => { this._toggleModal(e.detail.instance.modal, e.detail.extraData.top); })); window.addEventListener(NgxSmartModalConfig.prefixEvent + 'close', ((e) => { this._closeModal(e.detail.instance.modal); })); window.addEventListener(NgxSmartModalConfig.prefixEvent + 'dismiss', ((e) => { this._dismissModal(e.detail.instance.modal); })); window.addEventListener('keyup', this._escapeKeyboardEvent); return true; } _initModal(modalInstance) { modalInstance.modal.layerPosition += this.getModalStackCount(); this.addModal(modalInstance, modalInstance.modal.force); if (modalInstance.modal.autostart) { this.open(modalInstance.id); } } _openModal(modal, top) { if (modal.visible) { return false; } this.lastElementFocused = document.activeElement; if (modal.escapable) { window.addEventListener('keyup', this._escapeKeyboardEvent); } if (modal.backdrop) { window.addEventListener('keydown', this._trapFocusModal); } if (top) { modal.layerPosition = this.getHigherIndex(); } modal.addBodyClass(); modal.overlayVisible = true; modal.visible = true; modal.onOpen.emit(modal); modal.markForCheck(); setTimeout(() => { modal.openedClass = true; if (modal.target) { modal.targetPlacement(); } modal.nsmDialog.first.nativeElement.setAttribute('role', 'dialog'); modal.nsmDialog.first.nativeElement.setAttribute('tabIndex', '-1'); modal.nsmDialog.first.nativeElement.setAttribute('aria-modal', 'true'); modal.nsmDialog.first.nativeElement.focus(); modal.markForCheck(); modal.onOpenFinished.emit(modal); }); return true; } _toggleModal(modal, top) { if (modal.visible) { return this._closeModal(modal); } else { return this._openModal(modal, top); } } _closeModal(modal) { if (!modal.openedClass) { return false; } modal.openedClass = false; modal.onClose.emit(modal); modal.onAnyCloseEvent.emit(modal); if (this.getOpenedModals().length < 2) { modal.removeBodyClass(); window.removeEventListener('keyup', this._escapeKeyboardEvent); window.removeEventListener('keydown', this._trapFocusModal); } setTimeout(() => { modal.visibleChange.emit(modal.visible); modal.visible = false; modal.overlayVisible = false; modal.nsmDialog.first.nativeElement.removeAttribute('tabIndex'); modal.markForCheck(); modal.onCloseFinished.emit(modal); modal.onAnyCloseEventFinished.emit(modal); if (modal.refocus) { this.lastElementFocused.focus(); } }, modal.hideDelay); return true; } _dismissModal(modal) { if (!modal.openedClass) { return false; } modal.openedClass = false; modal.onDismiss.emit(modal); modal.onAnyCloseEvent.emit(modal); if (this.getOpenedModals().length < 2) { modal.removeBodyClass(); } setTimeout(() => { modal.visible = false; modal.visibleChange.emit(modal.visible); modal.overlayVisible = false; modal.markForCheck(); modal.onDismissFinished.emit(modal); modal.onAnyCloseEventFinished.emit(modal); }, modal.hideDelay); return true; } _deleteModal(modalInstance) { this.removeModal(modalInstance.id); if (!this.getModalStack().length) { modalInstance.modal.removeBodyClass(); } } /** * Resolve content according to the types * @param content The modal content (string, templateRef or Component) */ _resolveNgContent(content) { if (typeof content === 'string') { const element = this._document.createTextNode(content); return [[element]]; } if (content instanceof TemplateRef) { const viewRef = content.createEmbeddedView(null); this.applicationRef.attachView(viewRef); return [viewRef.rootNodes]; } return []; } /** * Is current platform browser */ get isBrowser() { return isPlatformBrowser(this._platformId); } /** * Remove dynamically created modal from DOM */ _destroyModal(modal) { // Prevent destruction of the inline modals if (modal.createFrom !== 'service') { return; } this._document.body.removeChild(modal.elementRef.nativeElement); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalService, deps: [{ token: i0.ApplicationRef }, { token: i0.Injector }, { token: NgxSmartModalStackService }, { token: i0.ApplicationRef }, { token: DOCUMENT }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: i0.ApplicationRef }, { type: i0.Injector }, { type: NgxSmartModalStackService }, { type: i0.ApplicationRef }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }]; } }); class NgxSmartModalModule { /** * Use in AppModule: new instance of NgxSmartModal. */ static forRoot() { return { ngModule: NgxSmartModalModule, providers: [ NgxSmartModalService, NgxSmartModalStackService ] }; } /** * Use in features modules with lazy loading: new instance of NgxSmartModal. */ static forChild() { return { ngModule: NgxSmartModalModule, providers: [ NgxSmartModalService, NgxSmartModalStackService ] }; } constructor(service) { this.service = service; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalModule, deps: [{ token: NgxSmartModalService }], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalModule, declarations: [NgxSmartModalComponent], imports: [CommonModule], exports: [NgxSmartModalComponent] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalModule, imports: [CommonModule] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.10", ngImport: i0, type: NgxSmartModalModule, decorators: [{ type: NgModule, args: [{ declarations: [ NgxSmartModalComponent ], imports: [ CommonModule ], exports: [ NgxSmartModalComponent ] }] }], ctorParameters: function () { return [{ type: NgxSmartModalService }]; } }); /* * Public API Surface of ngx-smart-modal */ /** * Generated bundle index. Do not edit. */ export { NgxSmartModalComponent, NgxSmartModalConfig, NgxSmartModalModule, NgxSmartModalService }; //# sourceMappingURL=ngx-smart-modal.mjs.map