ng2-encrm-components
Version:
259 lines (215 loc) • 8.01 kB
text/typescript
import {
Component,
Input,
AfterViewInit,
ElementRef,
ChangeDetectorRef,
OnDestroy,
ViewChild,
EventEmitter
} from "@angular/core";
import { Popover } from "./popover.directive";
export class PopoverContent implements AfterViewInit, OnDestroy {
// -------------------------------------------------------------------------
// Inputs / Outputs
// -------------------------------------------------------------------------
// @Input()
// hostElement: HTMLElement;
content: string;
placement: "top"|"bottom"|"left"|"right" = "bottom";
title: string;
animation: boolean = true;
closeOnClickOutside: boolean = true;
closeOnMouseOutside: boolean = false;
flavor: 'default' | 'success' | 'danger' | 'warning' | 'info' = 'default';
// -------------------------------------------------------------------------
// Properties
// -------------------------------------------------------------------------
popoverDiv: ElementRef;
popover: Popover;
onCloseFromOutside = new EventEmitter();
top: number = -1000;
left: number = -1000;
isIn: boolean = false;
displayType: string = "none";
// -------------------------------------------------------------------------
// Anonymous
// -------------------------------------------------------------------------
/**
* Closes dropdown if user clicks outside of this directive.
*/
onDocumentMouseDown = (event: any) => {
const element = this.element.nativeElement;
if (!element || !this.popover) return;
if (element.contains(event.target) || this.popover.getElement().contains(event.target)) return;
this.hide();
this.onCloseFromOutside.emit(undefined);
};
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private element: ElementRef,
private cdr: ChangeDetectorRef) {
}
// -------------------------------------------------------------------------
// Lifecycle callbacks
// -------------------------------------------------------------------------
ngAfterViewInit(): void {
if (this.closeOnClickOutside)
document.addEventListener("mousedown", this.onDocumentMouseDown);
if (this.closeOnMouseOutside)
document.addEventListener("mouseover", this.onDocumentMouseDown);
this.show();
this.cdr.detectChanges();
}
ngOnDestroy() {
if (this.closeOnClickOutside)
document.removeEventListener("mousedown", this.onDocumentMouseDown);
if (this.closeOnMouseOutside)
document.removeEventListener("mouseover", this.onDocumentMouseDown);
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
show(): void {
if (!this.popover || !this.popover.getElement())
return;
const p = this.positionElements(this.popover.getElement(), this.popoverDiv.nativeElement, this.placement);
this.displayType = "block";
this.top = p.top;
this.left = p.left;
this.isIn = true;
}
hide(): void {
this.top = -1000;
this.left = -1000;
this.isIn = true;
this.popover.hide();
}
hideFromPopover() {
this.top = -1000;
this.left = -1000;
this.isIn = true;
}
// -------------------------------------------------------------------------
// Private Methods
// -------------------------------------------------------------------------
private positionElements(hostEl: HTMLElement,
targetEl: HTMLElement,
positionStr: string,
appendToBody: boolean = false): { top: number, left: number } {
let positionStrParts = positionStr.split("-");
let pos0 = positionStrParts[0];
let pos1 = positionStrParts[1] || "center";
let hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
let targetElWidth = targetEl.offsetWidth;
let targetElHeight = targetEl.offsetHeight;
let shiftWidth: any = {
center: function (): number {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function (): number {
return hostElPos.left;
},
right: function (): number {
return hostElPos.left + hostElPos.width;
}
};
let shiftHeight: any = {
center: function (): number {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function (): number {
return hostElPos.top;
},
bottom: function (): number {
return hostElPos.top + hostElPos.height;
}
};
let targetElPos: { top: number, left: number };
switch (pos0) {
case "right":
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case "left":
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case "bottom":
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
private position(nativeEl: HTMLElement): { width: number, height: number, top: number, left: number } {
let offsetParentBCR = {top: 0, left: 0};
const elBCR = this.offset(nativeEl);
const offsetParentEl = this.parentOffsetEl(nativeEl);
if (offsetParentEl !== window.document) {
offsetParentBCR = this.offset(offsetParentEl);
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
const boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
}
private offset(nativeEl: any): { width: number, height: number, top: number, left: number } {
const boundingClientRect = nativeEl.getBoundingClientRect();
return {
width: boundingClientRect.width || nativeEl.offsetWidth,
height: boundingClientRect.height || nativeEl.offsetHeight,
top: boundingClientRect.top + (window.pageYOffset || window.document.documentElement.scrollTop),
left: boundingClientRect.left + (window.pageXOffset || window.document.documentElement.scrollLeft)
};
}
private getStyle(nativeEl: HTMLElement, cssProp: string): string {
if ((nativeEl as any).currentStyle) // IE
return (nativeEl as any).currentStyle[cssProp];
if (window.getComputedStyle)
return (window.getComputedStyle as any)(nativeEl)[cssProp];
// finally try and get inline style
return (nativeEl.style as any)[cssProp];
}
private isStaticPositioned(nativeEl: HTMLElement): boolean {
return (this.getStyle(nativeEl, "position") || "static" ) === "static";
}
private parentOffsetEl(nativeEl: HTMLElement): any {
let offsetParent: any = nativeEl.offsetParent || window.document;
while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || window.document;
}
}