packages/dom/src/platform/convertOffsetParentRelativeRectToViewportRelativeRect.ts
import type {Elements, Rect, Strategy} from '@floating-ui/core';
import {createCoords} from '@floating-ui/utils';
import {
getDocumentElement,
getNodeName,
getNodeScroll,
isHTMLElement,
isOverflowElement,
isTopLayer,
} from '@floating-ui/utils/dom';
import {getBoundingClientRect} from '../utils/getBoundingClientRect';
import {getScale} from './getScale';
import {getHTMLOffset} from '../utils/getHTMLOffset';
export function convertOffsetParentRelativeRectToViewportRelativeRect({
elements,
rect,
offsetParent,
strategy,
}: {
elements?: Elements;
rect: Rect;
offsetParent: Element | Window;
strategy: Strategy;
}): Rect {
const isFixed = strategy === 'fixed';
const documentElement = getDocumentElement(offsetParent);
const topLayer = elements ? isTopLayer(elements.floating) : false;
if (offsetParent === documentElement || (topLayer && isFixed)) {
return rect;
}
let scroll = {scrollLeft: 0, scrollTop: 0};
let scale = createCoords(1);
const offsets = createCoords(0);
const isOffsetParentAnElement = isHTMLElement(offsetParent);
if (isOffsetParentAnElement || (!isOffsetParentAnElement && !isFixed)) {
if (
getNodeName(offsetParent) !== 'body' ||
isOverflowElement(documentElement)
) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent);
scale = getScale(offsetParent);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.y + offsetParent.clientTop;
}
}
const htmlOffset =
documentElement && !isOffsetParentAnElement && !isFixed
? getHTMLOffset(documentElement, scroll, true)
: createCoords(0);
return {
width: rect.width * scale.x,
height: rect.height * scale.y,
x:
rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,
y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y,
};
}