UNPKG

@nex-ui/react

Version:

🎉 A beautiful, modern, and reliable React component library.

208 lines (205 loc) • 7.16 kB
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 };