You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

64 lines
2.1 KiB

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