ng-devui
Version:
DevUI components based on Angular
263 lines (258 loc) • 13.7 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, NgModule } from '@angular/core';
import * as i1 from 'ng-devui/window-ref';
import { WindowRefModule } from 'ng-devui/window-ref';
class PositionService {
constructor(documentRef, windowRef) {
this.documentRef = documentRef;
this.windowRef = windowRef;
}
position(element, round = true) {
let elPosition;
let parentOffset = { 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 !== this.documentRef.documentElement) {
parentOffset = this.offset(offsetParentEl, false);
}
parentOffset.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
parentOffset.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
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, round = true) {
const elBcr = element.getBoundingClientRect();
const viewportOffset = {
top: this.windowRef.pageYOffset - this.documentRef.documentElement.clientTop,
left: this.windowRef.pageXOffset - this.documentRef.documentElement.clientLeft,
};
const 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;
}
getScrollParent(element) {
let style = getComputedStyle(element);
const excludeStaticParent = style.position === 'absolute';
const overflowRegex = /(auto|scroll|overlay)/;
if (style.position === 'fixed') {
return this.documentRef.body;
}
for (let parent = element; (parent = parent.parentElement); parent.parentElement !== this.documentRef.body) {
style = getComputedStyle(parent);
if (excludeStaticParent && style.position === 'static') {
continue;
}
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
return parent;
}
}
return this.documentRef.body;
}
positionElements(hostElement, targetElement, placement, appendToBody) {
const hostElPosition = appendToBody ? this.offset(hostElement, false) : this.position(hostElement, false);
const shiftWidth = {
left: hostElPosition.left,
center: hostElPosition.left + hostElPosition.width / 2 - targetElement.offsetWidth / 2,
right: hostElPosition.left + hostElPosition.width,
};
const shiftHeight = {
top: hostElPosition.top,
center: hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2,
bottom: hostElPosition.top + hostElPosition.height,
};
const targetElBCR = targetElement.getBoundingClientRect();
const targetElPosition = {
height: targetElBCR.height || targetElement.offsetHeight,
width: targetElBCR.width || targetElement.offsetWidth,
top: 0,
bottom: targetElBCR.height || targetElement.offsetHeight,
left: 0,
right: targetElBCR.width || targetElement.offsetWidth,
};
let placementPrimary;
let placementSecondary;
if (Array.isArray(placement)) {
const targetPlacement = this.getPlacement(hostElement, targetElement, placement);
placementPrimary = targetPlacement[0];
placementSecondary = targetPlacement[1];
}
else {
placementPrimary = placement?.split('-')[0] || 'top';
placementSecondary = placement?.split('-')[1] || 'center';
}
switch (placementPrimary) {
case 'top':
targetElPosition.top = hostElPosition.top - targetElement.offsetHeight;
targetElPosition.bottom += hostElPosition.top - targetElement.offsetHeight;
targetElPosition.left =
placementSecondary === 'right' ? shiftWidth[placementSecondary] - targetElement.offsetWidth : shiftWidth[placementSecondary];
targetElPosition.right += shiftWidth[placementSecondary];
break;
case 'bottom':
targetElPosition.top = shiftHeight[placementPrimary];
targetElPosition.bottom += shiftHeight[placementPrimary];
targetElPosition.left =
placementSecondary === 'right' ? shiftWidth[placementSecondary] - targetElement.offsetWidth : shiftWidth[placementSecondary];
targetElPosition.right += shiftWidth[placementSecondary];
break;
case 'left':
targetElPosition.top =
placementSecondary === 'bottom' ? shiftHeight[placementSecondary] - targetElement.offsetHeight : shiftHeight[placementSecondary];
targetElPosition.bottom += shiftHeight[placementSecondary];
targetElPosition.left = hostElPosition.left - targetElement.offsetWidth;
targetElPosition.right += hostElPosition.left - targetElement.offsetWidth;
break;
case 'right':
targetElPosition.top =
placementSecondary === 'bottom' ? shiftHeight[placementSecondary] - targetElement.offsetHeight : shiftHeight[placementSecondary];
targetElPosition.bottom += shiftHeight[placementSecondary];
targetElPosition.left = shiftWidth[placementPrimary];
targetElPosition.right += shiftWidth[placementPrimary];
break;
default:
}
targetElPosition.top = Math.round(targetElPosition.top);
targetElPosition.bottom = Math.round(targetElPosition.bottom);
targetElPosition.left = Math.round(targetElPosition.left);
targetElPosition.right = Math.round(targetElPosition.right);
targetElPosition.placementPrimary = placementPrimary;
targetElPosition.placementSecondary = placementSecondary;
return targetElPosition;
}
// 根据传入数组选取第一个合适的位置
getPlacement(hostElement, targetElement, placement) {
const hostElPosition = this.offset(hostElement, false);
const shiftWidth = {
left: hostElPosition.left,
center: hostElPosition.left + hostElPosition.width / 2 - targetElement.offsetWidth / 2,
right: hostElPosition.left + hostElPosition.width,
};
const shiftHeight = {
top: hostElPosition.top,
center: hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2,
bottom: hostElPosition.top + hostElPosition.height,
};
let placementPrimary = placement[0].split('-')[0] || 'top';
let placementSecondary = placement[0].split('-')[1] || 'center';
for (let i = 0; i < placement.length; i++) {
const placementPrimaryTemp = placement[i].split('-')[0] || 'top';
const placementSecondaryTemp = placement[i].split('-')[1] || 'center';
let top;
let left;
switch (placementPrimaryTemp) {
case 'top':
top = hostElPosition.top - targetElement.offsetHeight;
left =
placementSecondaryTemp === 'right'
? shiftWidth[placementSecondaryTemp] - targetElement.offsetWidth
: shiftWidth[placementSecondary];
break;
case 'bottom':
top = shiftHeight[placementPrimaryTemp];
left =
placementSecondaryTemp === 'right'
? shiftWidth[placementSecondaryTemp] - targetElement.offsetWidth
: shiftWidth[placementSecondary];
break;
case 'left':
top =
placementSecondaryTemp === 'bottom'
? shiftHeight[placementSecondaryTemp] - targetElement.offsetHeight
: shiftHeight[placementSecondary];
left = hostElPosition.left - targetElement.offsetWidth;
break;
case 'right':
top =
placementSecondaryTemp === 'bottom'
? shiftHeight[placementSecondaryTemp] - targetElement.offsetHeight
: shiftHeight[placementSecondary];
left = shiftWidth[placementPrimaryTemp];
break;
default:
}
if (this.isInViewPort(targetElement, { offsetLeft: left, offsetTop: top })) {
placementPrimary = placement[i].split('-')[0] || 'top';
placementSecondary = placement[i].split('-')[1] || 'center';
return [placementPrimary, placementSecondary];
}
}
return [placementPrimary, placementSecondary];
}
isInViewPort(ele, { offsetLeft, offsetTop }) {
const targetElBCR = ele.getBoundingClientRect();
const viewPortHeight = this.windowRef.innerHeight || this.documentRef.documentElement.clientHeight || this.documentRef.body.clientHeight;
const viewPortWidth = this.windowRef.innerWidth || this.documentRef.documentElement.clientWidth || this.documentRef.body.clientWidth;
const height = targetElBCR.height || targetElBCR.offsetHeight;
const width = targetElBCR.width || targetElBCR.offsetWidth;
offsetTop = offsetTop || ele.offsetTop;
const scrollTop = this.documentRef.documentElement.scrollTop || this.documentRef.body.scrollTop;
const top = offsetTop - scrollTop;
offsetLeft = offsetLeft || ele.offsetLeft;
const scrollLeft = this.documentRef.documentElement.scrollLeft || this.documentRef.body.scrollLeft;
const left = offsetLeft - scrollLeft;
return top + height <= viewPortHeight && top > 0 && left + width <= viewPortWidth && left > 0;
}
getStyle(element, prop) {
return this.windowRef.getComputedStyle(element)[prop];
}
isStaticPositioned(element) {
return (this.getStyle(element, 'position') || 'static') === 'static';
}
offsetParent(element) {
let offsetParentEl = element.offsetParent || this.documentRef.documentElement;
while (offsetParentEl && offsetParentEl !== this.documentRef.documentElement && this.isStaticPositioned(offsetParentEl)) {
offsetParentEl = offsetParentEl.offsetParent;
}
return offsetParentEl || this.documentRef.documentElement;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PositionService, deps: [{ token: i1.DocumentRef }, { token: i1.WindowRef }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PositionService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PositionService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1.DocumentRef }, { type: i1.WindowRef }] });
class PositioningModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PositioningModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: PositioningModule, imports: [WindowRefModule] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PositioningModule, providers: [PositionService], imports: [WindowRefModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PositioningModule, decorators: [{
type: NgModule,
args: [{
imports: [WindowRefModule],
providers: [PositionService]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { PositionService, PositioningModule };
//# sourceMappingURL=ng-devui-position.mjs.map