1 import {Component, AfterViewInit, Input, ElementRef, ChangeDetectorRef} from "@angular/core";
5 selector: "tooltip-content",
6 templateUrl: "./tooltip-content.component.html",
7 styleUrls: ["./tooltip-content.component.less"]
10 export class TooltipContentComponent implements AfterViewInit {
12 // -------------------------------------------------------------------------
14 // -------------------------------------------------------------------------
16 @Input() hostElement: HTMLElement;
17 @Input() content: string;
18 @Input() placement: "top"|"bottom"|"left"|"right" = "bottom";
19 @Input() animation: boolean = true;
21 // -------------------------------------------------------------------------
23 // -------------------------------------------------------------------------
25 top: number = -100000;
26 left: number = -100000;
27 isIn: boolean = false;
28 isFade: boolean = false;
30 // -------------------------------------------------------------------------
32 // -------------------------------------------------------------------------
34 constructor(private element: ElementRef,
35 private cdr: ChangeDetectorRef) {
38 // -------------------------------------------------------------------------
39 // Lifecycle callbacks
40 // -------------------------------------------------------------------------
42 ngAfterViewInit(): void {
44 this.cdr.detectChanges();
47 // -------------------------------------------------------------------------
49 // -------------------------------------------------------------------------
52 if(!this.hostElement) {
56 const position = this.positionElement(this.hostElement, this.element.nativeElement.children[0], this.placement);
57 this.top = position.top;
58 this.left = position.left;
74 // -------------------------------------------------------------------------
76 // -------------------------------------------------------------------------
78 private positionElement(hostElem: HTMLElement, targetElem: HTMLElement, positionStr: string, appendToBody: boolean = false): {top: number, left: number} {
79 let positionStrParts = positionStr.split("-");
80 let pos0 = positionStrParts[0];
81 let pos1 = positionStrParts[1] || "center";
82 let hostElemPosition = appendToBody ? this.offset(hostElem) : this.position(hostElem);
83 let targetElemWidth = targetElem.offsetWidth;
84 let targetElemHeight = targetElem.offsetHeight;
85 let shiftWidth: any = {
87 return hostElemPosition.left + hostElemPosition.width / 2 - targetElemWidth / 2;
90 return hostElemPosition.left;
93 return hostElemPosition.left + hostElemPosition.width;
97 let shiftHeight: any = {
98 center: function (): number {
99 return hostElemPosition.top + hostElemPosition.height / 2 - targetElemHeight / 2;
101 top: function (): number {
102 return hostElemPosition.top;
104 bottom: function (): number {
105 return hostElemPosition.top + hostElemPosition.height;
109 let targetElemPosition: {top: number, left: number};
113 targetElemPosition = {
114 top: shiftHeight[pos1](),
115 left: shiftWidth[pos0]()
120 targetElemPosition = {
121 top: shiftHeight[pos1](),
122 left: hostElemPosition.left - targetElemWidth
127 targetElemPosition = {
128 top: shiftHeight[pos0](),
129 left: shiftWidth[pos1]()
134 targetElemPosition = {
135 top: hostElemPosition.top - targetElemHeight,
136 left: shiftWidth[pos1]()
141 return targetElemPosition;
145 private position(nativeElem: HTMLElement): {width: number, height: number, top: number, left: number} {
146 let offsetParentCBR = {top: 0, left: 0};
147 const elemBCR = this.offset(nativeElem);
148 const offsetParentElem = this.parentOffsetElem(nativeElem);
149 if(offsetParentElem !== window.document) {
150 offsetParentCBR = this.offset(offsetParentElem);
151 offsetParentCBR.top += offsetParentElem.clientTop - offsetParentElem.scrollTop;
152 offsetParentCBR.left += offsetParentElem.clientLeft - offsetParentElem.scrollTop;
155 const boundingClientRect = nativeElem.getBoundingClientRect();
158 width: boundingClientRect.width || nativeElem.offsetWidth,
159 height: boundingClientRect.height || nativeElem.offsetHeight,
160 top: elemBCR.top - offsetParentCBR.top,
161 left: elemBCR.left - offsetParentCBR.left
165 private offset(nativeElem:any): {width: number, height: number, top: number, left: number} {
166 const boundingClientRect = nativeElem.getBoundingClientRect();
168 width: boundingClientRect.width || nativeElem.offsetWidth,
169 height: boundingClientRect.height || nativeElem.offsetHeight,
170 top: boundingClientRect.top + (window.pageYOffset || window.document.documentElement.scrollTop),
171 left: boundingClientRect.left + (window.pageXOffset || window.document.documentElement.scrollLeft)
175 private getStyle(nativeElem: HTMLElement, cssProperty: string): string {
176 if(window.getComputedStyle) {
177 return (window.getComputedStyle(nativeElem) as any)[cssProperty];
180 return (nativeElem.style as any)[cssProperty];
183 private isStaticPositioned(nativeElem: HTMLElement): boolean {
184 return (this.getStyle(nativeElem, "position") || "static") === "static";
187 private parentOffsetElem(nativeElem: HTMLElement): any {
188 let offsetParent: any = nativeElem.offsetParent || window.document;
189 while (offsetParent && offsetParent !== window.document && this.isStaticPositioned(offsetParent)) {
190 offsetParent = offsetParent.offsetParent;
193 return offsetParent || window.document;