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]; }
6 private isStaticPositioned(element: HTMLElement): boolean {
7 return (this.getStyle(element, 'position') || 'static') === 'static';
10 private offsetParent(element: HTMLElement): HTMLElement {
11 let offsetParentEl = <HTMLElement>element.offsetParent || document.documentElement;
13 while (offsetParentEl && offsetParentEl !== document.documentElement && this.isStaticPositioned(offsetParentEl)) {
14 offsetParentEl = <HTMLElement>offsetParentEl.offsetParent;
17 return offsetParentEl || document.documentElement;
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};
24 if (this.getStyle(element, 'position') === 'fixed') {
25 elPosition = element.getBoundingClientRect();
27 const offsetParentEl = this.offsetParent(element);
29 elPosition = this.offset(element, false);
31 if (offsetParentEl !== document.documentElement) {
32 parentOffset = this.offset(offsetParentEl, false);
35 parentOffset.top += offsetParentEl.clientTop;
36 parentOffset.left += offsetParentEl.clientLeft;
39 elPosition.top -= parentOffset.top;
40 elPosition.bottom -= parentOffset.top;
41 elPosition.left -= parentOffset.left;
42 elPosition.right -= parentOffset.left;
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);
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
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
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);
82 public positionElements(hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean):
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
91 const shiftHeight: any = {
92 top: hostElPosition.top,
93 center: hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2,
94 bottom: hostElPosition.top + hostElPosition.height
96 const targetElBCR = targetElement.getBoundingClientRect();
97 const placementPrimary = placement.split('-')[0] || 'top';
98 const placementSecondary = placement.split('-')[1] || 'center';
100 let targetElPosition: ClientRect = {
101 height: targetElBCR.height || targetElement.offsetHeight,
102 width: targetElBCR.width || targetElement.offsetWidth,
104 bottom: targetElBCR.height || targetElement.offsetHeight,
106 right: targetElBCR.width || targetElement.offsetWidth
109 switch (placementPrimary) {
111 targetElPosition.top = hostElPosition.top - targetElement.offsetHeight;
112 targetElPosition.bottom += hostElPosition.top - targetElement.offsetHeight;
113 targetElPosition.left = shiftWidth[placementSecondary];
114 targetElPosition.right += shiftWidth[placementSecondary];
117 targetElPosition.top = shiftHeight[placementPrimary];
118 targetElPosition.bottom += shiftHeight[placementPrimary];
119 targetElPosition.left = shiftWidth[placementSecondary];
120 targetElPosition.right += shiftWidth[placementSecondary];
123 targetElPosition.top = shiftHeight[placementSecondary];
124 targetElPosition.bottom += shiftHeight[placementSecondary];
125 targetElPosition.left = hostElPosition.left - targetElement.offsetWidth;
126 targetElPosition.right += hostElPosition.left - targetElement.offsetWidth;
129 targetElPosition.top = shiftHeight[placementSecondary];
130 targetElPosition.bottom += shiftHeight[placementSecondary];
131 targetElPosition.left = shiftWidth[placementPrimary];
132 targetElPosition.right += shiftWidth[placementPrimary];
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);
142 return targetElPosition;
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);
151 targetElement.style.top = `${pos.top}px`;
152 targetElement.style.left = `${pos.left}px`;