"use client" import * as React from "react" import { useThrottledCallback } from "./use-throttled-callback" export interface WindowSizeState { /** * The width of the window's visual viewport in pixels. */ width: number /** * The height of the window's visual viewport in pixels. */ height: number /** * The distance from the top of the visual viewport to the top of the layout viewport. * Particularly useful for handling mobile keyboard appearance. */ offsetTop: number /** * The distance from the left of the visual viewport to the left of the layout viewport. */ offsetLeft: number /** * The scale factor of the visual viewport. * This is useful for scaling elements based on the current zoom level. */ scale: number } /** * Hook that tracks the window's visual viewport dimensions, position, and provides * a CSS transform for positioning elements. * * Uses the Visual Viewport API to get accurate measurements, especially important * for mobile devices where virtual keyboards can change the visible area. * Only updates state when values actually change to optimize performance. * * @returns An object containing viewport properties and a CSS transform string */ export function useWindowSize(): WindowSizeState { const [windowSize, setWindowSize] = React.useState({ width: 0, height: 0, offsetTop: 0, offsetLeft: 0, scale: 0, }) const handleViewportChange = useThrottledCallback(() => { if (typeof window === "undefined") return const vp = window.visualViewport if (!vp) return const { width = 0, height = 0, offsetTop = 0, offsetLeft = 0, scale = 0, } = vp setWindowSize((prevState) => { if ( width === prevState.width && height === prevState.height && offsetTop === prevState.offsetTop && offsetLeft === prevState.offsetLeft && scale === prevState.scale ) { return prevState } return { width, height, offsetTop, offsetLeft, scale } }) }, 200) React.useEffect(() => { const visualViewport = window.visualViewport if (!visualViewport) return visualViewport.addEventListener("resize", handleViewportChange) handleViewportChange() return () => { visualViewport.removeEventListener("resize", handleViewportChange) } }, [handleViewportChange]) return windowSize }