@nex-ui/react
Version:
🎉 A beautiful, modern, and reliable React component library.
208 lines (205 loc) • 7.16 kB
JavaScript
import { ownerWindow, ownerDocument } from '@nex-ui/utils';
import { isWebKit } from './utils.mjs';
const isHTMLElement = (element)=>{
/* istanbul ignore next */ if (typeof window === 'undefined') {
return false;
}
return element instanceof HTMLElement;
};
const isSVGElement = (element)=>{
/* istanbul ignore next */ if (typeof window === 'undefined') {
return false;
}
return element instanceof SVGElement;
};
const isElement = (node)=>{
return node instanceof Element;
};
const getElementName = (element)=>{
if (isElement(element)) {
return element.nodeName.toLowerCase();
}
return '#document';
};
const rootElements = new Set([
'html',
'body',
'#document'
]);
const isRootElement = (element)=>{
return rootElements.has(getElementName(element));
};
const transformProperties = [
'transform',
'translate',
'scale',
'rotate',
'perspective'
];
const willChangeValues = [
'transform',
'translate',
'scale',
'rotate',
'perspective',
'filter'
];
const containValues = [
'paint',
'layout',
'strict',
'content'
];
const isContainingBlock = (element)=>{
const webkit = isWebKit();
const win = ownerWindow(element);
const css = win.getComputedStyle(element);
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
// https://drafts.csswg.org/css-transforms-2/#individual-transforms
// istanbul ignore next
return transformProperties.some((value)=>css[value] ? css[value] !== 'none' : false) || (css.containerType ? css.containerType !== 'normal' : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== 'none' : false) || !webkit && (css.filter ? css.filter !== 'none' : false) || willChangeValues.some((value)=>(css.willChange || '').includes(value)) || containValues.some((value)=>(css.contain || '').includes(value));
};
const tableElements = new Set([
'table',
'td',
'th'
]);
const isTableElement = (element)=>{
return tableElements.has(getElementName(element));
};
const isStaticPositioned = (element)=>{
const win = ownerWindow(element);
return win.getComputedStyle(element).position === 'static';
};
const getElementScroll = (element)=>{
if (isElement(element)) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
return {
scrollLeft: element.scrollX,
scrollTop: element.scrollY
};
};
const invalidOverflowDisplayValues = new Set([
'inline',
'contents'
]);
const isOverflowElement = (element)=>{
const win = ownerWindow(element);
const overflowRegex = /(auto|scroll|overlay|hidden|clip)/;
const styles = win.getComputedStyle(element);
return overflowRegex.test(styles.overflow + styles.overflowY + styles.overflowX) && !invalidOverflowDisplayValues.has(styles.display);
};
const toClientRect = (rect)=>{
return {
top: rect.y,
right: rect.x + rect.width,
bottom: rect.y + rect.height,
left: rect.x,
width: rect.width,
height: rect.height,
x: rect.x,
y: rect.y
};
};
const getDimensions = (element)=>{
const css = ownerWindow(element).getComputedStyle(element);
let width = parseFloat(css.width) || 0;
let height = parseFloat(css.height) || 0;
const hasOffset = isHTMLElement(element);
const offsetWidth = hasOffset ? element.offsetWidth : width;
const offsetHeight = hasOffset ? element.offsetHeight : height;
const shouldFallback = Math.round(width) !== offsetWidth || Math.round(height) !== offsetHeight;
/* istanbul ignore next */ if (shouldFallback) {
width = offsetWidth;
height = offsetHeight;
}
return {
width,
height
};
};
const getBoundingClientRect = (element)=>{
const clientRect = element.getBoundingClientRect();
const { width, height } = getDimensions(element);
const rawX = width ? Math.round(clientRect.width) / width : 1;
const rawY = height ? Math.round(clientRect.height) / height : 1;
const scaleX = Number.isFinite(rawX) && rawX >= 0 ? rawX : 1;
const scaleY = Number.isFinite(rawY) && rawY >= 0 ? rawY : 1;
return toClientRect({
width: clientRect.width / scaleX,
height: clientRect.height / scaleY,
x: clientRect.left / scaleX,
y: clientRect.top / scaleY
});
};
const getRectRelativeToViewport = (elementRect, offsetParent)=>{
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
let documentElement;
if (offsetParent.document) {
({ documentElement } = offsetParent.document);
} else {
({ documentElement } = ownerDocument(offsetParent));
}
const offset = {
x: 0,
y: 0
};
if (getElementName(offsetParent) !== 'body' || // istanbul ignore next
isOverflowElement(documentElement)) {
scroll = getElementScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
({ x: offset.x, y: offset.y } = getBoundingClientRect(offsetParent));
} else if (!isStaticPositioned(documentElement)) {
({ x: offset.x, y: offset.y } = documentElement.getBoundingClientRect());
// The scrollbar should be on the viewport, not on the HTML element.
scroll.scrollLeft -= scroll.scrollLeft;
scroll.scrollTop -= scroll.scrollTop;
}
return {
x: elementRect.x - scroll.scrollLeft + offset.x,
y: elementRect.y - scroll.scrollTop + offset.y,
width: elementRect.width,
height: elementRect.height
};
};
const getRectRelativeToOffsetParent = (element, offsetParent)=>{
const { documentElement } = ownerDocument(element);
const elementRect = getBoundingClientRect(element);
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
const offset = {
x: 0,
y: 0
};
// https://drafts.csswg.org/css2/#overflow
// Only when both <body> and <html> have their overflow set to auto will <body> have a scrollbar.
if (getElementName(offsetParent) !== 'body' || // istanbul ignore next
isOverflowElement(documentElement)) {
scroll = getElementScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
({ x: offset.x, y: offset.y } = getBoundingClientRect(offsetParent));
} else if (!isStaticPositioned(documentElement)) {
({ x: offset.x, y: offset.y } = documentElement.getBoundingClientRect());
// The scrollbar should be on the viewport, not on the HTML element.
scroll.scrollLeft -= scroll.scrollLeft;
scroll.scrollTop -= scroll.scrollTop;
}
return {
x: elementRect.x + scroll.scrollLeft - offset.x,
y: elementRect.y + scroll.scrollTop - offset.y,
width: elementRect.width,
height: elementRect.height
};
};
export { getDimensions, getElementName, getRectRelativeToOffsetParent, getRectRelativeToViewport, isContainingBlock, isElement, isHTMLElement, isOverflowElement, isRootElement, isSVGElement, isStaticPositioned, isTableElement, toClientRect };