You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
14 KiB
JavaScript
267 lines
14 KiB
JavaScript
// previous version:
|
|
// https://github.com/angular-ui/bootstrap/blob/07c31d0731f7cb068a1932b8e01d2312b796b4ec/src/position/position.js
|
|
var Positioning = (function () {
|
|
function Positioning() {
|
|
}
|
|
Positioning.prototype.getAllStyles = function (element) { return window.getComputedStyle(element); };
|
|
Positioning.prototype.getStyle = function (element, prop) { return this.getAllStyles(element)[prop]; };
|
|
Positioning.prototype.isStaticPositioned = function (element) {
|
|
return (this.getStyle(element, 'position') || 'static') === 'static';
|
|
};
|
|
Positioning.prototype.offsetParent = function (element) {
|
|
var offsetParentEl = element.offsetParent || document.documentElement;
|
|
while (offsetParentEl && offsetParentEl !== document.documentElement && this.isStaticPositioned(offsetParentEl)) {
|
|
offsetParentEl = offsetParentEl.offsetParent;
|
|
}
|
|
return offsetParentEl || document.documentElement;
|
|
};
|
|
Positioning.prototype.position = function (element, round) {
|
|
if (round === void 0) { round = true; }
|
|
var elPosition;
|
|
var parentOffset = { width: 0, height: 0, top: 0, bottom: 0, left: 0, right: 0 };
|
|
if (this.getStyle(element, 'position') === 'fixed') {
|
|
elPosition = element.getBoundingClientRect();
|
|
}
|
|
else {
|
|
var 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;
|
|
};
|
|
Positioning.prototype.offset = function (element, round) {
|
|
if (round === void 0) { round = true; }
|
|
var elBcr = element.getBoundingClientRect();
|
|
var viewportOffset = {
|
|
top: window.pageYOffset - document.documentElement.clientTop,
|
|
left: window.pageXOffset - document.documentElement.clientLeft
|
|
};
|
|
var 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;
|
|
};
|
|
Positioning.prototype.positionElements = function (hostElement, targetElement, placement, appendToBody) {
|
|
var hostElPosition = appendToBody ? this.offset(hostElement, false) : this.position(hostElement, false);
|
|
var targetElStyles = this.getAllStyles(targetElement);
|
|
var targetElBCR = targetElement.getBoundingClientRect();
|
|
var placementPrimary = placement.split('-')[0] || 'top';
|
|
var placementSecondary = placement.split('-')[1] || 'center';
|
|
var 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
|
|
};
|
|
switch (placementPrimary) {
|
|
case 'top':
|
|
targetElPosition.top =
|
|
hostElPosition.top - (targetElement.offsetHeight + parseFloat(targetElStyles.marginBottom));
|
|
break;
|
|
case 'bottom':
|
|
targetElPosition.top = hostElPosition.top + hostElPosition.height;
|
|
break;
|
|
case 'left':
|
|
targetElPosition.left =
|
|
hostElPosition.left - (targetElement.offsetWidth + parseFloat(targetElStyles.marginRight));
|
|
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;
|
|
};
|
|
// get the availble placements of the target element in the viewport dependeing on the host element
|
|
Positioning.prototype.getAvailablePlacements = function (hostElement, targetElement) {
|
|
var availablePlacements = [];
|
|
var hostElemClientRect = hostElement.getBoundingClientRect();
|
|
var targetElemClientRect = targetElement.getBoundingClientRect();
|
|
var html = document.documentElement;
|
|
var windowHeight = window.innerHeight || html.clientHeight;
|
|
var windowWidth = window.innerWidth || html.clientWidth;
|
|
var hostElemClientRectHorCenter = hostElemClientRect.left + hostElemClientRect.width / 2;
|
|
var hostElemClientRectVerCenter = hostElemClientRect.top + hostElemClientRect.height / 2;
|
|
// left: check if target width can be placed between host left and viewport start and also height of target is
|
|
// inside viewport
|
|
if (targetElemClientRect.width < hostElemClientRect.left) {
|
|
// check for left only
|
|
if (hostElemClientRectVerCenter > targetElemClientRect.height / 2 &&
|
|
windowHeight - hostElemClientRectVerCenter > targetElemClientRect.height / 2) {
|
|
availablePlacements.splice(availablePlacements.length, 1, 'left');
|
|
}
|
|
// check for left-top and left-bottom
|
|
this.setSecondaryPlacementForLeftRight(hostElemClientRect, targetElemClientRect, 'left', availablePlacements);
|
|
}
|
|
// top: target height is less than host top
|
|
if (targetElemClientRect.height < hostElemClientRect.top) {
|
|
if (hostElemClientRectHorCenter > targetElemClientRect.width / 2 &&
|
|
windowWidth - hostElemClientRectHorCenter > targetElemClientRect.width / 2) {
|
|
availablePlacements.splice(availablePlacements.length, 1, 'top');
|
|
}
|
|
this.setSecondaryPlacementForTopBottom(hostElemClientRect, targetElemClientRect, 'top', availablePlacements);
|
|
}
|
|
// right: check if target width can be placed between host right and viewport end and also height of target is
|
|
// inside viewport
|
|
if (windowWidth - hostElemClientRect.right > targetElemClientRect.width) {
|
|
// check for right only
|
|
if (hostElemClientRectVerCenter > targetElemClientRect.height / 2 &&
|
|
windowHeight - hostElemClientRectVerCenter > targetElemClientRect.height / 2) {
|
|
availablePlacements.splice(availablePlacements.length, 1, 'right');
|
|
}
|
|
// check for right-top and right-bottom
|
|
this.setSecondaryPlacementForLeftRight(hostElemClientRect, targetElemClientRect, 'right', availablePlacements);
|
|
}
|
|
// bottom: check if there is enough space between host bottom and viewport end for target height
|
|
if (windowHeight - hostElemClientRect.bottom > targetElemClientRect.height) {
|
|
if (hostElemClientRectHorCenter > targetElemClientRect.width / 2 &&
|
|
windowWidth - hostElemClientRectHorCenter > targetElemClientRect.width / 2) {
|
|
availablePlacements.splice(availablePlacements.length, 1, 'bottom');
|
|
}
|
|
this.setSecondaryPlacementForTopBottom(hostElemClientRect, targetElemClientRect, 'bottom', availablePlacements);
|
|
}
|
|
return availablePlacements;
|
|
};
|
|
/**
|
|
* check if secondary placement for left and right are available i.e. left-top, left-bottom, right-top, right-bottom
|
|
* primaryplacement: left|right
|
|
* availablePlacementArr: array in which available placemets to be set
|
|
*/
|
|
Positioning.prototype.setSecondaryPlacementForLeftRight = function (hostElemClientRect, targetElemClientRect, primaryPlacement, availablePlacementArr) {
|
|
var html = document.documentElement;
|
|
// check for left-bottom
|
|
if (targetElemClientRect.height <= hostElemClientRect.bottom) {
|
|
availablePlacementArr.splice(availablePlacementArr.length, 1, primaryPlacement + '-bottom');
|
|
}
|
|
if ((window.innerHeight || html.clientHeight) - hostElemClientRect.top >= targetElemClientRect.height) {
|
|
availablePlacementArr.splice(availablePlacementArr.length, 1, primaryPlacement + '-top');
|
|
}
|
|
};
|
|
/**
|
|
* check if secondary placement for top and bottom are available i.e. top-left, top-right, bottom-left, bottom-right
|
|
* primaryplacement: top|bottom
|
|
* availablePlacementArr: array in which available placemets to be set
|
|
*/
|
|
Positioning.prototype.setSecondaryPlacementForTopBottom = function (hostElemClientRect, targetElemClientRect, primaryPlacement, availablePlacementArr) {
|
|
var html = document.documentElement;
|
|
// check for left-bottom
|
|
if ((window.innerWidth || html.clientWidth) - hostElemClientRect.left >= targetElemClientRect.width) {
|
|
availablePlacementArr.splice(availablePlacementArr.length, 1, primaryPlacement + '-left');
|
|
}
|
|
if (targetElemClientRect.width <= hostElemClientRect.right) {
|
|
availablePlacementArr.splice(availablePlacementArr.length, 1, primaryPlacement + '-right');
|
|
}
|
|
};
|
|
return Positioning;
|
|
}());
|
|
export { Positioning };
|
|
var positionService = new Positioning();
|
|
/*
|
|
* Accept the placement array and applies the appropriate placement dependent on the viewport.
|
|
* Returns the applied placement.
|
|
* In case of auto placement, placements are selected in order
|
|
* 'top', 'bottom', 'left', 'right',
|
|
* 'top-left', 'top-right',
|
|
* 'bottom-left', 'bottom-right',
|
|
* 'left-top', 'left-bottom',
|
|
* 'right-top', 'right-bottom'.
|
|
* */
|
|
export function positionElements(hostElement, targetElement, placement, appendToBody) {
|
|
var placementVals = Array.isArray(placement) ? placement : [placement];
|
|
// replace auto placement with other placements
|
|
var hasAuto = placementVals.findIndex(function (val) { return val === 'auto'; });
|
|
if (hasAuto >= 0) {
|
|
['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'left-top',
|
|
'left-bottom', 'right-top', 'right-bottom',
|
|
].forEach(function (obj) {
|
|
if (placementVals.find(function (val) { return val.search('^' + obj) !== -1; }) == null) {
|
|
placementVals.splice(hasAuto++, 1, obj);
|
|
}
|
|
});
|
|
}
|
|
// coordinates where to position
|
|
var topVal = 0, leftVal = 0;
|
|
var appliedPlacement;
|
|
// get available placements
|
|
var availablePlacements = positionService.getAvailablePlacements(hostElement, targetElement);
|
|
var _loop_1 = function (item, index) {
|
|
// check if passed placement is present in the available placement or otherwise apply the last placement in the
|
|
// passed placement list
|
|
if ((availablePlacements.find(function (val) { return val === item; }) != null) || (placementVals.length === index + 1)) {
|
|
appliedPlacement = item;
|
|
var pos = positionService.positionElements(hostElement, targetElement, item, appendToBody);
|
|
topVal = pos.top;
|
|
leftVal = pos.left;
|
|
return "break";
|
|
}
|
|
};
|
|
// iterate over all the passed placements
|
|
for (var _i = 0, _a = toItemIndexes(placementVals); _i < _a.length; _i++) {
|
|
var _b = _a[_i], item = _b.item, index = _b.index;
|
|
var state_1 = _loop_1(item, index);
|
|
if (state_1 === "break")
|
|
break;
|
|
}
|
|
targetElement.style.top = topVal + "px";
|
|
targetElement.style.left = leftVal + "px";
|
|
return appliedPlacement;
|
|
}
|
|
// function to get index and item of an array
|
|
function toItemIndexes(a) {
|
|
return a.map(function (item, index) { return ({ item: item, index: index }); });
|
|
}
|
|
//# sourceMappingURL=positioning.js.map
|