UNPKG

ngx-modialog

Version:
299 lines (298 loc) 10.3 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ import { Component, ElementRef, ViewChild, ViewContainerRef, ViewEncapsulation, Renderer2, TemplateRef } from '@angular/core'; import { PromiseCompleter, supportsKey } from '../framework/utils'; import { DialogRef } from '../models/dialog-ref'; import { BaseDynamicComponent } from '../components/index'; // TODO: use DI factory for this. // TODO: consolidate dup code const /** @type {?} */ isDoc = !(typeof document === 'undefined' || !document); /** * @record */ export function EmbedComponentConfig() { } function EmbedComponentConfig_tsickle_Closure_declarations() { /** @type {?} */ EmbedComponentConfig.prototype.component; /** @type {?|undefined} */ EmbedComponentConfig.prototype.projectableNodes; } /** * Represents the modal overlay. */ export class ModalOverlay extends BaseDynamicComponent { /** * @param {?} dialogRef * @param {?} vcr * @param {?} el * @param {?} renderer */ constructor(dialogRef, vcr, el, renderer) { super(el, renderer); this.dialogRef = dialogRef; this.vcr = vcr; this.activateAnimationListener(); } /** * \@internal * @template T * @param {?} content * @return {?} */ getProjectables(content) { let /** @type {?} */ nodes; if (typeof content === 'string') { nodes = [[this.renderer.createText(`${content}`)]]; } else if (content instanceof TemplateRef) { nodes = [this.vcr.createEmbeddedView(content, { $implicit: this.dialogRef.context, dialogRef: this.dialogRef }).rootNodes]; } else { nodes = [this.embedComponent({ component: content }).rootNodes]; } return nodes; } /** * @param {?} config * @return {?} */ embedComponent(config) { const /** @type {?} */ ctx = /** @type {?} */ (config); return this.vcr.createEmbeddedView(this.template, /** @type {?} */ ({ $implicit: ctx })); } /** * @template T * @param {?} type * @param {?=} projectableNodes * @return {?} */ addComponent(type, projectableNodes = []) { return super._addComponent({ component: type, vcRef: this.innerVcr, projectableNodes }); } /** * @return {?} */ fullscreen() { const /** @type {?} */ style = { position: 'fixed', top: 0, left: 0, bottom: 0, right: 0, 'z-index': 1500 }; Object.keys(style).forEach(k => this.setStyle(k, style[k])); } /** * @return {?} */ insideElement() { const /** @type {?} */ style = { position: 'absolute', overflow: 'hidden', width: '100%', height: '100%', top: 0, left: 0, bottom: 0, right: 0 }; Object.keys(style).forEach(k => this.setStyle(k, style[k])); } /** * Set a specific inline style for the container of the whole dialog component * The dialog component root element is the host of this component, it contains only 1 direct * child which is the container. * * Structure: * * ```html * <modal-overlay> * <div> * <!-- BACKDROP ELEMENT --> * <!-- DIALOG CONTAINER ELEMENT --> * </div> * </modal-overlay> * ``` * * @param {?} prop The style key * @param {?} value The value, undefined to remove * @return {?} */ setContainerStyle(prop, value) { this.renderer.setStyle(this.container.nativeElement, prop, value); return this; } /** * Define an element that click inside it will not trigger modal close. * Since events bubble, clicking on a dialog will bubble up to the overlay, a plugin * must define an element that represent the dialog, the overlay will make sure no to close when * it was clicked. * @param {?} element * @return {?} */ setClickBoundary(element) { let /** @type {?} */ target; const /** @type {?} */ elListener = event => target = /** @type {?} */ (event.target); const /** @type {?} */ docListener = event => { if (this.dialogRef.context.isBlocking || !this.dialogRef.overlay.isTopMost(this.dialogRef)) { return; } let /** @type {?} */ current = event.target; // on click, this will hit. if (current === target) return; // on mouse down -> drag -> release the current might not be 'target', it might be // a sibling or a child (i.e: not part of the tree-up direction). It might also be a release // outside the dialog... so we compare to the boundary element do { if (current === element) { return; } } while (current.parentNode && (current = current.parentNode)); this.dialogRef.dismiss(); }; if (isDoc) { this.dialogRef.onDestroy.subscribe(() => { element.removeEventListener('click', elListener, false); element.removeEventListener('touchstart', elListener, false); document.removeEventListener('click', docListener, false); document.removeEventListener('touchend', docListener, false); }); setTimeout(() => { element.addEventListener('mousedown', elListener, false); element.addEventListener('touchstart', docListener, false); document.addEventListener('click', docListener, false); document.addEventListener('touchend', docListener, false); }); } } /** * Temp workaround for animation where destruction of the top level component does not * trigger child animations. Solution should be found either in animation module or in design * of the modal component tree. * @return {?} */ canDestroy() { const /** @type {?} */ completer = new PromiseCompleter(); if (!Array.isArray(this.beforeDestroyHandlers)) { completer.resolve(); } else { // run destroy notification but protect against halt. let /** @type {?} */ id = setTimeout(() => { id = null; completer.reject(); }, 1000); const /** @type {?} */ resolve = () => { if (id === null) return; clearTimeout(id); completer.resolve(); }; Promise.all(this.beforeDestroyHandlers.map(fn => fn())) .then(resolve) .catch(resolve); } return completer.promise; } /** * A handler running before destruction of the overlay * use to delay destruction due to animation. * This is part of the workaround for animation, see canDestroy. * * NOTE: There is no guarantee that the listeners will fire, use dialog.onDestory for that. * @param {?} fn * @return {?} */ beforeDestroy(fn) { if (!this.beforeDestroyHandlers) { this.beforeDestroyHandlers = []; } this.beforeDestroyHandlers.push(fn); } /** * @param {?} event * @return {?} */ documentKeypress(event) { // check that this modal is the last in the stack. if (!this.dialogRef.overlay.isTopMost(this.dialogRef)) return; if (supportsKey(event.keyCode, /** @type {?} */ (this.dialogRef.context.keyboard))) { this.dialogRef.dismiss(); } } /** * @return {?} */ ngOnDestroy() { super.ngOnDestroy(); if (this.dialogRef.destroyed !== true) { // if we're here the overlay is destroyed by an external event that is not user invoked. // i.e: The user did no call dismiss or close and dialogRef.destroy() did not invoke. // this will happen when routing or killing an element containing a blocked overlay (ngIf) // we bail out, i.e gracefully shutting down. this.dialogRef.bailOut(); } } } ModalOverlay.decorators = [ { type: Component, args: [{ selector: 'modal-overlay', host: { '(body:keydown)': 'documentKeypress($event)' }, encapsulation: ViewEncapsulation.None, template: `<div #container> <ng-template #innerView></ng-template> </div> <ng-template #template let-ctx> <ng-container *ngComponentOutlet="ctx.component; injector: ctx.injector; content: ctx.projectableNodes"></ng-container> </ng-template>` },] }, ]; /** @nocollapse */ ModalOverlay.ctorParameters = () => [ { type: DialogRef, }, { type: ViewContainerRef, }, { type: ElementRef, }, { type: Renderer2, }, ]; ModalOverlay.propDecorators = { "container": [{ type: ViewChild, args: ['container', { read: ElementRef },] },], "innerVcr": [{ type: ViewChild, args: ['innerView', { read: ViewContainerRef },] },], "template": [{ type: ViewChild, args: ['template',] },], }; function ModalOverlay_tsickle_Closure_declarations() { /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */ ModalOverlay.decorators; /** * @nocollapse * @type {function(): !Array<(null|{type: ?, decorators: (undefined|!Array<{type: !Function, args: (undefined|!Array<?>)}>)})>} */ ModalOverlay.ctorParameters; /** @type {!Object<string,!Array<{type: !Function, args: (undefined|!Array<?>)}>>} */ ModalOverlay.propDecorators; /** @type {?} */ ModalOverlay.prototype.beforeDestroyHandlers; /** @type {?} */ ModalOverlay.prototype.container; /** @type {?} */ ModalOverlay.prototype.innerVcr; /** @type {?} */ ModalOverlay.prototype.template; /** @type {?} */ ModalOverlay.prototype.dialogRef; /** @type {?} */ ModalOverlay.prototype.vcr; } //# sourceMappingURL=overlay.component.js.map