UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

338 lines (330 loc) • 14.4 kB
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; }