import {NgModule,Component,ElementRef,AfterViewInit,AfterViewChecked,OnDestroy,Input,Output,EventEmitter,Renderer2,
ContentChildren,QueryList,ViewChild,NgZone} from '@angular/core';
import {trigger,state,style,transition,animate} from '@angular/animations';
import {CommonModule} from '@angular/common';
import {DomHandler} from '../dom/domhandler';
import {Header,Footer,SharedModule} from '../common/shared';
let idx: number = 0;
@Component({
selector: 'p-dialog',
template: `
`,
animations: [
trigger('dialogState', [
state('hidden', style({
opacity: 0
})),
state('visible', style({
opacity: 1
})),
transition('visible => hidden', animate('400ms ease-in')),
transition('hidden => visible', animate('400ms ease-out'))
])
],
providers: [DomHandler]
})
export class Dialog implements AfterViewInit,AfterViewChecked,OnDestroy {
@Input() header: string;
@Input() draggable: boolean = true;
@Input() resizable: boolean = true;
@Input() minWidth: number = 150;
@Input() minHeight: number = 150;
@Input() width: any;
@Input() height: any;
@Input() positionLeft: number;
@Input() positionTop: number;
@Input() contentStyle: any;
@Input() modal: boolean;
@Input() closeOnEscape: boolean = true;
@Input() dismissableMask: boolean;
@Input() rtl: boolean;
@Input() closable: boolean = true;
@Input() responsive: boolean = true;
@Input() appendTo: any;
@Input() style: any;
@Input() styleClass: string;
@Input() showHeader: boolean = true;
@Input() breakpoint: number = 640;
@Input() blockScroll: boolean = false;
@Input() autoZIndex: boolean = true;
@Input() baseZIndex: number = 0;
@Input() minX: number = 0;
@Input() minY: number = 0;
@Input() autoAlign: boolean = true;
@ContentChildren(Header, {descendants: false}) headerFacet: QueryList;
@ContentChildren(Footer, {descendants: false}) footerFacet: QueryList;
@ViewChild('container') containerViewChild: ElementRef;
@ViewChild('titlebar') headerViewChild: ElementRef;
@ViewChild('content') contentViewChild: ElementRef;
@Output() onShow: EventEmitter = new EventEmitter();
@Output() onHide: EventEmitter = new EventEmitter();
@Output() visibleChange:EventEmitter = new EventEmitter();
_visible: boolean;
dragging: boolean;
documentDragListener: any;
documentDragEndListener: any;
resizing: boolean;
documentResizeListener: any;
documentResizeEndListener: any;
documentResponsiveListener: any;
documentEscapeListener: Function;
maskClickListener: Function;
lastPageX: number;
lastPageY: number;
mask: HTMLDivElement;
closeIconMouseDown: boolean;
preWidth: number;
preventVisibleChangePropagation: boolean;
executePostDisplayActions: boolean;
initialized: boolean;
currentHeight: number;
id: string = `ui-dialog-${idx++}`;
constructor(public el: ElementRef, public domHandler: DomHandler, public renderer: Renderer2, public zone: NgZone) {}
@Input() get visible(): boolean {
return this._visible;
}
set visible(val:boolean) {
this._visible = val;
if(this.initialized && this.containerViewChild && this.containerViewChild.nativeElement) {
if(this._visible)
this.show();
else {
if(this.preventVisibleChangePropagation)
this.preventVisibleChangePropagation = false;
else
this.hide();
}
}
}
ngAfterViewChecked() {
if(this.executePostDisplayActions) {
this.onShow.emit({});
this.positionOverlay();
this.focus();
this.currentHeight = this.domHandler.getOuterHeight(this.containerViewChild.nativeElement);
this.executePostDisplayActions = false;
}
else if(this.autoAlign && this.visible) {
this.zone.runOutsideAngular(() => {
setTimeout(() => {
let height = this.domHandler.getOuterHeight(this.containerViewChild.nativeElement);
if(height !== this.currentHeight) {
this.currentHeight = height;
this.positionOverlay();
}
}, 50);
});
}
}
focus() {
let focusable = this.domHandler.findSingle(this.containerViewChild.nativeElement, 'button');
if(focusable) {
focusable.focus();
}
}
show() {
this.executePostDisplayActions = true;
this.moveOnTop();
this.bindGlobalListeners();
if(this.modal) {
this.enableModality();
}
}
positionOverlay() {
let viewport = this.domHandler.getViewport();
if(this.domHandler.getOuterHeight(this.containerViewChild.nativeElement) > viewport.height) {
this.contentViewChild.nativeElement.style.height = (viewport.height * .75) + 'px';
}
if(this.positionLeft >= 0 && this.positionTop >= 0) {
this.containerViewChild.nativeElement.style.left = this.positionLeft + 'px';
this.containerViewChild.nativeElement.style.top = this.positionTop + 'px';
}
else if (this.positionTop >= 0) {
this.center();
this.containerViewChild.nativeElement.style.top = this.positionTop + 'px';
}
else{
this.center();
}
}
hide() {
this.onHide.emit({});
this.unbindMaskClickListener();
this.unbindGlobalListeners();
this.dragging = false;
if(this.modal) {
this.disableModality();
}
}
close(event: Event) {
this.preventVisibleChangePropagation = true;
this.hide();
this.visibleChange.emit(false);
event.preventDefault();
}
ngAfterViewInit() {
this.initialized = true;
if(this.appendTo) {
if(this.appendTo === 'body')
document.body.appendChild(this.containerViewChild.nativeElement);
else
this.domHandler.appendChild(this.containerViewChild.nativeElement, this.appendTo);
}
if(this.visible) {
this.show();
}
}
center() {
let elementWidth = this.domHandler.getOuterWidth(this.containerViewChild.nativeElement);
let elementHeight = this.domHandler.getOuterHeight(this.containerViewChild.nativeElement);
if(elementWidth == 0 && elementHeight == 0) {
this.containerViewChild.nativeElement.style.visibility = 'hidden';
this.containerViewChild.nativeElement.style.display = 'block';
elementWidth = this.domHandler.getOuterWidth(this.containerViewChild.nativeElement);
elementHeight = this.domHandler.getOuterHeight(this.containerViewChild.nativeElement);
this.containerViewChild.nativeElement.style.display = 'none';
this.containerViewChild.nativeElement.style.visibility = 'visible';
}
let viewport = this.domHandler.getViewport();
let x = Math.max((viewport.width - elementWidth) / 2, 0);
let y = Math.max((viewport.height - elementHeight) / 2, 0);
this.containerViewChild.nativeElement.style.left = x + 'px';
this.containerViewChild.nativeElement.style.top = y + 'px';
}
enableModality() {
if(!this.mask) {
this.mask = document.createElement('div');
this.mask.style.zIndex = String(parseInt(this.containerViewChild.nativeElement.style.zIndex) - 1);
let maskStyleClass = 'ui-widget-overlay ui-dialog-mask';
if(this.blockScroll) {
maskStyleClass += ' ui-dialog-mask-scrollblocker';
}
this.domHandler.addMultipleClasses(this.mask, maskStyleClass);
if(this.closable && this.dismissableMask) {
this.maskClickListener = this.renderer.listen(this.mask, 'click', (event: any) => {
this.close(event);
});
}
document.body.appendChild(this.mask);
if(this.blockScroll) {
this.domHandler.addClass(document.body, 'ui-overflow-hidden');
}
}
}
disableModality() {
if(this.mask) {
document.body.removeChild(this.mask);
if(this.blockScroll) {
let bodyChildren = document.body.children;
let hasBlockerMasks: boolean;
for(let i = 0; i < bodyChildren.length; i++) {
let bodyChild = bodyChildren[i];
if(this.domHandler.hasClass(bodyChild, 'ui-dialog-mask-scrollblocker')) {
hasBlockerMasks = true;
break;
}
}
if(!hasBlockerMasks) {
this.domHandler.removeClass(document.body, 'ui-overflow-hidden');
}
}
this.mask = null;
}
}
unbindMaskClickListener() {
if(this.maskClickListener) {
this.maskClickListener();
this.maskClickListener = null;
}
}
moveOnTop() {
if(this.autoZIndex) {
this.containerViewChild.nativeElement.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
}
}
onCloseMouseDown(event: Event) {
this.closeIconMouseDown = true;
}
initDrag(event: MouseEvent) {
if(this.closeIconMouseDown) {
this.closeIconMouseDown = false;
return;
}
if(this.draggable) {
this.dragging = true;
this.lastPageX = event.pageX;
this.lastPageY = event.pageY;
this.domHandler.addClass(document.body, 'ui-unselectable-text');
}
}
onDrag(event: MouseEvent) {
if(this.dragging) {
let deltaX = event.pageX - this.lastPageX;
let deltaY = event.pageY - this.lastPageY;
let leftPos = parseInt(this.containerViewChild.nativeElement.style.left) + deltaX;
let topPos = parseInt(this.containerViewChild.nativeElement.style.top) + deltaY;
if(leftPos >= this.minX) {
this.containerViewChild.nativeElement.style.left = leftPos + 'px';
}
if(topPos >= this.minY) {
this.containerViewChild.nativeElement.style.top = topPos + 'px';
}
this.lastPageX = event.pageX;
this.lastPageY = event.pageY;
}
}
endDrag(event: MouseEvent) {
if(this.draggable) {
this.dragging = false;
this.domHandler.removeClass(document.body, 'ui-unselectable-text');
}
}
initResize(event: MouseEvent) {
if(this.resizable) {
this.preWidth = null;
this.resizing = true;
this.lastPageX = event.pageX;
this.lastPageY = event.pageY;
this.domHandler.addClass(document.body, 'ui-unselectable-text');
}
}
onResize(event: MouseEvent) {
if(this.resizing) {
let deltaX = event.pageX - this.lastPageX;
let deltaY = event.pageY - this.lastPageY;
let containerWidth = this.domHandler.getOuterWidth(this.containerViewChild.nativeElement);
let containerHeight = this.domHandler.getOuterHeight(this.containerViewChild.nativeElement);
let contentHeight = this.domHandler.getOuterHeight(this.contentViewChild.nativeElement);
let newWidth = containerWidth + deltaX;
let newHeight = containerHeight + deltaY;
if(newWidth > this.minWidth) {
this.containerViewChild.nativeElement.style.width = newWidth + 'px';
}
if(newHeight > this.minHeight) {
this.containerViewChild.nativeElement.style.height = newHeight + 'px';
this.contentViewChild.nativeElement.style.height = contentHeight + deltaY + 'px';
}
this.lastPageX = event.pageX;
this.lastPageY = event.pageY;
}
}
onResizeEnd(event: MouseEvent) {
if(this.resizing) {
this.resizing = false;
this.domHandler.removeClass(document.body, 'ui-unselectable-text');
}
}
bindGlobalListeners() {
if(this.draggable) {
this.bindDocumentDragListener();
this.bindDocumentDragEndListener();
}
if(this.resizable) {
this.bindDocumentResizeListeners();
}
if(this.responsive) {
this.bindDocumentResponsiveListener();
}
if(this.closeOnEscape && this.closable) {
this.bindDocumentEscapeListener();
}
}
unbindGlobalListeners() {
this.unbindDocumentDragListener();
this.unbindDocumentDragEndListener();
this.unbindDocumentResizeListeners();
this.unbindDocumentResponsiveListener();
this.unbindDocumentEscapeListener();
}
bindDocumentDragListener() {
this.zone.runOutsideAngular(() => {
this.documentDragListener = this.onDrag.bind(this);
window.document.addEventListener('mousemove', this.documentDragListener);
});
}
unbindDocumentDragListener() {
if(this.documentDragListener) {
window.document.removeEventListener('mousemove', this.documentDragListener);
this.documentDragListener = null;
}
}
bindDocumentDragEndListener() {
this.zone.runOutsideAngular(() => {
this.documentDragEndListener = this.endDrag.bind(this);
window.document.addEventListener('mouseup', this.documentDragEndListener);
});
}
unbindDocumentDragEndListener() {
if(this.documentDragEndListener) {
window.document.removeEventListener('mouseup', this.documentDragEndListener);
this.documentDragEndListener = null;
}
}
bindDocumentResizeListeners() {
this.zone.runOutsideAngular(() => {
this.documentResizeListener = this.onResize.bind(this);
this.documentResizeEndListener = this.onResizeEnd.bind(this);
window.document.addEventListener('mousemove', this.documentResizeListener);
window.document.addEventListener('mouseup', this.documentResizeEndListener);
});
}
unbindDocumentResizeListeners() {
if(this.documentResizeListener && this.documentResizeEndListener) {
window.document.removeEventListener('mouseup', this.documentResizeListener);
window.document.removeEventListener('mouseup', this.documentResizeEndListener);
this.documentResizeListener = null;
this.documentResizeEndListener = null;
}
}
bindDocumentResponsiveListener() {
this.zone.runOutsideAngular(() => {
this.documentResponsiveListener = this.onWindowResize.bind(this);
window.addEventListener('resize', this.documentResponsiveListener);
});
}
unbindDocumentResponsiveListener() {
if(this.documentResponsiveListener) {
window.removeEventListener('resize', this.documentResponsiveListener);
this.documentResponsiveListener = null;
}
}
onWindowResize(event) {
let viewport = this.domHandler.getViewport();
let width = this.domHandler.getOuterWidth(this.containerViewChild.nativeElement);
if(viewport.width <= this.breakpoint) {
if(!this.preWidth) {
this.preWidth = width;
}
this.containerViewChild.nativeElement.style.left = '0px';
this.containerViewChild.nativeElement.style.width = '100%';
}
else {
this.containerViewChild.nativeElement.style.width = this.preWidth + 'px';
this.positionOverlay();
}
}
bindDocumentEscapeListener() {
this.documentEscapeListener = this.renderer.listen('document', 'keydown', (event) => {
if(event.which == 27) {
if(parseInt(this.containerViewChild.nativeElement.style.zIndex) == DomHandler.zindex) {
this.close(event);
}
}
});
}
unbindDocumentEscapeListener() {
if(this.documentEscapeListener) {
this.documentEscapeListener();
this.documentEscapeListener = null;
}
}
ngOnDestroy() {
this.initialized = false;
this.disableModality();
this.unbindGlobalListeners();
if(this.appendTo) {
this.el.nativeElement.appendChild(this.containerViewChild.nativeElement);
}
this.unbindMaskClickListener();
}
}
@NgModule({
imports: [CommonModule],
exports: [Dialog,SharedModule],
declarations: [Dialog]
})
export class DialogModule { }