UNPKG

@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
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