import { getBoundingClientRect } from './getBoundingClientRect'; import { getClientRect } from './getClientRect'; import { getScrollParent } from './getScrollParent'; import { getStyleComputedProperty } from './getStyleComputedProperty'; import { includeScroll } from './includeScroll'; import { Offsets } from '../models'; import { isNumber } from './isNumeric'; export function getOffsetRectRelativeToArbitraryNode( children: HTMLElement, parent: HTMLElement, fixedPosition = false ): Offsets { const isHTML = parent.nodeName === 'HTML'; const childrenRect = getBoundingClientRect(children); const parentRect = getBoundingClientRect(parent); const scrollParent = getScrollParent(children); const styles = getStyleComputedProperty(parent); const borderTopWidth = parseFloat(styles.borderTopWidth); const borderLeftWidth = parseFloat(styles.borderLeftWidth); // In cases where the parent is fixed, we must ignore negative scroll in offset calc if (fixedPosition && isHTML) { parentRect.top = Math.max(parentRect.top ?? 0, 0); parentRect.left = Math.max(parentRect.left ?? 0, 0); } const offsets: Offsets = getClientRect({ top: (childrenRect.top ?? 0) - (parentRect.top ?? 0) - borderTopWidth, left: (childrenRect.left ?? 0) - (parentRect.left ?? 0) - borderLeftWidth, width: childrenRect.width, height: childrenRect.height }); offsets.marginTop = 0; offsets.marginLeft = 0; // Subtract margins of documentElement in case it's being used as parent // we do this only on HTML because it's the only element that behaves // differently when margins are applied to it. The margins are included in // the box of the documentElement, in the other cases not. if (isHTML) { const marginTop = parseFloat(styles.marginTop); const marginLeft = parseFloat(styles.marginLeft); if (isNumber(offsets.top)) { offsets.top -= borderTopWidth - marginTop; } if (isNumber(offsets.bottom)) { offsets.bottom -= borderTopWidth - marginTop; } if (isNumber(offsets.left)) { offsets.left -= borderLeftWidth - marginLeft; } if (isNumber(offsets.right)) { offsets.right -= borderLeftWidth - marginLeft; } // Attach marginTop and marginLeft because in some circumstances we may need them offsets.marginTop = marginTop; offsets.marginLeft = marginLeft; } return offsets; }