UNPKG

ng2-encrm-components

Version:
259 lines (215 loc) 8.01 kB
import { Component, Input, AfterViewInit, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, EventEmitter } from "@angular/core"; import { Popover } from "./popover.directive"; @Component({ selector: "popover-content", template: require('./popover-conent.component.html'), styles: [require('./popover-content.component.scss')] }) export class PopoverContent implements AfterViewInit, OnDestroy { // ------------------------------------------------------------------------- // Inputs / Outputs // ------------------------------------------------------------------------- // @Input() // hostElement: HTMLElement; @Input() content: string; @Input() placement: "top"|"bottom"|"left"|"right" = "bottom"; @Input() title: string; @Input() animation: boolean = true; @Input() closeOnClickOutside: boolean = true; @Input() closeOnMouseOutside: boolean = false; @Input() flavor: 'default' | 'success' | 'danger' | 'warning' | 'info' = 'default'; // ------------------------------------------------------------------------- // Properties // ------------------------------------------------------------------------- @ViewChild("popoverDiv") 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; } }