Merge "[1707-OS] Updated license text according to the"
[sdc.git] / catalog-ui / src / app / ng2 / components / tooltip / tooltip-content.component.ts
1 import {Component, AfterViewInit, Input, ElementRef, ChangeDetectorRef} from "@angular/core";
2
3 @Component
4 ({
5     selector: "tooltip-content",
6     templateUrl: "./tooltip-content.component.html",
7     styleUrls: ["./tooltip-content.component.less"]
8 })
9
10 export class TooltipContentComponent implements AfterViewInit {
11
12     // -------------------------------------------------------------------------
13     // Inputs / Outputs
14     // -------------------------------------------------------------------------
15
16     @Input() hostElement: HTMLElement;
17     @Input() content: string;
18     @Input() placement: "top"|"bottom"|"left"|"right" = "bottom";
19     @Input() animation: boolean = true;
20
21     // -------------------------------------------------------------------------
22     // Properties
23     // -------------------------------------------------------------------------
24
25     top: number = -100000;
26     left: number = -100000;
27     isIn: boolean = false;
28     isFade: boolean = false;
29
30     // -------------------------------------------------------------------------
31     // Constructor
32     // -------------------------------------------------------------------------
33
34     constructor(private element: ElementRef,
35                 private cdr: ChangeDetectorRef) {
36     }
37
38     // -------------------------------------------------------------------------
39     // Lifecycle callbacks
40     // -------------------------------------------------------------------------
41
42     ngAfterViewInit(): void {
43         this.show();
44         this.cdr.detectChanges();
45     }
46
47     // -------------------------------------------------------------------------
48     // Public Methods
49     // -------------------------------------------------------------------------
50
51     show(): void {
52         if(!this.hostElement) {
53             return;
54         }
55
56         const position = this.positionElement(this.hostElement, this.element.nativeElement.children[0], this.placement);
57         this.top = position.top;
58         this.left = position.left;
59         this.isIn = true;
60         if (this.animation) {
61             this.isFade = true;
62         }
63     }
64
65     hide(): void {
66         this.top = -100000;
67         this.left = -100000;
68         this.isIn = true;
69         if(this.animation) {
70             this.isFade = false;
71         }
72     }
73
74     // -------------------------------------------------------------------------
75     // Private Methods
76     // -------------------------------------------------------------------------
77
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 = {
86             center(): number {
87                 return hostElemPosition.left + hostElemPosition.width / 2 - targetElemWidth / 2;
88             },
89             left(): number {
90                 return hostElemPosition.left;
91             },
92             right(): number {
93                 return hostElemPosition.left + hostElemPosition.width;
94             }
95         };
96
97         let shiftHeight: any = {
98             center: function (): number {
99                 return hostElemPosition.top + hostElemPosition.height / 2 - targetElemHeight / 2;
100             },
101             top: function (): number {
102                 return hostElemPosition.top;
103             },
104             bottom: function (): number {
105                 return hostElemPosition.top + hostElemPosition.height;
106             }
107         }
108
109         let targetElemPosition: {top: number, left: number};
110
111         switch (pos0) {
112             case "right":
113                 targetElemPosition = {
114                     top: shiftHeight[pos1](),
115                     left: shiftWidth[pos0]()
116                 };
117                 break;
118
119             case "left":
120                 targetElemPosition = {
121                     top: shiftHeight[pos1](),
122                     left: hostElemPosition.left - targetElemWidth
123                 };
124                 break;
125
126             case "bottom":
127                 targetElemPosition = {
128                     top: shiftHeight[pos0](),
129                     left: shiftWidth[pos1]()
130                 };
131                 break;
132
133             default:
134                 targetElemPosition = {
135                     top: hostElemPosition.top - targetElemHeight,
136                     left: shiftWidth[pos1]()
137                 };
138                 break;
139         }
140
141         return targetElemPosition;
142     }
143
144
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;
153         }
154
155         const boundingClientRect = nativeElem.getBoundingClientRect();
156
157         return {
158             width: boundingClientRect.width || nativeElem.offsetWidth,
159             height: boundingClientRect.height || nativeElem.offsetHeight,
160             top: elemBCR.top - offsetParentCBR.top,
161             left: elemBCR.left - offsetParentCBR.left
162         };
163     }
164
165     private offset(nativeElem:any): {width: number, height: number, top: number, left: number} {
166         const boundingClientRect = nativeElem.getBoundingClientRect();
167         return {
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)
172         };
173     }
174
175     private getStyle(nativeElem: HTMLElement, cssProperty: string): string {
176         if(window.getComputedStyle) {
177             return (window.getComputedStyle(nativeElem) as any)[cssProperty];
178         }
179
180         return (nativeElem.style as any)[cssProperty];
181     }
182
183     private isStaticPositioned(nativeElem: HTMLElement): boolean {
184         return (this.getStyle(nativeElem, "position") || "static") === "static";
185     }
186
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;
191         }
192
193         return offsetParent || window.document;
194     }
195 }