All files / spotlight-tour/src/hooks useViewportSize.ts

100% Statements 14/14
100% Branches 9/9
100% Functions 5/5
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47                                                783x   783x 176x 44x 44x   132x 133x 133x 139x         132x 132x 132x     783x    
import { useState } from "react";
import { useIsomorphicLayoutEffect } from "@usefy/use-isomorphic-layout-effect";
 
/** A viewport size in CSS px. */
export interface ViewportSize {
  width: number;
  height: number;
}
 
/**
 * Track the window's inner size while `active` — used to feed the live
 * viewport box into `computeTooltipPosition`. Commits state only when the
 * size actually changes; returns `null` while inactive or on the server.
 *
 * `resize` is the only listener (scroll never changes the viewport size), so
 * no rAF batching is needed — resize events are low-frequency and the commit
 * is change-gated anyway.
 *
 * @param active - Track while true (typically "the tour is open").
 * @returns The current viewport size, or `null` when inactive/SSR.
 *
 * @internal
 */
export function useViewportSize(active: boolean): ViewportSize | null {
  const [size, setSize] = useState<ViewportSize | null>(null);
 
  useIsomorphicLayoutEffect(() => {
    if (!active || typeof window === "undefined") {
      setSize(null);
      return;
    }
    const update = () => {
      const next = { width: window.innerWidth, height: window.innerHeight };
      setSize((prev) =>
        prev && prev.width === next.width && prev.height === next.height
          ? prev
          : next
      );
    };
    update();
    window.addEventListener("resize", update, { passive: true });
    return () => window.removeEventListener("resize", update);
  }, [active]);
 
  return size;
}