Files
nocodb/packages/nc-gui/utils/domUtils.ts

80 lines
2.9 KiB
TypeScript

const isElementInvisible = (elem: HTMLElement) => {
return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length)
}
/**
* Waits until the smooth scrolling animation completes.
* Supports both `window` scrolling and scrolling inside an element.
*
* @param {HTMLElement | Window} element - The element (or window) to track scrolling on.
* @returns {Promise<void>} Resolves when scrolling has stopped.
*/
const waitForScrollEnd = (element: HTMLElement | Window = window) => {
return new Promise<void>((resolve) => {
// Get initial scroll positions
let lastX = element instanceof Window ? window.scrollX : (element as HTMLElement).scrollLeft
let lastY = element instanceof Window ? window.scrollY : (element as HTMLElement).scrollTop
/**
* Checks if scrolling has stopped by comparing the last and current positions.
* If scrolling continues, it recursively calls itself using `requestAnimationFrame`.
*/
const checkScroll = () => {
const currentX = element instanceof Window ? window.scrollX : (element as HTMLElement).scrollLeft
const currentY = element instanceof Window ? window.scrollY : (element as HTMLElement).scrollTop
// If positions are nearly the same, scrolling has stopped
if (Math.abs(currentX - lastX) < 1 && Math.abs(currentY - lastY) < 1) {
resolve() // Resolve the promise
} else {
// Update last positions and continue checking
lastX = currentX
lastY = currentY
requestAnimationFrame(checkScroll)
}
}
// Small delay to allow smooth scroll animation to begin
setTimeout(() => {
requestAnimationFrame(checkScroll)
}, 50)
})
}
function isScrollbarAlwaysVisible() {
/**
* Guard against cases where `document.body` is temporarily unavailable.
*
* This can happen if:
* - The code runs before <body> is parsed (very early in page lifecycle)
* - During SSR hydration (document exists but <body> not yet attached)
* - Immediately after a route/navigation change, when Vue is tearing down
* the old page and <body> may briefly be null
*
* Without this check, `document.body.appendChild(...)` can throw:
* "Cannot read properties of null (reading 'appendChild')".
*/
if (!document?.body) return false
const testElement = document.createElement('div')
testElement.style.width = '100px'
testElement.style.height = '100px'
testElement.style.overflow = 'scroll'
testElement.style.position = 'absolute'
testElement.style.top = '-9999px'
// Add to DOM temporarily
document.body.appendChild(testElement)
// Check if scrollbar width exists when not hovering/scrolling
const scrollbarWidth = testElement.offsetWidth - testElement.clientWidth
// Clean up
document.body.removeChild(testElement)
// If scrollbar width > 0, scrollbars are likely always visible
return scrollbarWidth > 0
}
export { isElementInvisible, waitForScrollEnd, isScrollbarAlwaysVisible }