ngx-smart-modal-2
Version:
Smart modal handler to manage modals and data everywhere in Angular apps.
285 lines (238 loc) • 10.7 kB
text/typescript
import {
Input,
Output,
OnInit,
OnDestroy,
Renderer2,
Component,
EventEmitter,
HostListener,
ChangeDetectorRef,
ViewChild,
ElementRef,
Inject,
PLATFORM_ID,
} from '@angular/core';
import { NgxSmartModalService } from '../services/ngx-smart-modal.service';
import { isPlatformBrowser } from '@angular/common';
export class NgxSmartModalComponent implements OnInit, OnDestroy {
public closable: boolean = true;
public escapable: boolean = true;
public dismissable: boolean = true;
public identifier: string = '';
public customClass: string = 'nsm-dialog-animation-fade';
public visible: boolean = false;
public backdrop: boolean = true;
public force: boolean = true;
public hideDelay: number = 500;
public autostart: boolean = false;
public target: any;
public visibleChange: EventEmitter<boolean> = new EventEmitter<boolean>();
public onClose: EventEmitter<any> = new EventEmitter();
public onCloseFinished: EventEmitter<any> = new EventEmitter();
public onDismiss: EventEmitter<any> = new EventEmitter();
public onDismissFinished: EventEmitter<any> = new EventEmitter();
public onAnyCloseEvent: EventEmitter<any> = new EventEmitter();
public onAnyCloseEventFinished: EventEmitter<any> = new EventEmitter();
public onOpen: EventEmitter<any> = new EventEmitter();
public onEscape: EventEmitter<any> = new EventEmitter();
public onDataAdded: EventEmitter<any> = new EventEmitter();
public onDataRemoved: EventEmitter<any> = new EventEmitter();
public layerPosition: number = 1041;
public overlayVisible: boolean = false;
public openedClass: boolean = false;
private _data: any;
private nsmContent: ElementRef | undefined;
private nsmDialog: ElementRef | undefined;
private nsmOverlay: ElementRef | undefined;
constructor(
private _renderer: Renderer2,
private _changeDetectorRef: ChangeDetectorRef,
private _ngxSmartModalService: NgxSmartModalService,
private platformId: Object,
) {
}
public ngOnInit() {
if (!!this.identifier && this.identifier.length) {
this.layerPosition += this._ngxSmartModalService.getModalStackCount();
this._ngxSmartModalService.addModal({ id: this.identifier, modal: this }, this.force);
if (this.autostart) {
this._ngxSmartModalService.open(this.identifier);
}
} else {
throw new Error('identifier field isn’t set. Please set one before calling <ngx-smart-modal> in a template.');
}
}
public ngOnDestroy() {
this._ngxSmartModalService.removeModal(this.identifier);
if (isPlatformBrowser(this.platformId)) {
window.removeEventListener('keyup', this.escapeKeyboardEvent);
}
if (!this._ngxSmartModalService.getModalStack.length && isPlatformBrowser(this.platformId)) {
this._renderer.removeClass(document.body, 'dialog-open');
}
}
public open(top?: boolean): void {
if (top) {
this.layerPosition = this._ngxSmartModalService.getHigherIndex();
}
if (isPlatformBrowser(this.platformId)) {
this._renderer.addClass(document.body, 'dialog-open');
}
this.overlayVisible = true;
this.visible = true;
setTimeout(() => {
this.openedClass = true;
if (this.target) {
this.targetPlacement();
}
this._changeDetectorRef.markForCheck();
});
this.onOpen.emit(this);
if (this.escapable && isPlatformBrowser(this.platformId)) {
window.addEventListener('keyup', this.escapeKeyboardEvent);
}
}
public close(): void {
const me = this;
this.openedClass = false;
this.onClose.emit(this);
this.onAnyCloseEvent.emit(this);
if (this._ngxSmartModalService.getOpenedModals().length < 2 && isPlatformBrowser(this.platformId)) {
this._renderer.removeClass(document.body, 'dialog-open');
}
setTimeout(() => {
me.visibleChange.emit(me.visible);
me.visible = false;
me.overlayVisible = false;
me._changeDetectorRef.markForCheck();
me.onCloseFinished.emit(me);
me.onAnyCloseEventFinished.emit(me);
}, this.hideDelay);
if (isPlatformBrowser(this.platformId)) {
window.removeEventListener('keyup', this.escapeKeyboardEvent);
}
}
public dismiss(e: any): void {
const me = this;
if (!this.dismissable) {
return;
}
if (e.target.classList.contains('overlay')) {
this.openedClass = false;
this.onDismiss.emit(this);
this.onAnyCloseEvent.emit(this);
if (this._ngxSmartModalService.getOpenedModals().length < 2 && isPlatformBrowser(this.platformId)) {
this._renderer.removeClass(document.body, 'dialog-open');
}
setTimeout(() => {
me.visible = false;
me.visibleChange.emit(me.visible);
me.overlayVisible = false;
me._changeDetectorRef.markForCheck();
me.onDismissFinished.emit(me);
me.onAnyCloseEventFinished.emit(me);
}, this.hideDelay);
if (isPlatformBrowser(this.platformId)) {
window.removeEventListener('keyup', this.escapeKeyboardEvent);
}
}
}
public toggle(top?: boolean) {
if (this.visible) {
this.close();
} else {
this.open(top);
}
}
public addCustomClass(className: string): void {
if (!this.customClass.length) {
this.customClass = className;
} else {
this.customClass += ' ' + className;
}
}
public removeCustomClass(className?: string): void {
if (className) {
this.customClass = this.customClass.replace(className, '').trim();
} else {
this.customClass = '';
}
}
public isVisible(): boolean {
return this.visible;
}
public hasData(): boolean {
return this._data !== undefined;
}
public setData(data: any, force?: boolean): any {
if (!this.hasData() || (this.hasData() && force)) {
this._data = data;
this.onDataAdded.emit(this._data);
this._changeDetectorRef.markForCheck();
}
}
public getData(): any {
return this._data;
}
public removeData(): void {
this._data = undefined;
this.onDataRemoved.emit(true);
this._changeDetectorRef.markForCheck();
}
public escapeKeyboardEvent = (event: KeyboardEvent) => {
if (event.keyCode === 27) {
this.onEscape.emit(this);
this._ngxSmartModalService.closeLatestModal();
}
}
public targetPlacement() {
if (!this.nsmDialog || !this.nsmContent || !this.nsmOverlay || !this.target || !isPlatformBrowser(this.platformId)) {
return;
}
const targetElementRect = document.querySelector(this.target).getBoundingClientRect();
const bodyRect = this.nsmOverlay.nativeElement.getBoundingClientRect();
const nsmContentRect = this.nsmContent.nativeElement.getBoundingClientRect();
const nsmDialogRect = this.nsmDialog.nativeElement.getBoundingClientRect();
const marginLeft = parseInt(getComputedStyle(this.nsmContent.nativeElement).marginLeft as any, 10);
const marginTop = parseInt(getComputedStyle(this.nsmContent.nativeElement).marginTop as any, 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;
}
if (offsetTop < 0) {
offsetTop = 0;
}
this._renderer.setStyle(this.nsmContent.nativeElement, 'top', offsetTop + 'px');
this._renderer.setStyle(this.nsmContent.nativeElement, 'left', offsetLeft + 'px');
}
}