novo-elements
Version:
Bullhorn's NOVO Element Repository for Angular 2
219 lines (190 loc) • 7.82 kB
text/typescript
import { Component, AfterViewInit, OnDestroy, Input, ViewChild, ElementRef, EventEmitter, ChangeDetectorRef } from '@angular/core';
import { PopOverDirective } from './PopOver';
export class PopOverContent implements AfterViewInit {
content: string;
placement: string = 'top';
title: string;
animation: boolean = true;
popoverDiv: ElementRef;
popover: PopOverDirective;
onCloseFromOutside = new EventEmitter();
top: number = -10000;
left: number = -10000;
displayType: string = 'none';
effectivePlacement: string;
effectiveAlignment: string;
isHidden: boolean = false;
constructor(protected element: ElementRef,
protected cdr: ChangeDetectorRef) {
}
ngAfterViewInit(): void {
this.show();
this.cdr.detectChanges();
}
toggle(): void {
if (this.isHidden) {
this.show();
} else {
this.hide();
}
}
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.isHidden = false;
}
hide(): void {
this.top = -10000;
this.left = -10000;
this.isHidden = true;
this.popover.hide();
}
hideFromPopover() {
this.top = -10000;
this.left = -10000;
}
protected positionElements(hostEl: HTMLElement, targetEl: HTMLElement, positionStr: string, appendToBody = false): { top: number, left: number } {
let positionStrParts = positionStr.split('-');
let mainSide = this.effectivePlacement = this.getEffectivePlacement(positionStrParts[0] || 'right', hostEl, targetEl);
let orientation = this.effectiveAlignment = 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 - targetElWidth) / 2;
},
right: function (): number {
return hostElPos.left;
},
left: function (): number {
return hostElPos.left + (hostElPos.width - targetElWidth);
}
};
let shiftHeight: any = {
center: function (): number {
return hostElPos.top + (hostElPos.height - targetElHeight) / 2;
},
bottom: function (): number {
return hostElPos.top;
},
top: function (): number {
return hostElPos.top + (hostElPos.height - targetElHeight);
}
};
let targetElPos: { top: number, left: number };
switch (mainSide) {
case 'right':
targetElPos = {
top: shiftHeight[orientation](),
left: hostElPos.left + hostElPos.width
};
break;
case 'left':
targetElPos = {
top: shiftHeight[orientation](),
left: hostElPos.left - targetElWidth
};
break;
case 'bottom':
targetElPos = {
top: hostElPos.top + hostElPos.height,
left: shiftWidth[orientation]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[orientation]()
};
break;
}
return targetElPos;
}
protected 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
};
}
protected 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)
};
}
protected getStyle(nativeEl: HTMLElement, cssProp: string): string {
if ((nativeEl as any).currentStyle) {
return (nativeEl as any).currentStyle[cssProp];
}
if (window.getComputedStyle) {
return (window.getComputedStyle as any)(nativeEl)[cssProp];
}
return (nativeEl.style as any)[cssProp];
}
protected isStaticPositioned(nativeEl: HTMLElement): boolean {
return (this.getStyle(nativeEl, 'position') || 'static') === 'static';
}
protected 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;
}
protected getEffectivePlacement(desiredPlacement: string, hostElement: HTMLElement, targetElement: HTMLElement): string {
const hostElBoundingRect = hostElement.getBoundingClientRect();
if (desiredPlacement === 'top' && hostElBoundingRect.top - targetElement.offsetHeight < 0) {
return 'bottom';
}
if (desiredPlacement === 'bottom' && hostElBoundingRect.bottom + targetElement.offsetHeight > window.innerHeight) {
return 'top';
}
if (desiredPlacement === 'left' && hostElBoundingRect.left - targetElement.offsetWidth < 0) {
return 'right';
}
if (desiredPlacement === 'right' && hostElBoundingRect.right + targetElement.offsetWidth > window.innerWidth) {
return 'left';
}
return desiredPlacement;
}
}