mirror of
https://github.com/facebook/react.git
synced 2026-02-24 20:53:03 +00:00
* Update Flow to 0.84 * Fix violations * Use inexact object syntax in files from fbsource * Fix warning extraction to use a modern parser * Codemod inexact objects to new syntax * Tighten types that can be exact * Revert unintentional formatting changes from codemod
129 lines
4.1 KiB
JavaScript
129 lines
4.1 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
export type Rect = {
|
|
bottom: number,
|
|
height: number,
|
|
left: number,
|
|
right: number,
|
|
top: number,
|
|
width: number,
|
|
...
|
|
};
|
|
|
|
// Get the window object for the document that a node belongs to,
|
|
// or return null if it cannot be found (node not attached to DOM,
|
|
// etc).
|
|
export function getOwnerWindow(node: HTMLElement): typeof window | null {
|
|
if (!node.ownerDocument) {
|
|
return null;
|
|
}
|
|
return node.ownerDocument.defaultView;
|
|
}
|
|
|
|
// Get the iframe containing a node, or return null if it cannot
|
|
// be found (node not within iframe, etc).
|
|
export function getOwnerIframe(node: HTMLElement): HTMLElement | null {
|
|
const nodeWindow = getOwnerWindow(node);
|
|
if (nodeWindow) {
|
|
return nodeWindow.frameElement;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Get a bounding client rect for a node, with an
|
|
// offset added to compensate for its border.
|
|
export function getBoundingClientRectWithBorderOffset(node: HTMLElement) {
|
|
const dimensions = getElementDimensions(node);
|
|
return mergeRectOffsets([
|
|
node.getBoundingClientRect(),
|
|
{
|
|
top: dimensions.borderTop,
|
|
left: dimensions.borderLeft,
|
|
bottom: dimensions.borderBottom,
|
|
right: dimensions.borderRight,
|
|
// This width and height won't get used by mergeRectOffsets (since this
|
|
// is not the first rect in the array), but we set them so that this
|
|
// object typechecks as a ClientRect.
|
|
width: 0,
|
|
height: 0,
|
|
},
|
|
]);
|
|
}
|
|
|
|
// Add together the top, left, bottom, and right properties of
|
|
// each ClientRect, but keep the width and height of the first one.
|
|
export function mergeRectOffsets(rects: Array<Rect>): Rect {
|
|
return rects.reduce((previousRect, rect) => {
|
|
if (previousRect == null) {
|
|
return rect;
|
|
}
|
|
|
|
return {
|
|
top: previousRect.top + rect.top,
|
|
left: previousRect.left + rect.left,
|
|
width: previousRect.width,
|
|
height: previousRect.height,
|
|
bottom: previousRect.bottom + rect.bottom,
|
|
right: previousRect.right + rect.right,
|
|
};
|
|
});
|
|
}
|
|
|
|
// Calculate a boundingClientRect for a node relative to boundaryWindow,
|
|
// taking into account any offsets caused by intermediate iframes.
|
|
export function getNestedBoundingClientRect(
|
|
node: HTMLElement,
|
|
boundaryWindow: typeof window,
|
|
): Rect {
|
|
const ownerIframe = getOwnerIframe(node);
|
|
if (ownerIframe && ownerIframe !== boundaryWindow) {
|
|
const rects = [node.getBoundingClientRect()];
|
|
let currentIframe = ownerIframe;
|
|
let onlyOneMore = false;
|
|
while (currentIframe) {
|
|
const rect = getBoundingClientRectWithBorderOffset(currentIframe);
|
|
rects.push(rect);
|
|
currentIframe = getOwnerIframe(currentIframe);
|
|
|
|
if (onlyOneMore) {
|
|
break;
|
|
}
|
|
// We don't want to calculate iframe offsets upwards beyond
|
|
// the iframe containing the boundaryWindow, but we
|
|
// need to calculate the offset relative to the boundaryWindow.
|
|
if (currentIframe && getOwnerWindow(currentIframe) === boundaryWindow) {
|
|
onlyOneMore = true;
|
|
}
|
|
}
|
|
|
|
return mergeRectOffsets(rects);
|
|
} else {
|
|
return node.getBoundingClientRect();
|
|
}
|
|
}
|
|
|
|
export function getElementDimensions(domElement: Element) {
|
|
const calculatedStyle = window.getComputedStyle(domElement);
|
|
return {
|
|
borderLeft: parseInt(calculatedStyle.borderLeftWidth, 10),
|
|
borderRight: parseInt(calculatedStyle.borderRightWidth, 10),
|
|
borderTop: parseInt(calculatedStyle.borderTopWidth, 10),
|
|
borderBottom: parseInt(calculatedStyle.borderBottomWidth, 10),
|
|
marginLeft: parseInt(calculatedStyle.marginLeft, 10),
|
|
marginRight: parseInt(calculatedStyle.marginRight, 10),
|
|
marginTop: parseInt(calculatedStyle.marginTop, 10),
|
|
marginBottom: parseInt(calculatedStyle.marginBottom, 10),
|
|
paddingLeft: parseInt(calculatedStyle.paddingLeft, 10),
|
|
paddingRight: parseInt(calculatedStyle.paddingRight, 10),
|
|
paddingTop: parseInt(calculatedStyle.paddingTop, 10),
|
|
paddingBottom: parseInt(calculatedStyle.paddingBottom, 10),
|
|
};
|
|
}
|