ed9005c1fdca47ca3bc135a456a8e3423d5856e1
[sdc/sdc-workflow-designer.git] /
1 // previous version:
2 // https://github.com/angular-ui/bootstrap/blob/07c31d0731f7cb068a1932b8e01d2312b796b4ec/src/position/position.js
3 export class Positioning {
4   private getStyle(element: HTMLElement, prop: string): string { return window.getComputedStyle(element)[prop]; }
5
6   private isStaticPositioned(element: HTMLElement): boolean {
7     return (this.getStyle(element, 'position') || 'static') === 'static';
8   }
9
10   private offsetParent(element: HTMLElement): HTMLElement {
11     let offsetParentEl = <HTMLElement>element.offsetParent || document.documentElement;
12
13     while (offsetParentEl && offsetParentEl !== document.documentElement && this.isStaticPositioned(offsetParentEl)) {
14       offsetParentEl = <HTMLElement>offsetParentEl.offsetParent;
15     }
16
17     return offsetParentEl || document.documentElement;
18   }
19
20   public position(element: HTMLElement, round = true): ClientRect {
21     let elPosition: ClientRect;
22     let parentOffset: ClientRect = {width: 0, height: 0, top: 0, bottom: 0, left: 0, right: 0};
23
24     if (this.getStyle(element, 'position') === 'fixed') {
25       elPosition = element.getBoundingClientRect();
26     } else {
27       const offsetParentEl = this.offsetParent(element);
28
29       elPosition = this.offset(element, false);
30
31       if (offsetParentEl !== document.documentElement) {
32         parentOffset = this.offset(offsetParentEl, false);
33       }
34
35       parentOffset.top += offsetParentEl.clientTop;
36       parentOffset.left += offsetParentEl.clientLeft;
37     }
38
39     elPosition.top -= parentOffset.top;
40     elPosition.bottom -= parentOffset.top;
41     elPosition.left -= parentOffset.left;
42     elPosition.right -= parentOffset.left;
43
44     if (round) {
45       elPosition.top = Math.round(elPosition.top);
46       elPosition.bottom = Math.round(elPosition.bottom);
47       elPosition.left = Math.round(elPosition.left);
48       elPosition.right = Math.round(elPosition.right);
49     }
50
51     return elPosition;
52   }
53
54   public offset(element: HTMLElement, round = true): ClientRect {
55     const elBcr = element.getBoundingClientRect();
56     const viewportOffset = {
57       top: window.pageYOffset - document.documentElement.clientTop,
58       left: window.pageXOffset - document.documentElement.clientLeft
59     };
60
61     let elOffset = {
62       height: elBcr.height || element.offsetHeight,
63       width: elBcr.width || element.offsetWidth,
64       top: elBcr.top + viewportOffset.top,
65       bottom: elBcr.bottom + viewportOffset.top,
66       left: elBcr.left + viewportOffset.left,
67       right: elBcr.right + viewportOffset.left
68     };
69
70     if (round) {
71       elOffset.height = Math.round(elOffset.height);
72       elOffset.width = Math.round(elOffset.width);
73       elOffset.top = Math.round(elOffset.top);
74       elOffset.bottom = Math.round(elOffset.bottom);
75       elOffset.left = Math.round(elOffset.left);
76       elOffset.right = Math.round(elOffset.right);
77     }
78
79     return elOffset;
80   }
81
82   public positionElements(hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean):
83       ClientRect {
84     const hostElPosition = appendToBody ? this.offset(hostElement, false) : this.position(hostElement, false);
85     const shiftWidth: any = {
86       left: hostElPosition.left,
87       left2: (hostElPosition.left - 85),
88       center: hostElPosition.left + hostElPosition.width / 2 - targetElement.offsetWidth / 2,
89       right: hostElPosition.left + hostElPosition.width
90     };
91     const shiftHeight: any = {
92       top: hostElPosition.top,
93       center: hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2,
94       bottom: hostElPosition.top + hostElPosition.height
95     };
96     const targetElBCR = targetElement.getBoundingClientRect();
97     const placementPrimary = placement.split('-')[0] || 'top';
98     const placementSecondary = placement.split('-')[1] || 'center';
99
100     let targetElPosition: ClientRect = {
101       height: targetElBCR.height || targetElement.offsetHeight,
102       width: targetElBCR.width || targetElement.offsetWidth,
103       top: 0,
104       bottom: targetElBCR.height || targetElement.offsetHeight,
105       left: 0,
106       right: targetElBCR.width || targetElement.offsetWidth
107     };
108
109     switch (placementPrimary) {
110       case 'top':
111         targetElPosition.top = hostElPosition.top - targetElement.offsetHeight;
112         targetElPosition.bottom += hostElPosition.top - targetElement.offsetHeight;
113         targetElPosition.left = shiftWidth[placementSecondary];
114         targetElPosition.right += shiftWidth[placementSecondary];
115         break;
116       case 'bottom':
117         targetElPosition.top = shiftHeight[placementPrimary];
118         targetElPosition.bottom += shiftHeight[placementPrimary];
119         targetElPosition.left = shiftWidth[placementSecondary];
120         targetElPosition.right += shiftWidth[placementSecondary];
121         break;
122       case 'left':
123         targetElPosition.top = shiftHeight[placementSecondary];
124         targetElPosition.bottom += shiftHeight[placementSecondary];
125         targetElPosition.left = hostElPosition.left - targetElement.offsetWidth;
126         targetElPosition.right += hostElPosition.left - targetElement.offsetWidth;
127         break;
128       case 'right':
129         targetElPosition.top = shiftHeight[placementSecondary];
130         targetElPosition.bottom += shiftHeight[placementSecondary];
131         targetElPosition.left = shiftWidth[placementPrimary];
132         targetElPosition.right += shiftWidth[placementPrimary];
133         break;
134
135     }
136
137     targetElPosition.top = Math.round(targetElPosition.top);
138     targetElPosition.bottom = Math.round(targetElPosition.bottom);
139     targetElPosition.left = Math.round(targetElPosition.left);
140     targetElPosition.right = Math.round(targetElPosition.right);
141
142     return targetElPosition;
143   }
144 }
145
146 const positionService = new Positioning();
147 export function positionElements(
148     hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean): void {
149   const pos = positionService.positionElements(hostElement, targetElement, placement, appendToBody);
150
151   targetElement.style.top = `${pos.top}px`;
152   targetElement.style.left = `${pos.left}px`;
153 }