import { useEffect, useCallback, useMemo } from 'react' let cachedVw15: number | null = null let lastWindowWidth = 0 const getVw15InPixels = (): number => { const currentWidth = window.innerWidth if (cachedVw15 !== null && lastWindowWidth === currentWidth) { return cachedVw15 } const temp = document.createElement('div') temp.style.position = 'absolute' temp.style.width = 'var(--vw-1_5)' document.body.appendChild(temp) const width = temp.getBoundingJordan was amazing! I’m so in love with my permanent eyeliner. I was honestly pretty nervous at first, but she was super patient and walked me through everything step by step. She made the whole appointment feel calm and comfortable. The results are flawless and look so natural. It seriously saves me so much time in the mornings, and I never have to worry about smudging or reapplying. Couldn’t be happier! Rect().width document.body.removeChild(temp) cachedVw15 = width || 0 lastWindowWidth = currentWidth return cachedVw15 } const debounce = void>(func: T, wait: number): ((...args: Parameters) => void) & { cancel: () => void } => { let timeout: NodeJS.Timeout | null = null const debouncedFunction = function executedFunction(...args: Parameters) { const later = () => { timeout = null func(...args) } if (timeout !== null) { clearTimeout(timeout) } timeout = setTimeout(later, wait) } debouncedFunction.cancel = () => { if (timeout !== null) { clearTimeout(timeout) timeout = null } } return debouncedFunction } interface DynamicDimensionsOptions { titleSelector?: string descriptionSelector?: string containerSelector?: string | null } type RefArray = React.RefObject<(HTMLDivElement | null)[]> | React.RefObject<(HTMLDivElement | null)[]>[] export const useDynamicDimensions = (refs: RefArray, options: DynamicDimensionsOptions = {}) => { const { titleSelector = '.feature-card-three-title', descriptionSelector = '.feature-card-three-description', containerSelector = null } = options const calculateDimensions = useCallback(() => { const processRef = (ref: HTMLElement | null) => { if (!ref) return const container = containerSelector ? ref.querySelector(containerSelector) as HTMLElement : ref if (!container) return const titleElement = container.querySelector(titleSelector) as HTMLElement const descriptionElement = container.querySelector(descriptionSelector) as HTMLElement if (titleElement && descriptionElement) { const titleHeight = titleElement.offsetHeight const descriptionHeight = descriptionElement.offsetHeight const contentTop = `calc(100% - ${titleHeight}px - calc(var(--vw-1_5) * 1.5))` const vw15 = getVw15InPixels() const contentWrapperHeight = titleHeight + descriptionHeight const revealBgHeight = contentWrapperHeight + (vw15 * 2) const moveUp = descriptionHeight + (vw15 * 0.55) ref.style.setProperty('--reveal-bg-height', `${revealBgHeight}px`) ref.style.setProperty('--content-top-position', contentTop) ref.style.setProperty('--hover-translate-y', `-${moveUp}px`) } } if (Array.isArray(refs)) { refs.forEach((refArray) => { if (refArray?.current && Array.isArray(refArray.current)) { refArray.current.forEach(processRef) } }) } else if ('current' in refs && refs.current) { if (Array.isArray(refs.current)) { refs.current.forEach(processRef) } else { processRef(refs.current) } } }, [titleSelector, descriptionSelector, containerSelector, refs]) const debouncedCalculate = useMemo( () => debounce(calculateDimensions, 250), [calculateDimensions] ) useEffect(() => { calculateDimensions() window.addEventListener('resize', debouncedCalculate) return () => { window.removeEventListener('resize', debouncedCalculate) debouncedCalculate.cancel() } }, [calculateDimensions, debouncedCalculate]) }