152 lines
5.5 KiB
TypeScript
152 lines
5.5 KiB
TypeScript
// previous version:
|
|
// https://github.com/angular-ui/bootstrap/blob/07c31d0731f7cb068a1932b8e01d2312b796b4ec/src/position/position.js
|
|
export class Positioning {
|
|
private getStyle(element: HTMLElement, prop: string): string { return window.getComputedStyle(element)[prop]; }
|
|
|
|
private isStaticPositioned(element: HTMLElement): boolean {
|
|
return (this.getStyle(element, 'position') || 'static') === 'static';
|
|
}
|
|
|
|
private offsetParent(element: HTMLElement): HTMLElement {
|
|
let offsetParentEl = <HTMLElement>element.offsetParent || document.documentElement;
|
|
|
|
while (offsetParentEl && offsetParentEl !== document.documentElement && this.isStaticPositioned(offsetParentEl)) {
|
|
offsetParentEl = <HTMLElement>offsetParentEl.offsetParent;
|
|
}
|
|
|
|
return offsetParentEl || document.documentElement;
|
|
}
|
|
|
|
position(element: HTMLElement, round = true): ClientRect {
|
|
let elPosition: ClientRect;
|
|
let parentOffset: ClientRect = {width: 0, height: 0, top: 0, bottom: 0, left: 0, right: 0};
|
|
|
|
if (this.getStyle(element, 'position') === 'fixed') {
|
|
elPosition = element.getBoundingClientRect();
|
|
} else {
|
|
const offsetParentEl = this.offsetParent(element);
|
|
|
|
elPosition = this.offset(element, false);
|
|
|
|
if (offsetParentEl !== document.documentElement) {
|
|
parentOffset = this.offset(offsetParentEl, false);
|
|
}
|
|
|
|
parentOffset.top += offsetParentEl.clientTop;
|
|
parentOffset.left += offsetParentEl.clientLeft;
|
|
}
|
|
|
|
elPosition.top -= parentOffset.top;
|
|
elPosition.bottom -= parentOffset.top;
|
|
elPosition.left -= parentOffset.left;
|
|
elPosition.right -= parentOffset.left;
|
|
|
|
if (round) {
|
|
elPosition.top = Math.round(elPosition.top);
|
|
elPosition.bottom = Math.round(elPosition.bottom);
|
|
elPosition.left = Math.round(elPosition.left);
|
|
elPosition.right = Math.round(elPosition.right);
|
|
}
|
|
|
|
return elPosition;
|
|
}
|
|
|
|
offset(element: HTMLElement, round = true): ClientRect {
|
|
const elBcr = element.getBoundingClientRect();
|
|
const viewportOffset = {
|
|
top: window.pageYOffset - document.documentElement.clientTop,
|
|
left: window.pageXOffset - document.documentElement.clientLeft
|
|
};
|
|
|
|
let elOffset = {
|
|
height: elBcr.height || element.offsetHeight,
|
|
width: elBcr.width || element.offsetWidth,
|
|
top: elBcr.top + viewportOffset.top,
|
|
bottom: elBcr.bottom + viewportOffset.top,
|
|
left: elBcr.left + viewportOffset.left,
|
|
right: elBcr.right + viewportOffset.left
|
|
};
|
|
|
|
if (round) {
|
|
elOffset.height = Math.round(elOffset.height);
|
|
elOffset.width = Math.round(elOffset.width);
|
|
elOffset.top = Math.round(elOffset.top);
|
|
elOffset.bottom = Math.round(elOffset.bottom);
|
|
elOffset.left = Math.round(elOffset.left);
|
|
elOffset.right = Math.round(elOffset.right);
|
|
}
|
|
|
|
return elOffset;
|
|
}
|
|
|
|
positionElements(hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean):
|
|
ClientRect {
|
|
const hostElPosition = appendToBody ? this.offset(hostElement, false) : this.position(hostElement, false);
|
|
const targetElBCR = targetElement.getBoundingClientRect();
|
|
const placementPrimary = placement.split('-')[0] || 'top';
|
|
const placementSecondary = placement.split('-')[1] || 'center';
|
|
|
|
let targetElPosition: ClientRect = {
|
|
'height': targetElBCR.height || targetElement.offsetHeight,
|
|
'width': targetElBCR.width || targetElement.offsetWidth,
|
|
'top': 0,
|
|
'bottom': targetElBCR.height || targetElement.offsetHeight,
|
|
'left': 0,
|
|
'right': targetElBCR.width || targetElement.offsetWidth
|
|
};
|
|
|
|
switch (placementPrimary) {
|
|
case 'top':
|
|
targetElPosition.top = hostElPosition.top - targetElement.offsetHeight;
|
|
break;
|
|
case 'bottom':
|
|
targetElPosition.top = hostElPosition.top + hostElPosition.height;
|
|
break;
|
|
case 'left':
|
|
targetElPosition.left = hostElPosition.left - targetElement.offsetWidth;
|
|
break;
|
|
case 'right':
|
|
targetElPosition.left = hostElPosition.left + hostElPosition.width;
|
|
break;
|
|
}
|
|
|
|
switch (placementSecondary) {
|
|
case 'top':
|
|
targetElPosition.top = hostElPosition.top;
|
|
break;
|
|
case 'bottom':
|
|
targetElPosition.top = hostElPosition.top + hostElPosition.height - targetElement.offsetHeight;
|
|
break;
|
|
case 'left':
|
|
targetElPosition.left = hostElPosition.left;
|
|
break;
|
|
case 'right':
|
|
targetElPosition.left = hostElPosition.left + hostElPosition.width - targetElement.offsetWidth;
|
|
break;
|
|
case 'center':
|
|
if (placementPrimary === 'top' || placementPrimary === 'bottom') {
|
|
targetElPosition.left = hostElPosition.left + hostElPosition.width / 2 - targetElement.offsetWidth / 2;
|
|
} else {
|
|
targetElPosition.top = hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
targetElPosition.top = Math.round(targetElPosition.top);
|
|
targetElPosition.bottom = Math.round(targetElPosition.bottom);
|
|
targetElPosition.left = Math.round(targetElPosition.left);
|
|
targetElPosition.right = Math.round(targetElPosition.right);
|
|
|
|
return targetElPosition;
|
|
}
|
|
}
|
|
|
|
const positionService = new Positioning();
|
|
export function positionElements(
|
|
hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean): void {
|
|
const pos = positionService.positionElements(hostElement, targetElement, placement, appendToBody);
|
|
|
|
targetElement.style.top = `${pos.top}px`;
|
|
targetElement.style.left = `${pos.left}px`;
|
|
}
|