// @flow import type { Rect, VirtualElement, Window } from '../types'; import getBoundingClientRect from './getBoundingClientRect'; import getNodeScroll from './getNodeScroll'; import getNodeName from './getNodeName'; import { isHTMLElement } from './instanceOf'; import getWindowScrollBarX from './getWindowScrollBarX'; import getDocumentElement from './getDocumentElement'; import isScrollParent from './isScrollParent'; import { round } from '../utils/math'; function isElementScaled(element: HTMLElement) { const rect = element.getBoundingClientRect(); const scaleX = round(rect.width) / element.offsetWidth || 1; const scaleY = round(rect.height) / element.offsetHeight || 1; return scaleX !== 1 || scaleY !== 1; } // Returns the composite rect of an element relative to its offsetParent. // Composite means it takes into account transforms as well as layout. export default function getCompositeRect( elementOrVirtualElement: Element | VirtualElement, offsetParent: Element | Window, isFixed: boolean = false ): Rect { const isOffsetParentAnElement = isHTMLElement(offsetParent); const offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent); const documentElement = getDocumentElement(offsetParent); const rect = getBoundingClientRect( elementOrVirtualElement, offsetParentIsScaled, isFixed ); let scroll = { scrollLeft: 0, scrollTop: 0 }; let offsets = { x: 0, y: 0 }; if (isOffsetParentAnElement || (!isOffsetParentAnElement && !isFixed)) { if ( getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078 isScrollParent(documentElement) ) { scroll = getNodeScroll(offsetParent); } if (isHTMLElement(offsetParent)) { offsets = getBoundingClientRect(offsetParent, true); offsets.x += offsetParent.clientLeft; offsets.y += offsetParent.clientTop; } else if (documentElement) { offsets.x = getWindowScrollBarX(documentElement); } } return { x: rect.left + scroll.scrollLeft - offsets.x, y: rect.top + scroll.scrollTop - offsets.y, width: rect.width, height: rect.height, }; }