@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
338 lines (330 loc) • 14.4 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
export function isBody(elem) {
return elem === document.body;
}
export function isTextNode(elem) {
return elem && elem.nodeType === 3;
}
/**
* Decides if given fitHeight fits below or above the target taking boundaries into account.
*/
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/max-params
export function getVerticalPlacement(target, boundariesElement, fitHeight, alignY, forcePlacement, preventOverflow) {
if (forcePlacement && alignY) {
return alignY;
}
if (!fitHeight) {
return 'bottom';
}
if (isTextNode(target)) {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
target = target.parentElement;
}
var boundariesClientRect = boundariesElement.getBoundingClientRect();
var boundariesHeight = boundariesClientRect.height;
var boundariesTop = isBody(boundariesElement) ? 0 : boundariesClientRect.top;
var _target$getBoundingCl = target.getBoundingClientRect(),
targetTop = _target$getBoundingCl.top,
targetHeight = _target$getBoundingCl.height;
var spaceAbove = targetTop - (boundariesTop - boundariesElement.scrollTop);
var spaceBelow = boundariesTop + boundariesHeight - (targetTop + targetHeight);
// Force vertical placement to bottom if the space above doesn't accomodate the fitHeight
if (preventOverflow) {
if (spaceAbove <= fitHeight) {
return 'bottom';
}
}
if (spaceBelow >= fitHeight || spaceBelow >= spaceAbove) {
return 'bottom';
}
return 'top';
}
/**
* Decides if given fitWidth fits to the left or to the right of the target taking boundaries into account.
*/
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/max-params
export function getHorizontalPlacement(target, boundariesElement, fitWidth, alignX, forcePlacement, preventOverflow) {
// force placement unless preventOverflow is enabled
if (forcePlacement && alignX && !preventOverflow) {
return alignX;
}
if (!fitWidth) {
return alignX || 'left';
}
if (isTextNode(target)) {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
target = target.parentElement;
}
var _target$getBoundingCl2 = target.getBoundingClientRect(),
targetLeft = _target$getBoundingCl2.left,
targetWidth = _target$getBoundingCl2.width;
var _boundariesElement$ge = boundariesElement.getBoundingClientRect(),
boundariesLeft = _boundariesElement$ge.left,
boundariesWidth = _boundariesElement$ge.width;
var spaceLeft = targetLeft - boundariesLeft + targetWidth;
var spaceRight = boundariesLeft + boundariesWidth - targetLeft;
if (alignX && spaceLeft > fitWidth && spaceRight > fitWidth) {
return alignX;
} else if (spaceRight >= fitWidth || spaceRight >= spaceLeft && !alignX) {
return 'left';
}
return 'right';
}
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/max-params
export function calculatePlacement(target, boundariesElement, fitWidth, fitHeight, alignX, alignY, forcePlacement, preventOverflow) {
return [getVerticalPlacement(target, boundariesElement, fitHeight, alignY, forcePlacement, preventOverflow), getHorizontalPlacement(target, boundariesElement, fitWidth, alignX, forcePlacement, preventOverflow)];
}
var calculateHorizontalPlacement = function calculateHorizontalPlacement(_ref) {
var placement = _ref.placement,
targetLeft = _ref.targetLeft,
targetRight = _ref.targetRight,
targetWidth = _ref.targetWidth,
isPopupParentBody = _ref.isPopupParentBody,
popupOffsetParentLeft = _ref.popupOffsetParentLeft,
popupOffsetParentRight = _ref.popupOffsetParentRight,
popupOffsetParentScrollLeft = _ref.popupOffsetParentScrollLeft,
popupOffsetParentClientWidth = _ref.popupOffsetParentClientWidth,
popupClientWidth = _ref.popupClientWidth,
offset = _ref.offset,
_ref$allowOutOfBounds = _ref.allowOutOfBounds,
allowOutOfBounds = _ref$allowOutOfBounds === void 0 ? false : _ref$allowOutOfBounds;
var position = {};
if (placement === 'left') {
position.left = Math.ceil(targetLeft - popupOffsetParentLeft + (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0]);
} else if (placement === 'center') {
position.left = Math.ceil(targetLeft - popupOffsetParentLeft + (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0] + targetWidth / 2 - popupClientWidth / 2);
} else if (placement === 'end') {
var left = Math.ceil(targetRight - popupOffsetParentLeft + (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0]);
position.left = left;
} else {
position.right = Math.ceil(popupOffsetParentRight - targetRight - (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0]);
}
if (!allowOutOfBounds) {
if (position.left !== undefined) {
position.left = getPopupXInsideParent(position.left, popupClientWidth, popupOffsetParentClientWidth);
}
if (position.right !== undefined) {
position.right = getPopupXInsideParent(position.right, popupClientWidth, popupOffsetParentClientWidth);
}
}
return position;
};
var getPopupXInsideParent = function getPopupXInsideParent(x, popupClientWidth, popupOffsetParentClientWidth) {
// minimum distance the popup can be from the edge of its parent
var minPopupMargin = 1;
// prevent going too far right
if (popupOffsetParentClientWidth < x + popupClientWidth) {
x = popupOffsetParentClientWidth - popupClientWidth - minPopupMargin;
}
// prevent going too far left
return Math.max(minPopupMargin, x);
};
var calculateVerticalStickBottom = function calculateVerticalStickBottom(_ref2) {
var target = _ref2.target,
targetTop = _ref2.targetTop,
targetHeight = _ref2.targetHeight,
popup = _ref2.popup,
offset = _ref2.offset,
position = _ref2.position;
var scrollParent = findOverflowScrollParent(target);
var newPos = _objectSpread({}, position);
if (scrollParent) {
var topOffsetTop = targetTop - scrollParent.getBoundingClientRect().top;
var targetEnd = targetHeight + topOffsetTop;
if (scrollParent.clientHeight - targetEnd <= popup.clientHeight + offset[1] * 2 && topOffsetTop < scrollParent.clientHeight) {
var scroll = targetEnd - scrollParent.clientHeight + offset[1] * 2;
var top = newPos.top || 0;
top = top - (scroll + popup.clientHeight);
newPos.top = top;
}
}
return newPos;
};
var calculateVerticalStickTop = function calculateVerticalStickTop(_ref3) {
var target = _ref3.target,
targetTop = _ref3.targetTop,
targetHeight = _ref3.targetHeight,
popupOffsetParentHeight = _ref3.popupOffsetParentHeight,
popupOffsetParent = _ref3.popupOffsetParent,
offset = _ref3.offset,
position = _ref3.position,
placement = _ref3.placement;
var scrollParent = findOverflowScrollParent(target);
var newPos = _objectSpread({}, position);
if (scrollParent) {
var _scrollParent$getBoun = scrollParent.getBoundingClientRect(),
scrollParentTop = _scrollParent$getBoun.top;
var topBoundary = targetTop - scrollParentTop;
var scrollParentScrollTop = scrollParent.scrollTop;
if (topBoundary < 0) {
var isBelowNodeBoundary = targetTop + (scrollParentScrollTop - scrollParentTop) + targetHeight + offset[1] < scrollParentScrollTop;
if (placement === 'top') {
if (isBelowNodeBoundary) {
newPos.bottom = popupOffsetParentHeight - (topBoundary + popupOffsetParent.scrollTop + targetHeight);
} else {
newPos.bottom = topBoundary + (newPos.bottom || 0);
}
}
if (placement === 'start') {
if (isBelowNodeBoundary) {
newPos.top = topBoundary + popupOffsetParent.scrollTop + targetHeight;
} else {
newPos.top = Math.abs(topBoundary) + (newPos.top || 0) + offset[1];
}
}
}
}
return newPos;
};
var calculateVerticalPlacement = function calculateVerticalPlacement(_ref4) {
var placement = _ref4.placement,
targetTop = _ref4.targetTop,
targetHeight = _ref4.targetHeight,
isPopupParentBody = _ref4.isPopupParentBody,
popupOffsetParentHeight = _ref4.popupOffsetParentHeight,
popupOffsetParentTop = _ref4.popupOffsetParentTop,
popupOffsetParentScrollTop = _ref4.popupOffsetParentScrollTop,
borderBottomWidth = _ref4.borderBottomWidth,
offset = _ref4.offset;
var position = {};
if (placement === 'top') {
position.bottom = Math.ceil(popupOffsetParentHeight - (targetTop - popupOffsetParentTop) - (isPopupParentBody ? 0 : popupOffsetParentScrollTop) - borderBottomWidth + offset[1]);
} else if (placement === 'start') {
position.top = Math.ceil(targetTop - popupOffsetParentTop - offset[1] + (isPopupParentBody ? 0 : popupOffsetParentScrollTop));
} else {
var top = Math.ceil(targetTop - popupOffsetParentTop + targetHeight + (isPopupParentBody ? 0 : popupOffsetParentScrollTop) - borderBottomWidth + offset[1]);
position.top = top;
}
return position;
};
/**
* Calculates relative coordinates for placing popup along with the target.
* Uses placement from calculatePlacement.
*/
export function calculatePosition(_ref5) {
var placement = _ref5.placement,
target = _ref5.target,
popup = _ref5.popup,
offset = _ref5.offset,
stick = _ref5.stick,
_ref5$allowOutOfBound = _ref5.allowOutOfBounds,
allowOutOfBounds = _ref5$allowOutOfBound === void 0 ? false : _ref5$allowOutOfBound,
rect = _ref5.rect;
var position = {};
if (!target || !popup || !popup.offsetParent) {
return position;
}
if (isTextNode(target)) {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
target = target.parentElement;
}
// Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
var popupOffsetParent = popup.offsetParent;
var offsetParentStyle = popupOffsetParent.style;
var borderBottomWidth = 0;
if (offsetParentStyle && offsetParentStyle.borderBottomWidth) {
borderBottomWidth = parseInt(offsetParentStyle.borderBottomWidth, 10);
}
var _placement = _slicedToArray(placement, 2),
verticalPlacement = _placement[0],
horizontalPlacement = _placement[1];
var _ref6 = rect ? rect : popupOffsetParent.getBoundingClientRect(),
popupOffsetParentTop = _ref6.top,
popupOffsetParentLeft = _ref6.left,
popupOffsetParentRight = _ref6.right,
popupOffsetParentHeight = _ref6.height;
var _target$getBoundingCl3 = target.getBoundingClientRect(),
targetTop = _target$getBoundingCl3.top,
targetLeft = _target$getBoundingCl3.left,
targetRight = _target$getBoundingCl3.right,
targetHeight = _target$getBoundingCl3.height,
targetWidth = _target$getBoundingCl3.width;
var isPopupParentBody = isBody(popupOffsetParent);
var verticalPosition = calculateVerticalPlacement({
placement: verticalPlacement,
targetTop: targetTop,
isPopupParentBody: isPopupParentBody,
popupOffsetParentHeight: popupOffsetParentHeight,
popupOffsetParentTop: popupOffsetParentTop,
popupOffsetParentScrollTop: popupOffsetParent.scrollTop || 0,
targetHeight: targetHeight,
borderBottomWidth: borderBottomWidth,
offset: offset
});
position = _objectSpread(_objectSpread({}, position), verticalPosition);
if ((verticalPlacement === 'top' || verticalPlacement === 'start') && stick) {
position = calculateVerticalStickTop({
target: target,
targetTop: targetTop,
targetHeight: targetHeight,
popupOffsetParentHeight: popupOffsetParentHeight,
popupOffsetParent: popupOffsetParent,
popup: popup,
offset: offset,
position: position,
placement: verticalPlacement
});
}
if (verticalPlacement !== 'top' && verticalPlacement !== 'start' && stick) {
position = calculateVerticalStickBottom({
target: target,
targetTop: targetTop,
targetHeight: targetHeight,
popup: popup,
offset: offset,
position: position
});
}
var horizontalPosition = calculateHorizontalPlacement({
placement: horizontalPlacement,
targetLeft: targetLeft,
targetRight: targetRight,
targetWidth: targetWidth,
isPopupParentBody: isPopupParentBody,
popupOffsetParentLeft: popupOffsetParentLeft,
popupOffsetParentRight: popupOffsetParentRight,
popupOffsetParentScrollLeft: popupOffsetParent.scrollLeft || 0,
popupOffsetParentClientWidth: popup.offsetParent.clientWidth,
popupClientWidth: popup.clientWidth || 0,
offset: offset,
allowOutOfBounds: allowOutOfBounds
});
position = _objectSpread(_objectSpread({}, position), horizontalPosition);
return position;
}
export function validatePosition(popup) {
// popup.offsetParent does not exist if the popup element is not mounted
if (!popup || !popup.offsetParent) {
return false;
}
return true;
}
/**
* Traverse DOM Tree upwards looking for popup parents with "overflow: scroll".
*/
export function findOverflowScrollParent(popup) {
var parent = popup;
if (!parent) {
return false;
}
// Ignored via go/ees005
// eslint-disable-next-line no-cond-assign
while (parent = parent.parentElement) {
// IE11 on Window 8 doesn't show styles from CSS when accessing through element.style property.
var style = window.getComputedStyle(parent);
if (style.overflow === 'scroll' || style.overflowX === 'scroll' || style.overflowY === 'scroll' || parent.classList.contains('fabric-editor-popup-scroll-parent')) {
return parent;
}
}
return false;
}