UNPKG

@wordpress/components

Version:
358 lines (319 loc) 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.positionToPlacement = exports.placementToMotionAnimationProps = exports.getReferenceOwnerDocument = exports.getReferenceElement = exports.getFrameScale = exports.getFrameOffset = void 0; /** * External dependencies */ // eslint-disable-next-line no-restricted-imports /** * Internal dependencies */ const POSITION_TO_PLACEMENT = { bottom: 'bottom', top: 'top', 'middle left': 'left', 'middle right': 'right', 'bottom left': 'bottom-end', 'bottom center': 'bottom', 'bottom right': 'bottom-start', 'top left': 'top-end', 'top center': 'top', 'top right': 'top-start', 'middle left left': 'left', 'middle left right': 'left', 'middle left bottom': 'left-end', 'middle left top': 'left-start', 'middle right left': 'right', 'middle right right': 'right', 'middle right bottom': 'right-end', 'middle right top': 'right-start', 'bottom left left': 'bottom-end', 'bottom left right': 'bottom-end', 'bottom left bottom': 'bottom-end', 'bottom left top': 'bottom-end', 'bottom center left': 'bottom', 'bottom center right': 'bottom', 'bottom center bottom': 'bottom', 'bottom center top': 'bottom', 'bottom right left': 'bottom-start', 'bottom right right': 'bottom-start', 'bottom right bottom': 'bottom-start', 'bottom right top': 'bottom-start', 'top left left': 'top-end', 'top left right': 'top-end', 'top left bottom': 'top-end', 'top left top': 'top-end', 'top center left': 'top', 'top center right': 'top', 'top center bottom': 'top', 'top center top': 'top', 'top right left': 'top-start', 'top right right': 'top-start', 'top right bottom': 'top-start', 'top right top': 'top-start', // `middle`/`middle center [corner?]` positions are associated to a fallback // `bottom` placement because there aren't any corresponding placement values. middle: 'bottom', 'middle center': 'bottom', 'middle center bottom': 'bottom', 'middle center left': 'bottom', 'middle center right': 'bottom', 'middle center top': 'bottom' }; /** * Converts the `Popover`'s legacy "position" prop to the new "placement" prop * (used by `floating-ui`). * * @param position The legacy position * @return The corresponding placement */ const positionToPlacement = position => { var _POSITION_TO_PLACEMEN; return (_POSITION_TO_PLACEMEN = POSITION_TO_PLACEMENT[position]) !== null && _POSITION_TO_PLACEMEN !== void 0 ? _POSITION_TO_PLACEMEN : 'bottom'; }; /** * @typedef AnimationOrigin * @type {Object} * @property {number} originX A number between 0 and 1 (in CSS logical properties jargon, 0 is "start", 0.5 is "center", and 1 is "end") * @property {number} originY A number between 0 and 1 (0 is top, 0.5 is center, and 1 is bottom) */ exports.positionToPlacement = positionToPlacement; const PLACEMENT_TO_ANIMATION_ORIGIN = { top: { originX: 0.5, originY: 1 }, // open from bottom, center 'top-start': { originX: 0, originY: 1 }, // open from bottom, left 'top-end': { originX: 1, originY: 1 }, // open from bottom, right right: { originX: 0, originY: 0.5 }, // open from middle, left 'right-start': { originX: 0, originY: 0 }, // open from top, left 'right-end': { originX: 0, originY: 1 }, // open from bottom, left bottom: { originX: 0.5, originY: 0 }, // open from top, center 'bottom-start': { originX: 0, originY: 0 }, // open from top, left 'bottom-end': { originX: 1, originY: 0 }, // open from top, right left: { originX: 1, originY: 0.5 }, // open from middle, right 'left-start': { originX: 1, originY: 0 }, // open from top, right 'left-end': { originX: 1, originY: 1 }, // open from bottom, right overlay: { originX: 0.5, originY: 0.5 } // open from center, center }; /** * Given the floating-ui `placement`, compute the framer-motion props for the * popover's entry animation. * * @param placement A placement string from floating ui * @return The object containing the motion props */ const placementToMotionAnimationProps = placement => { const translateProp = placement.startsWith('top') || placement.startsWith('bottom') ? 'translateY' : 'translateX'; const translateDirection = placement.startsWith('top') || placement.startsWith('left') ? 1 : -1; return { style: PLACEMENT_TO_ANIMATION_ORIGIN[placement], initial: { opacity: 0, scale: 0, [translateProp]: `${2 * translateDirection}em` }, animate: { opacity: 1, scale: 1, [translateProp]: 0 }, transition: { duration: 0.1, ease: [0, 0, 0.2, 1] } }; }; /** * Returns the offset of a document's frame element. * * @param document The iframe's owner document. * * @return The offset of the document's frame element, or undefined if the * document has no frame element. */ exports.placementToMotionAnimationProps = placementToMotionAnimationProps; const getFrameOffset = document => { var _document$defaultView; const frameElement = document === null || document === void 0 ? void 0 : (_document$defaultView = document.defaultView) === null || _document$defaultView === void 0 ? void 0 : _document$defaultView.frameElement; if (!frameElement) { return; } const iframeRect = frameElement.getBoundingClientRect(); return { x: iframeRect.left, y: iframeRect.top }; }; exports.getFrameOffset = getFrameOffset; const getFrameScale = document => { var _document$defaultView2; const frameElement = document === null || document === void 0 ? void 0 : (_document$defaultView2 = document.defaultView) === null || _document$defaultView2 === void 0 ? void 0 : _document$defaultView2.frameElement; if (!frameElement) { return { x: 1, y: 1 }; } const rect = frameElement.getBoundingClientRect(); return { x: rect.width / frameElement.offsetWidth, y: rect.height / frameElement.offsetHeight }; }; exports.getFrameScale = getFrameScale; const getReferenceOwnerDocument = _ref => { var _resultingReferenceOw; let { anchor, anchorRef, anchorRect, getAnchorRect, fallbackReferenceElement, fallbackDocument } = _ref; // In floating-ui's terms: // - "reference" refers to the popover's anchor element. // - "floating" refers the floating popover's element. // A floating element can also be positioned relative to a virtual element, // instead of a real one. A virtual element is represented by an object // with the `getBoundingClientRect()` function (like real elements). // See https://floating-ui.com/docs/virtual-elements for more info. let resultingReferenceOwnerDoc; if (anchor) { resultingReferenceOwnerDoc = anchor.ownerDocument; } else if (anchorRef !== null && anchorRef !== void 0 && anchorRef.top) { resultingReferenceOwnerDoc = anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.top.ownerDocument; } else if (anchorRef !== null && anchorRef !== void 0 && anchorRef.startContainer) { resultingReferenceOwnerDoc = anchorRef.startContainer.ownerDocument; } else if (anchorRef !== null && anchorRef !== void 0 && anchorRef.current) { resultingReferenceOwnerDoc = anchorRef.current.ownerDocument; } else if (anchorRef) { // This one should be deprecated. resultingReferenceOwnerDoc = anchorRef.ownerDocument; } else if (anchorRect && anchorRect !== null && anchorRect !== void 0 && anchorRect.ownerDocument) { resultingReferenceOwnerDoc = anchorRect.ownerDocument; } else if (getAnchorRect) { var _getAnchorRect; resultingReferenceOwnerDoc = (_getAnchorRect = getAnchorRect(fallbackReferenceElement)) === null || _getAnchorRect === void 0 ? void 0 : _getAnchorRect.ownerDocument; } return (_resultingReferenceOw = resultingReferenceOwnerDoc) !== null && _resultingReferenceOw !== void 0 ? _resultingReferenceOw : fallbackDocument; }; exports.getReferenceOwnerDocument = getReferenceOwnerDocument; const getReferenceElement = _ref2 => { var _referenceElement; let { anchor, anchorRef, anchorRect, getAnchorRect, fallbackReferenceElement, scale } = _ref2; let referenceElement = null; if (anchor) { referenceElement = anchor; } else if (anchorRef !== null && anchorRef !== void 0 && anchorRef.top) { // Create a virtual element for the ref. The expectation is that // if anchorRef.top is defined, then anchorRef.bottom is defined too. // Seems to be used by the block toolbar, when multiple blocks are selected // (top and bottom blocks are used to calculate the resulting rect). referenceElement = { getBoundingClientRect() { const topRect = anchorRef.top.getBoundingClientRect(); const bottomRect = anchorRef.bottom.getBoundingClientRect(); return new window.DOMRect(topRect.x, topRect.y, topRect.width, bottomRect.bottom - topRect.top); } }; } else if (anchorRef !== null && anchorRef !== void 0 && anchorRef.current) { // Standard React ref. referenceElement = anchorRef.current; } else if (anchorRef) { // If `anchorRef` holds directly the element's value (no `current` key) // This is a weird scenario and should be deprecated. referenceElement = anchorRef; } else if (anchorRect) { // Create a virtual element for the ref. referenceElement = { getBoundingClientRect() { return anchorRect; } }; } else if (getAnchorRect) { // Create a virtual element for the ref. referenceElement = { getBoundingClientRect() { var _rect$x, _rect$y, _rect$width, _rect$height; const rect = getAnchorRect(fallbackReferenceElement); return new window.DOMRect((_rect$x = rect.x) !== null && _rect$x !== void 0 ? _rect$x : rect.left, (_rect$y = rect.y) !== null && _rect$y !== void 0 ? _rect$y : rect.top, (_rect$width = rect.width) !== null && _rect$width !== void 0 ? _rect$width : rect.right - rect.left, (_rect$height = rect.height) !== null && _rect$height !== void 0 ? _rect$height : rect.bottom - rect.top); } }; } else if (fallbackReferenceElement) { // If no explicit ref is passed via props, fall back to // anchoring to the popover's parent node. referenceElement = fallbackReferenceElement.parentElement; } if (referenceElement && (scale.x !== 1 || scale.y !== 1)) { // If the popover is inside an iframe, the coordinates of the // reference element need to be scaled to match the iframe's scale. const rect = referenceElement.getBoundingClientRect(); referenceElement = { getBoundingClientRect() { return new window.DOMRect(rect.x * scale.x, rect.y * scale.y, rect.width * scale.x, rect.height * scale.y); } }; } // Convert any `undefined` value to `null`. return (_referenceElement = referenceElement) !== null && _referenceElement !== void 0 ? _referenceElement : null; }; exports.getReferenceElement = getReferenceElement; //# sourceMappingURL=utils.js.map