office-ui-fabric-react
Version:
Reusable React components for building experiences for Microsoft 365.
669 lines • 33.9 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var DirectionalHint_1 = require("../../common/DirectionalHint");
var Utilities_1 = require("../../Utilities");
var positioning_types_1 = require("./positioning.types");
var Utilities_2 = require("../../Utilities");
function _createPositionData(targetEdge, alignmentEdge, isAuto) {
return {
targetEdge: targetEdge,
alignmentEdge: alignmentEdge,
isAuto: isAuto,
};
}
// Currently the beakPercent is set to 50 for all positions meaning that it should tend to the center of the target
var DirectionalDictionary = (_a = {},
_a[DirectionalHint_1.DirectionalHint.topLeftEdge] = _createPositionData(positioning_types_1.RectangleEdge.top, positioning_types_1.RectangleEdge.left),
_a[DirectionalHint_1.DirectionalHint.topCenter] = _createPositionData(positioning_types_1.RectangleEdge.top),
_a[DirectionalHint_1.DirectionalHint.topRightEdge] = _createPositionData(positioning_types_1.RectangleEdge.top, positioning_types_1.RectangleEdge.right),
_a[DirectionalHint_1.DirectionalHint.topAutoEdge] = _createPositionData(positioning_types_1.RectangleEdge.top, undefined, true),
_a[DirectionalHint_1.DirectionalHint.bottomLeftEdge] = _createPositionData(positioning_types_1.RectangleEdge.bottom, positioning_types_1.RectangleEdge.left),
_a[DirectionalHint_1.DirectionalHint.bottomCenter] = _createPositionData(positioning_types_1.RectangleEdge.bottom),
_a[DirectionalHint_1.DirectionalHint.bottomRightEdge] = _createPositionData(positioning_types_1.RectangleEdge.bottom, positioning_types_1.RectangleEdge.right),
_a[DirectionalHint_1.DirectionalHint.bottomAutoEdge] = _createPositionData(positioning_types_1.RectangleEdge.bottom, undefined, true),
_a[DirectionalHint_1.DirectionalHint.leftTopEdge] = _createPositionData(positioning_types_1.RectangleEdge.left, positioning_types_1.RectangleEdge.top),
_a[DirectionalHint_1.DirectionalHint.leftCenter] = _createPositionData(positioning_types_1.RectangleEdge.left),
_a[DirectionalHint_1.DirectionalHint.leftBottomEdge] = _createPositionData(positioning_types_1.RectangleEdge.left, positioning_types_1.RectangleEdge.bottom),
_a[DirectionalHint_1.DirectionalHint.rightTopEdge] = _createPositionData(positioning_types_1.RectangleEdge.right, positioning_types_1.RectangleEdge.top),
_a[DirectionalHint_1.DirectionalHint.rightCenter] = _createPositionData(positioning_types_1.RectangleEdge.right),
_a[DirectionalHint_1.DirectionalHint.rightBottomEdge] = _createPositionData(positioning_types_1.RectangleEdge.right, positioning_types_1.RectangleEdge.bottom),
_a);
function _isRectangleWithinBounds(rect, boundingRect) {
if (rect.top < boundingRect.top) {
return false;
}
if (rect.bottom > boundingRect.bottom) {
return false;
}
if (rect.left < boundingRect.left) {
return false;
}
if (rect.right > boundingRect.right) {
return false;
}
return true;
}
/**
* Gets all of the edges of a rectangle that are outside of the given bounds.
* If there are no out of bounds edges it returns an empty array.
*/
function _getOutOfBoundsEdges(rect, boundingRect) {
var outOfBounds = [];
if (rect.top < boundingRect.top) {
outOfBounds.push(positioning_types_1.RectangleEdge.top);
}
if (rect.bottom > boundingRect.bottom) {
outOfBounds.push(positioning_types_1.RectangleEdge.bottom);
}
if (rect.left < boundingRect.left) {
outOfBounds.push(positioning_types_1.RectangleEdge.left);
}
if (rect.right > boundingRect.right) {
outOfBounds.push(positioning_types_1.RectangleEdge.right);
}
return outOfBounds;
}
function _getEdgeValue(rect, edge) {
return rect[positioning_types_1.RectangleEdge[edge]];
}
function _setEdgeValue(rect, edge, value) {
rect[positioning_types_1.RectangleEdge[edge]] = value;
return rect;
}
/**
* Returns the middle value of an edge. Only returns 1 value rather than xy coordinates as
* the itself already contains the other coordinate.
* For instance, a bottom edge's current value is it's y coordinate, so the number returned is the x.
*/
function _getCenterValue(rect, edge) {
var edges = _getFlankingEdges(edge);
return (_getEdgeValue(rect, edges.positiveEdge) + _getEdgeValue(rect, edges.negativeEdge)) / 2;
}
/**
* Flips the value depending on the edge.
* If the edge is a "positive" edge, Top or Left, then the value should stay as it is.
* If the edge is a "negative" edge, Bottom or Right, then the value should be flipped.
* This is to account for the fact that the coordinates are effectively reveserved in certain cases for the
* "negative" edges.
*
* For example, when testing to see if a bottom edge 1 is within the bounds of another bottom edge 2:
* If edge 1 is greater than edge 2 then it is out of bounds. This is reversed for top edge 1 and top edge 2.
* If top edge 1 is less than edge 2 then it is out of bounds.
*/
function _getRelativeEdgeValue(edge, value) {
if (edge > 0) {
return value;
}
else {
return value * -1;
}
}
function _getRelativeRectEdgeValue(edge, rect) {
return _getRelativeEdgeValue(edge, _getEdgeValue(rect, edge));
}
function _getRelativeEdgeDifference(rect, hostRect, edge) {
var edgeDifference = _getEdgeValue(rect, edge) - _getEdgeValue(hostRect, edge);
return _getRelativeEdgeValue(edge, edgeDifference);
}
/**
* Moves the edge of a rectangle to the value given. It only moves the edge in a linear direction based on that edge.
* For example, if it's a bottom edge it will only change y coordinates.
*/
function _moveEdge(rect, edge, newValue) {
var difference = _getEdgeValue(rect, edge) - newValue;
rect = _setEdgeValue(rect, edge, newValue);
rect = _setEdgeValue(rect, edge * -1, _getEdgeValue(rect, edge * -1) - difference);
return rect;
}
/**
* Aligns the edge on the passed in rect to the target. If there is a gap then it will have that space between the two.
*/
function _alignEdges(rect, target, edge, gap) {
if (gap === void 0) { gap = 0; }
return _moveEdge(rect, edge, _getEdgeValue(target, edge) + _getRelativeEdgeValue(edge, gap));
}
/**
* Aligns the targetEdge on the passed in target to the rects corresponding opposite edge.
* For instance if targetEdge is bottom, then the rects top will be moved to match it.
*/
function _alignOppositeEdges(rect, target, targetEdge, gap) {
if (gap === void 0) { gap = 0; }
var oppositeEdge = targetEdge * -1;
var adjustedGap = _getRelativeEdgeValue(oppositeEdge, gap);
return _moveEdge(rect, targetEdge * -1, _getEdgeValue(target, targetEdge) + adjustedGap);
}
/**
* Tests to see if the given edge is within the bounds of the given rectangle.
*/
function _isEdgeInBounds(rect, bounds, edge) {
var adjustedRectValue = _getRelativeRectEdgeValue(edge, rect);
return adjustedRectValue > _getRelativeRectEdgeValue(edge, bounds);
}
/**
* Attempts to move the rectangle through various sides of the target to find a place to fit.
* If no fit is found, the original position should be returned.
*/
function _flipToFit(rect, target, bounding, positionData, gap) {
if (gap === void 0) { gap = 0; }
var directions = [
positioning_types_1.RectangleEdge.left,
positioning_types_1.RectangleEdge.right,
positioning_types_1.RectangleEdge.bottom,
positioning_types_1.RectangleEdge.top,
];
// In RTL page, RectangleEdge.right has a higher priority than RectangleEdge.left, so the order should be updated.
if (Utilities_1.getRTL()) {
directions[0] *= -1;
directions[1] *= -1;
}
var currentEstimate = rect;
var currentEdge = positionData.targetEdge;
var currentAlignment = positionData.alignmentEdge;
// Keep switching sides until one is found with enough space.
// If all sides don't fit then return the unmodified element.
for (var i = 0; i < 4; i++) {
if (!_isEdgeInBounds(currentEstimate, bounding, currentEdge)) {
directions.splice(directions.indexOf(currentEdge), 1);
if (directions.length > 0) {
if (directions.indexOf(currentEdge * -1) > -1) {
currentEdge = currentEdge * -1;
}
else {
currentAlignment = currentEdge;
currentEdge = directions.slice(-1)[0];
}
currentEstimate = _estimatePosition(rect, target, { targetEdge: currentEdge, alignmentEdge: currentAlignment }, gap);
}
}
else {
return {
elementRectangle: currentEstimate,
targetEdge: currentEdge,
alignmentEdge: currentAlignment,
};
}
}
return {
elementRectangle: rect,
targetEdge: positionData.targetEdge,
alignmentEdge: positionData.alignmentEdge,
};
}
/**
* Flips only the alignment edge of an element rectangle. This is used instead of nudging the alignment edges
* into position, when alignTargetEdge is specified.
*/
function _flipAlignmentEdge(elementEstimate, target, gap, coverTarget) {
var alignmentEdge = elementEstimate.alignmentEdge, targetEdge = elementEstimate.targetEdge, elementRectangle = elementEstimate.elementRectangle;
var oppositeEdge = alignmentEdge * -1;
var newEstimate = _estimatePosition(elementRectangle, target, { targetEdge: targetEdge, alignmentEdge: oppositeEdge }, gap, coverTarget);
return {
elementRectangle: newEstimate,
targetEdge: targetEdge,
alignmentEdge: oppositeEdge,
};
}
/**
* Adjusts a element rectangle to fit within the bounds given. If directionalHintFixed or covertarget is passed in
* then the element will not flip sides on the target. They will, however, be nudged to fit within the bounds given.
*/
function _adjustFitWithinBounds(element, target, bounding, positionData, gap, directionalHintFixed, coverTarget) {
if (gap === void 0) { gap = 0; }
var alignmentEdge = positionData.alignmentEdge, alignTargetEdge = positionData.alignTargetEdge;
var elementEstimate = {
elementRectangle: element,
targetEdge: positionData.targetEdge,
alignmentEdge: alignmentEdge,
};
if (!directionalHintFixed && !coverTarget) {
elementEstimate = _flipToFit(element, target, bounding, positionData, gap);
}
var outOfBounds = _getOutOfBoundsEdges(element, bounding);
if (alignTargetEdge) {
// The edge opposite to the alignment edge might be out of bounds.
// Flip alignment to see if we can get it within bounds.
if (elementEstimate.alignmentEdge && outOfBounds.indexOf(elementEstimate.alignmentEdge * -1) > -1) {
var flippedElementEstimate = _flipAlignmentEdge(elementEstimate, target, gap, coverTarget);
if (_isRectangleWithinBounds(flippedElementEstimate.elementRectangle, bounding)) {
return flippedElementEstimate;
}
else {
// If the flipped elements edges are still out of bounds, try nudging it.
elementEstimate = _alignOutOfBoundsEdges(_getOutOfBoundsEdges(flippedElementEstimate.elementRectangle, bounding), elementEstimate, bounding);
}
}
}
else {
elementEstimate = _alignOutOfBoundsEdges(outOfBounds, elementEstimate, bounding);
}
return elementEstimate;
}
/**
* Iterates through a list of out of bounds edges and tries to nudge and align them.
* @param outOfBoundsEdges - Array of edges that are out of bounds
* @param elementEstimate - The current element positioning estimate
* @param bounding - The current bounds
*/
function _alignOutOfBoundsEdges(outOfBoundsEdges, elementEstimate, bounding) {
for (var _i = 0, outOfBoundsEdges_1 = outOfBoundsEdges; _i < outOfBoundsEdges_1.length; _i++) {
var direction = outOfBoundsEdges_1[_i];
elementEstimate.elementRectangle = _alignEdges(elementEstimate.elementRectangle, bounding, direction);
}
return elementEstimate;
}
/**
* Moves the middle point on an edge to the point given.
* Only moves in one direction. For instance if a bottom edge is passed in, then
* the bottom edge will be moved in the x axis to match the point.
*/
function _centerEdgeToPoint(rect, edge, point) {
var positiveEdge = _getFlankingEdges(edge).positiveEdge;
var elementMiddle = _getCenterValue(rect, edge);
var distanceToMiddle = elementMiddle - _getEdgeValue(rect, positiveEdge);
return _moveEdge(rect, positiveEdge, point - distanceToMiddle);
}
/**
* Moves the element rectangle to be appropriately positioned relative to a given target.
* Does not flip or adjust the element.
*/
function _estimatePosition(elementToPosition, target, positionData, gap, coverTarget) {
if (gap === void 0) { gap = 0; }
var estimatedElementPosition;
var alignmentEdge = positionData.alignmentEdge, targetEdge = positionData.targetEdge;
var elementEdge = coverTarget ? targetEdge : targetEdge * -1;
estimatedElementPosition = coverTarget
? _alignEdges(elementToPosition, target, targetEdge, gap)
: _alignOppositeEdges(elementToPosition, target, targetEdge, gap);
// if no alignment edge is provided it's supposed to be centered.
if (!alignmentEdge) {
var targetMiddlePoint = _getCenterValue(target, targetEdge);
estimatedElementPosition = _centerEdgeToPoint(estimatedElementPosition, elementEdge, targetMiddlePoint);
}
else {
estimatedElementPosition = _alignEdges(estimatedElementPosition, target, alignmentEdge);
}
return estimatedElementPosition;
}
/**
* Returns the non-opposite edges of the target edge.
* For instance if bottom is passed in then left and right will be returned.
*/
function _getFlankingEdges(edge) {
if (edge === positioning_types_1.RectangleEdge.top || edge === positioning_types_1.RectangleEdge.bottom) {
return {
positiveEdge: positioning_types_1.RectangleEdge.left,
negativeEdge: positioning_types_1.RectangleEdge.right,
};
}
else {
return {
positiveEdge: positioning_types_1.RectangleEdge.top,
negativeEdge: positioning_types_1.RectangleEdge.bottom,
};
}
}
/**
* Retrieve the final value for the return edge of elementRectangle. If the elementRectangle is closer to one side
* of the bounds versus the other, the return edge is flipped to grow inward.
*/
function _finalizeReturnEdge(elementRectangle, returnEdge, bounds) {
if (bounds &&
Math.abs(_getRelativeEdgeDifference(elementRectangle, bounds, returnEdge)) >
Math.abs(_getRelativeEdgeDifference(elementRectangle, bounds, returnEdge * -1))) {
return returnEdge * -1;
}
return returnEdge;
}
/**
* Finalizes the element positon based on the hostElement. Only returns the
* rectangle values to position such that they are anchored to the target.
* This helps prevent resizing from looking very strange.
* For instance, if the target edge is top and aligned with the left side then
* the bottom and left values are returned so as the callou shrinks it shrinks towards that corner.
*/
function _finalizeElementPosition(elementRectangle, hostElement, targetEdge, bounds, alignmentEdge, coverTarget, doNotFinalizeReturnEdge) {
var returnValue = {};
var hostRect = _getRectangleFromElement(hostElement);
var elementEdge = coverTarget ? targetEdge : targetEdge * -1;
var elementEdgeString = positioning_types_1.RectangleEdge[elementEdge];
var returnEdge = alignmentEdge ? alignmentEdge : _getFlankingEdges(targetEdge).positiveEdge;
if (!doNotFinalizeReturnEdge) {
returnEdge = _finalizeReturnEdge(elementRectangle, returnEdge, bounds);
}
returnValue[elementEdgeString] = _getRelativeEdgeDifference(elementRectangle, hostRect, elementEdge);
returnValue[positioning_types_1.RectangleEdge[returnEdge]] = _getRelativeEdgeDifference(elementRectangle, hostRect, returnEdge);
return returnValue;
}
// Since the beak is rotated 45 degrees the actual height/width is the length of the diagonal.
// We still want to position the beak based on it's midpoint which does not change. It will
// be at (beakwidth / 2, beakwidth / 2)
function _calculateActualBeakWidthInPixels(beakWidth) {
return Math.sqrt(beakWidth * beakWidth * 2);
}
/**
* Returns the appropriate IPositionData based on the props altered for RTL.
* If directionalHintForRTL is passed in that is used if the page is RTL.
* If directionalHint is specified, no directionalHintForRTL is available, and the page is RTL, the hint will be
* flipped (e.g. bottomLeftEdge would become bottomRightEdge).
*
* If there is no directionalHint passed in, bottomAutoEdge is chosen automatically.
*/
function _getPositionData(directionalHint, directionalHintForRTL, previousPositions) {
if (directionalHint === void 0) { directionalHint = DirectionalHint_1.DirectionalHint.bottomAutoEdge; }
if (previousPositions) {
return {
alignmentEdge: previousPositions.alignmentEdge,
isAuto: previousPositions.isAuto,
targetEdge: previousPositions.targetEdge,
};
}
var positionInformation = tslib_1.__assign({}, DirectionalDictionary[directionalHint]);
if (Utilities_1.getRTL()) {
// If alignment edge exists and that alignment edge is -2 or 2, right or left, then flip it.
if (positionInformation.alignmentEdge && positionInformation.alignmentEdge % 2 === 0) {
positionInformation.alignmentEdge = positionInformation.alignmentEdge * -1;
}
return directionalHintForRTL !== undefined ? DirectionalDictionary[directionalHintForRTL] : positionInformation;
}
return positionInformation;
}
/**
* Get's the alignment data for the given information. This only really matters if the positioning is Auto.
* If it is auto then the alignmentEdge should be chosen based on the target edge's position relative to
* the center of the page.
*/
function _getAlignmentData(positionData, target, boundingRect, coverTarget, alignTargetEdge) {
if (positionData.isAuto) {
positionData.alignmentEdge = getClosestEdge(positionData.targetEdge, target, boundingRect);
}
positionData.alignTargetEdge = alignTargetEdge;
return positionData;
}
function getClosestEdge(targetEdge, target, boundingRect) {
var targetCenter = _getCenterValue(target, targetEdge);
var boundingCenter = _getCenterValue(boundingRect, targetEdge);
var _a = _getFlankingEdges(targetEdge), positiveEdge = _a.positiveEdge, negativeEdge = _a.negativeEdge;
if (targetCenter <= boundingCenter) {
return positiveEdge;
}
else {
return negativeEdge;
}
}
function _positionElementWithinBounds(elementToPosition, target, bounding, positionData, gap, directionalHintFixed, coverTarget) {
var estimatedElementPosition = _estimatePosition(elementToPosition, target, positionData, gap, coverTarget);
if (_isRectangleWithinBounds(estimatedElementPosition, bounding)) {
return {
elementRectangle: estimatedElementPosition,
targetEdge: positionData.targetEdge,
alignmentEdge: positionData.alignmentEdge,
};
}
else {
return _adjustFitWithinBounds(elementToPosition, target, bounding, positionData, gap, directionalHintFixed, coverTarget);
}
}
function _finalizeBeakPosition(elementPosition, positionedBeak, bounds) {
var targetEdge = elementPosition.targetEdge * -1;
// The "host" element that we will use to help position the beak.
var actualElement = new Utilities_2.Rectangle(0, elementPosition.elementRectangle.width, 0, elementPosition.elementRectangle.height);
var returnValue = {};
var returnEdge = _finalizeReturnEdge(elementPosition.elementRectangle, elementPosition.alignmentEdge ? elementPosition.alignmentEdge : _getFlankingEdges(targetEdge).positiveEdge, bounds);
returnValue[positioning_types_1.RectangleEdge[targetEdge]] = _getEdgeValue(positionedBeak, targetEdge);
returnValue[positioning_types_1.RectangleEdge[returnEdge]] = _getRelativeEdgeDifference(positionedBeak, actualElement, returnEdge);
return {
elementPosition: tslib_1.__assign({}, returnValue),
closestEdge: getClosestEdge(elementPosition.targetEdge, positionedBeak, actualElement),
targetEdge: targetEdge,
};
}
function _positionBeak(beakWidth, elementPosition) {
var target = elementPosition.targetRectangle;
/**
* Note about beak positioning: The actual beak width only matters for getting the gap between the callout and
* target, it does not impact the beak placement within the callout. For example example, if the beakWidth is 8,
* then the actual beakWidth is sqrroot(8^2 + 8^2) = 11.31x11.31. So the callout will need to be an extra 3 pixels
* away from its target. While the beak is being positioned in the callout it still acts as though it were 8x8.
*/
var _a = _getFlankingEdges(elementPosition.targetEdge), positiveEdge = _a.positiveEdge, negativeEdge = _a.negativeEdge;
var beakTargetPoint = _getCenterValue(target, elementPosition.targetEdge);
var elementBounds = new Utilities_2.Rectangle(beakWidth / 2, elementPosition.elementRectangle.width - beakWidth / 2, beakWidth / 2, elementPosition.elementRectangle.height - beakWidth / 2);
var beakPosition = new Utilities_2.Rectangle(0, beakWidth, 0, beakWidth);
beakPosition = _moveEdge(beakPosition, elementPosition.targetEdge * -1, -beakWidth / 2);
beakPosition = _centerEdgeToPoint(beakPosition, elementPosition.targetEdge * -1, beakTargetPoint - _getRelativeRectEdgeValue(positiveEdge, elementPosition.elementRectangle));
if (!_isEdgeInBounds(beakPosition, elementBounds, positiveEdge)) {
beakPosition = _alignEdges(beakPosition, elementBounds, positiveEdge);
}
else if (!_isEdgeInBounds(beakPosition, elementBounds, negativeEdge)) {
beakPosition = _alignEdges(beakPosition, elementBounds, negativeEdge);
}
return beakPosition;
}
function _getRectangleFromElement(element) {
var clientRect = element.getBoundingClientRect();
return new Utilities_2.Rectangle(clientRect.left, clientRect.right, clientRect.top, clientRect.bottom);
}
function _getRectangleFromIRect(rect) {
return new Utilities_2.Rectangle(rect.left, rect.right, rect.top, rect.bottom);
}
function _getTargetRect(bounds, target) {
var targetRectangle;
if (target) {
// eslint-disable-next-line no-extra-boolean-cast
if (!!target.preventDefault) {
var ev = target;
targetRectangle = new Utilities_2.Rectangle(ev.clientX, ev.clientX, ev.clientY, ev.clientY);
// eslint-disable-next-line no-extra-boolean-cast
}
else if (!!target.getBoundingClientRect) {
targetRectangle = _getRectangleFromElement(target);
// HTMLImgElements can have x and y values. The check for it being a point must go last.
}
else {
var rectOrPoint = target;
// eslint-disable-next-line deprecation/deprecation
var left = rectOrPoint.left || rectOrPoint.x;
// eslint-disable-next-line deprecation/deprecation
var top_1 = rectOrPoint.top || rectOrPoint.y;
var right = rectOrPoint.right || left;
var bottom = rectOrPoint.bottom || top_1;
targetRectangle = new Utilities_2.Rectangle(left, right, top_1, bottom);
}
if (!_isRectangleWithinBounds(targetRectangle, bounds)) {
var outOfBounds = _getOutOfBoundsEdges(targetRectangle, bounds);
for (var _i = 0, outOfBounds_1 = outOfBounds; _i < outOfBounds_1.length; _i++) {
var direction = outOfBounds_1[_i];
targetRectangle[positioning_types_1.RectangleEdge[direction]] = bounds[positioning_types_1.RectangleEdge[direction]];
}
}
}
else {
targetRectangle = new Utilities_2.Rectangle(0, 0, 0, 0);
}
return targetRectangle;
}
/**
* If max height is less than zero it returns the bounds height instead.
*/
function _getMaxHeightFromTargetRectangle(targetRectangle, targetEdge, gapSpace, bounds, coverTarget) {
var maxHeight = 0;
var directionalHint = DirectionalDictionary[targetEdge];
// If cover target is set, then the max height should be calculated using the opposite of the target edge since
// that's the direction that the callout will expand in.
// For instance, if the directionalhint is bottomLeftEdge then the callout will position so it's bottom edge
// is aligned with the bottom of the target and expand up towards the top of the screen and the calculated max height
// is (bottom of target) - (top of screen) - gapSpace.
var target = coverTarget ? directionalHint.targetEdge * -1 : directionalHint.targetEdge;
if (target === positioning_types_1.RectangleEdge.top) {
maxHeight = _getEdgeValue(targetRectangle, directionalHint.targetEdge) - bounds.top - gapSpace;
}
else if (target === positioning_types_1.RectangleEdge.bottom) {
maxHeight = bounds.bottom - _getEdgeValue(targetRectangle, directionalHint.targetEdge) - gapSpace;
}
else {
maxHeight = bounds.bottom - targetRectangle.top - gapSpace;
}
return maxHeight > 0 ? maxHeight : bounds.height;
}
function _positionElementRelative(props, elementToPosition, boundingRect, previousPositions) {
var gap = props.gapSpace ? props.gapSpace : 0;
var targetRect = _getTargetRect(boundingRect, props.target);
var positionData = _getAlignmentData(_getPositionData(props.directionalHint, props.directionalHintForRTL, previousPositions), targetRect, boundingRect, props.coverTarget, props.alignTargetEdge);
var positionedElement = _positionElementWithinBounds(_getRectangleFromElement(elementToPosition), targetRect, boundingRect, positionData, gap, props.directionalHintFixed, props.coverTarget);
return tslib_1.__assign(tslib_1.__assign({}, positionedElement), { targetRectangle: targetRect });
}
function _finalizePositionData(positionedElement, hostElement, bounds, coverTarget, doNotFinalizeReturnEdge) {
var finalizedElement = _finalizeElementPosition(positionedElement.elementRectangle, hostElement, positionedElement.targetEdge, bounds, positionedElement.alignmentEdge, coverTarget, doNotFinalizeReturnEdge);
return {
elementPosition: finalizedElement,
targetEdge: positionedElement.targetEdge,
alignmentEdge: positionedElement.alignmentEdge,
};
}
function _positionElement(props, hostElement, elementToPosition, previousPositions) {
var boundingRect = props.bounds
? _getRectangleFromIRect(props.bounds)
: new Utilities_2.Rectangle(0, window.innerWidth - Utilities_1.getScrollbarWidth(), 0, window.innerHeight);
var positionedElement = _positionElementRelative(props, elementToPosition, boundingRect, previousPositions);
return _finalizePositionData(positionedElement, hostElement, boundingRect, props.coverTarget);
}
function _positionCallout(props, hostElement, callout, previousPositions, doNotFinalizeReturnEdge) {
var beakWidth = props.isBeakVisible ? props.beakWidth || 0 : 0;
var gap = _calculateActualBeakWidthInPixels(beakWidth) / 2 + (props.gapSpace ? props.gapSpace : 0);
var positionProps = props;
positionProps.gapSpace = gap;
var boundingRect = props.bounds
? _getRectangleFromIRect(props.bounds)
: new Utilities_2.Rectangle(0, window.innerWidth - Utilities_1.getScrollbarWidth(), 0, window.innerHeight);
var positionedElement = _positionElementRelative(positionProps, callout, boundingRect, previousPositions);
var beakPositioned = _positionBeak(beakWidth, positionedElement);
var finalizedBeakPosition = _finalizeBeakPosition(positionedElement, beakPositioned, boundingRect);
return tslib_1.__assign(tslib_1.__assign({}, _finalizePositionData(positionedElement, hostElement, boundingRect, props.coverTarget, doNotFinalizeReturnEdge)), { beakPosition: finalizedBeakPosition });
}
function _positionCard(props, hostElement, callout, previousPositions) {
return _positionCallout(props, hostElement, callout, previousPositions, true);
}
// END PRIVATE FUNCTIONS
exports.__positioningTestPackage = {
_finalizePositionData: _finalizePositionData,
_finalizeBeakPosition: _finalizeBeakPosition,
_calculateActualBeakWidthInPixels: _calculateActualBeakWidthInPixels,
_positionElementWithinBounds: _positionElementWithinBounds,
_positionBeak: _positionBeak,
_getPositionData: _getPositionData,
_getMaxHeightFromTargetRectangle: _getMaxHeightFromTargetRectangle,
};
/**
* Used to position an element relative to the given positioning props.
* If positioning has been completed before, previousPositions can be passed to ensure that the positioning element
* repositions based on its previous targets rather than starting with directionalhint.
*/
function positionElement(props, hostElement, elementToPosition, previousPositions) {
return _positionElement(props, hostElement, elementToPosition, previousPositions);
}
exports.positionElement = positionElement;
function positionCallout(props, hostElement, elementToPosition, previousPositions) {
return _positionCallout(props, hostElement, elementToPosition, previousPositions);
}
exports.positionCallout = positionCallout;
function positionCard(props, hostElement, elementToPosition, previousPositions) {
return _positionCard(props, hostElement, elementToPosition, previousPositions);
}
exports.positionCard = positionCard;
/**
* Gets the maximum height that a rectangle can have in order to fit below or above a target.
* If the directional hint specifies a left or right edge (i.e. leftCenter) it will limit the height to the topBorder
* of the target given.
* If no bounds are provided then the window is treated as the bounds.
*/
function getMaxHeight(target, targetEdge, gapSpace, bounds, coverTarget) {
if (gapSpace === void 0) { gapSpace = 0; }
var mouseTarget = target;
var elementTarget = target;
var rectOrPointTarget = target;
var targetRect;
var boundingRectangle = bounds
? _getRectangleFromIRect(bounds)
: new Utilities_2.Rectangle(0, window.innerWidth - Utilities_1.getScrollbarWidth(), 0, window.innerHeight);
// eslint-disable-next-line deprecation/deprecation
var left = rectOrPointTarget.left || rectOrPointTarget.x;
// eslint-disable-next-line deprecation/deprecation
var top = rectOrPointTarget.top || rectOrPointTarget.y;
var right = rectOrPointTarget.right || left;
var bottom = rectOrPointTarget.bottom || top;
// eslint-disable-next-line no-extra-boolean-cast -- may not actually be a MouseEvent
if (!!mouseTarget.stopPropagation) {
targetRect = new Utilities_2.Rectangle(mouseTarget.clientX, mouseTarget.clientX, mouseTarget.clientY, mouseTarget.clientY);
}
else if (left !== undefined && top !== undefined) {
targetRect = new Utilities_2.Rectangle(left, right, top, bottom);
}
else {
targetRect = _getRectangleFromElement(elementTarget);
}
return _getMaxHeightFromTargetRectangle(targetRect, targetEdge, gapSpace, boundingRectangle, coverTarget);
}
exports.getMaxHeight = getMaxHeight;
/**
* Returns the opposite edge of the given RectangleEdge.
*/
function getOppositeEdge(edge) {
return edge * -1;
}
exports.getOppositeEdge = getOppositeEdge;
function _getBoundsFromTargetWindow(target, targetWindow) {
var segments = undefined;
if (targetWindow.getWindowSegments) {
segments = targetWindow.getWindowSegments();
}
// Identify if we're dealing with single screen scenarios.
if (segments === undefined || segments.length <= 1) {
return {
top: 0,
left: 0,
right: targetWindow.innerWidth,
bottom: targetWindow.innerHeight,
width: targetWindow.innerWidth,
height: targetWindow.innerHeight,
};
}
// Logic for determining dual screen scenarios.
var x = 0;
var y = 0;
// If the target is an Element get coordinates for its center.
if (target !== null && !!target.getBoundingClientRect) {
var clientRect = target.getBoundingClientRect();
x = (clientRect.left + clientRect.right) / 2;
y = (clientRect.top + clientRect.bottom) / 2;
}
// If the target is not null get x-axis and y-axis coordinates directly.
else if (target !== null) {
// eslint-disable-next-line deprecation/deprecation
x = target.left || target.x;
// eslint-disable-next-line deprecation/deprecation
y = target.top || target.y;
}
var bounds = { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
// Define which window segment are the coordinates in and calculate bounds based on that.
for (var _i = 0, segments_1 = segments; _i < segments_1.length; _i++) {
var segment = segments_1[_i];
if (x && segment.left <= x && segment.right >= x && y && segment.top <= y && segment.bottom >= y) {
bounds = {
top: segment.top,
left: segment.left,
right: segment.right,
bottom: segment.bottom,
width: segment.width,
height: segment.height,
};
}
}
return bounds;
}
function getBoundsFromTargetWindow(target, targetWindow) {
return _getBoundsFromTargetWindow(target, targetWindow);
}
exports.getBoundsFromTargetWindow = getBoundsFromTargetWindow;
//# sourceMappingURL=positioning.js.map