1 | // @flow
|
---|
2 | import type { Rect, VirtualElement, Window } from '../types';
|
---|
3 | import getBoundingClientRect from './getBoundingClientRect';
|
---|
4 | import getNodeScroll from './getNodeScroll';
|
---|
5 | import getNodeName from './getNodeName';
|
---|
6 | import { isHTMLElement } from './instanceOf';
|
---|
7 | import getWindowScrollBarX from './getWindowScrollBarX';
|
---|
8 | import getDocumentElement from './getDocumentElement';
|
---|
9 | import isScrollParent from './isScrollParent';
|
---|
10 | import { round } from '../utils/math';
|
---|
11 |
|
---|
12 | function isElementScaled(element: HTMLElement) {
|
---|
13 | const rect = element.getBoundingClientRect();
|
---|
14 | const scaleX = round(rect.width) / element.offsetWidth || 1;
|
---|
15 | const scaleY = round(rect.height) / element.offsetHeight || 1;
|
---|
16 |
|
---|
17 | return scaleX !== 1 || scaleY !== 1;
|
---|
18 | }
|
---|
19 |
|
---|
20 | // Returns the composite rect of an element relative to its offsetParent.
|
---|
21 | // Composite means it takes into account transforms as well as layout.
|
---|
22 | export default function getCompositeRect(
|
---|
23 | elementOrVirtualElement: Element | VirtualElement,
|
---|
24 | offsetParent: Element | Window,
|
---|
25 | isFixed: boolean = false
|
---|
26 | ): Rect {
|
---|
27 | const isOffsetParentAnElement = isHTMLElement(offsetParent);
|
---|
28 | const offsetParentIsScaled =
|
---|
29 | isHTMLElement(offsetParent) && isElementScaled(offsetParent);
|
---|
30 | const documentElement = getDocumentElement(offsetParent);
|
---|
31 | const rect = getBoundingClientRect(
|
---|
32 | elementOrVirtualElement,
|
---|
33 | offsetParentIsScaled,
|
---|
34 | isFixed
|
---|
35 | );
|
---|
36 |
|
---|
37 | let scroll = { scrollLeft: 0, scrollTop: 0 };
|
---|
38 | let offsets = { x: 0, y: 0 };
|
---|
39 |
|
---|
40 | if (isOffsetParentAnElement || (!isOffsetParentAnElement && !isFixed)) {
|
---|
41 | if (
|
---|
42 | getNodeName(offsetParent) !== 'body' ||
|
---|
43 | // https://github.com/popperjs/popper-core/issues/1078
|
---|
44 | isScrollParent(documentElement)
|
---|
45 | ) {
|
---|
46 | scroll = getNodeScroll(offsetParent);
|
---|
47 | }
|
---|
48 |
|
---|
49 | if (isHTMLElement(offsetParent)) {
|
---|
50 | offsets = getBoundingClientRect(offsetParent, true);
|
---|
51 | offsets.x += offsetParent.clientLeft;
|
---|
52 | offsets.y += offsetParent.clientTop;
|
---|
53 | } else if (documentElement) {
|
---|
54 | offsets.x = getWindowScrollBarX(documentElement);
|
---|
55 | }
|
---|
56 | }
|
---|
57 |
|
---|
58 | return {
|
---|
59 | x: rect.left + scroll.scrollLeft - offsets.x,
|
---|
60 | y: rect.top + scroll.scrollTop - offsets.y,
|
---|
61 | width: rect.width,
|
---|
62 | height: rect.height,
|
---|
63 | };
|
---|
64 | }
|
---|