All files / hooks/use-intersection-observer/src utils.ts

92.85% Statements 13/14
80% Branches 12/15
85.71% Functions 6/7
92.85% Lines 13/14

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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116              266x                               36x                                               10x 2x       8x                         8x                   10x                                           121x 1x   120x 23x   97x               7x        
import type { IntersectionEntry } from "./types";
 
/**
 * Check if IntersectionObserver API is supported in the current environment
 * Returns false in SSR environments or browsers without support
 */
export function isIntersectionObserverSupported(): boolean {
  return (
    typeof window !== "undefined" &&
    "IntersectionObserver" in window
  );
}
 
/**
 * Convert a native IntersectionObserverEntry to our IntersectionEntry type
 * Provides a consistent interface with additional convenience properties
 *
 * @param nativeEntry - The native IntersectionObserverEntry from the browser
 * @returns IntersectionEntry with all properties
 */
export function toIntersectionEntry(
  nativeEntry: IntersectionObserverEntry
): IntersectionEntry {
  return {
    entry: nativeEntry,
    isIntersecting: nativeEntry.isIntersecting,
    intersectionRatio: nativeEntry.intersectionRatio,
    target: nativeEntry.target,
    boundingClientRect: nativeEntry.boundingClientRect,
    intersectionRect: nativeEntry.intersectionRect,
    rootBounds: nativeEntry.rootBounds,
    time: nativeEntry.time,
  };
}
 
/**
 * Create an initial IntersectionEntry for SSR or before first observation
 * Used when initialIsIntersecting is true
 *
 * @param isIntersecting - Whether to set initial state as intersecting
 * @param target - Optional target element (null for SSR)
 * @returns A mock IntersectionEntry
 */
export function createInitialEntry(
  isIntersecting: boolean,
  target: Element | null = null
): IntersectionEntry | null {
  if (!isIntersecting) {
    return null;
  }
 
  // Create a placeholder DOMRect for SSR
  const emptyRect: DOMRectReadOnly = {
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    toJSON: () => ({}),
  };
 
  // Create a mock native entry
  const mockNativeEntry = {
    target: target as Element,
    isIntersecting,
    intersectionRatio: isIntersecting ? 1 : 0,
    boundingClientRect: emptyRect,
    intersectionRect: emptyRect,
    rootBounds: null,
    time: typeof performance !== "undefined" ? performance.now() : Date.now(),
  } as IntersectionObserverEntry;
 
  return {
    entry: mockNativeEntry,
    isIntersecting,
    intersectionRatio: isIntersecting ? 1 : 0,
    target: target as Element,
    boundingClientRect: emptyRect,
    intersectionRect: emptyRect,
    rootBounds: null,
    time: mockNativeEntry.time,
  };
}
 
/**
 * Normalize threshold to always be an array
 * Handles both single number and array inputs
 *
 * @param threshold - Single threshold or array of thresholds
 * @returns Array of threshold values
 */
export function normalizeThreshold(
  threshold: number | number[] | undefined
): number[] {
  if (threshold === undefined) {
    return [0];
  }
  if (Array.isArray(threshold)) {
    return threshold;
  }
  return [threshold];
}
 
/**
 * Create a no-op ref callback for SSR environments
 * Returns a function that does nothing when called
 */
export function createNoopRef(): (node: Element | null) => void {
  return () => {
    // No-op for SSR
  };
}