@kubit-ui-web/react-components
Version:
Kubit React Components is a customizable, accessible library of React web components, designed to enhance your application's user experience
119 lines • 5.5 kB
JavaScript
import { getBoundingClientRect } from './getBoundingClientRect';
import { getDocumentRect } from './getDocumentRect';
import { getOverflowAncestors } from './getOverflowAncestors';
import { getParentNode } from './getParentNode';
import { getViewportRect } from './getViewportRect';
import { isClientRectVisualViewportBased, isContainingBlock, isElement, isLastTraversableNode, isOverflowElement, } from './is.utils';
import { getNodeName } from './node';
import { rectToClientRect } from './rectToClientRect';
/**
* Get the client inner rect (with borders)
*/
export const getInnerBoundingClientRect = (element, strategy) => {
const clientRect = getBoundingClientRect(element, strategy === 'fixed');
const top = clientRect.top + element.clientTop;
const left = clientRect.left + element.clientLeft;
const width = element.clientWidth;
const height = element.clientHeight;
const x = left;
const y = top;
return {
width,
height,
x,
y,
};
};
export const getClientRectFromClippingAncestor = (clippingAncestor, strategy) => {
let rect;
if (clippingAncestor === 'viewport') {
rect = getViewportRect(strategy);
}
else if (clippingAncestor === 'document') {
rect = getDocumentRect(window.document.documentElement);
}
else if (isElement(clippingAncestor)) {
rect = getInnerBoundingClientRect(clippingAncestor, strategy);
}
else {
const mutableRect = { ...clippingAncestor };
if (isClientRectVisualViewportBased()) {
mutableRect.x -= window.visualViewport?.offsetLeft || 0;
mutableRect.y -= window.visualViewport?.offsetTop || 0;
}
rect = mutableRect;
}
return rectToClientRect(rect);
};
export const hasFixedPositionAncestor = (element, stopNode) => {
const parentNode = getParentNode(element);
if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
return false;
}
return (window.getComputedStyle(parentNode).position === 'fixed' ||
hasFixedPositionAncestor(parentNode, stopNode));
};
/**
* A "clipping ancestor" is an `overflow` element with the characteristic of
* clipping (or hiding) child elements. This returns all clipping ancestors
* of the given element up the tree.
*/
export const getClippingElementAncestors = (element) => {
let result = getOverflowAncestors(element).filter(el => isElement(el) && getNodeName(el) !== 'body');
let currentContainingBlockComputedStyle = null;
const elementIsFixed = window.getComputedStyle(element).position === 'fixed';
let currentNode = elementIsFixed ? getParentNode(element) : element;
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
const computedStyle = window.getComputedStyle(currentNode);
const currentNodeIsContaining = isContainingBlock(currentNode);
if (!currentNodeIsContaining && computedStyle.position === 'fixed') {
currentContainingBlockComputedStyle = null;
}
const shouldDropCurrentNode = elementIsFixed
? !currentNodeIsContaining && !currentContainingBlockComputedStyle
: (!currentNodeIsContaining &&
computedStyle.position === 'static' &&
!!currentContainingBlockComputedStyle &&
['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position)) ||
(isOverflowElement(currentNode) &&
!currentNodeIsContaining &&
hasFixedPositionAncestor(element, currentNode));
if (shouldDropCurrentNode) {
// Drop non-containing blocks.
result = result.filter(ancestor => ancestor !== currentNode);
}
else {
// Record last containing block for next iteration.
currentContainingBlockComputedStyle = computedStyle;
}
currentNode = getParentNode(currentNode);
}
return result;
};
/**
* Gets the maximum area that the element is visible in due to any number of clipping ancestors.
* A "clipping ancestor" is an `overflow` element with the characteristic of
* clipping (or hiding) child elements. This returns all clipping ancestors
* of the given element up the tree.
*/
export const getClippingRect = ({ element, boundary, rootBoundary, strategy, }) => {
const elementClippingAncestors = boundary === 'clippingAncestors' ? getClippingElementAncestors(element) : [...boundary];
const clippingAncestors = [...elementClippingAncestors, rootBoundary];
const firstClippingAncestor = clippingAncestors[0];
const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
const rect = getClientRectFromClippingAncestor(clippingAncestor, strategy);
accRect.top = Math.max(rect.top, accRect.top);
accRect.right = Math.min(rect.right, accRect.right);
accRect.bottom = Math.min(rect.bottom, accRect.bottom);
accRect.left = Math.max(rect.left, accRect.left);
return accRect;
}, getClientRectFromClippingAncestor(firstClippingAncestor, strategy));
return {
width: clippingRect.right - clippingRect.left,
height: clippingRect.bottom - clippingRect.top,
x: clippingRect.left,
y: clippingRect.top,
};
};
//# sourceMappingURL=getClippinRect.js.map