@wordpress/components
Version:
UI components for WordPress.
358 lines (319 loc) • 11.2 kB
JavaScript
;
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