All files / components/IntersectionObserver observe.ts

100% Statements 80/80
93.75% Branches 15/16
100% Functions 9/9
100% Lines 80/80

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 811x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11x 11x 11x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 4x 1x 1x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 4x 4x 4x 4x 4x 11x 11x 11x 11x 1x 1x 1x 15x 15x 15x 15x 15x 15x 1x 1x 1x 1x 1x 1x 1x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x  
import type { AnyMap } from "$lib/util/types";
 
export type ObserveCallbackArg = {
  target: Element;
  isIntersecting: boolean;
};
export type ObserveCallback = (arg: ObserveCallbackArg) => void;
 
type ResolvedObserveOptions = {
  once: boolean;
};
export type ObserveOptions = Partial<ResolvedObserveOptions>;
 
const defaultObserveOptions = {
  once: false,
};
 
const resolveOptions = (options: ObserveOptions): ResolvedObserveOptions => ({
  ...defaultObserveOptions,
  ...options,
});
 
export type ObserveCallbacks = AnyMap<Element, ObserveCallback>;
 
const unobserve = (
  observer: IntersectionObserver,
  callbacks: ObserveCallbacks,
  target: Element
) => {
  callbacks.delete(target);
  observer.unobserve(target);
};
 
const observe = (
  observer: IntersectionObserver,
  callbacks: ObserveCallbacks,
  target: Element,
  callback: ObserveCallback,
  options: ObserveOptions = {}
) => {
  const { once } = resolveOptions(options);
 
  let cb = callback;
  if (once) {
    cb = (arg: ObserveCallbackArg) => {
      callback(arg);
      if (arg.isIntersecting && once) unobserve(observer, callbacks, target);
    };
  }
 
  callbacks.set(target, cb);
  observer.observe(target);
};
 
export type ObserverEntry = Pick<IntersectionObserverEntry, "target" | "isIntersecting">;
const getObserverCallback = (callbacks: ObserveCallbacks) => (entries: ObserverEntry[]) => {
  entries.forEach((entry) => {
    const { target, isIntersecting } = entry;
    const callback = callbacks.get(target);
    if (callback) callback({ target, isIntersecting });
  });
};
 
export type RootObserver = {
  unobserve: (target: Element) => void;
  observe: (target: Element, callback: ObserveCallback, options?: ObserveOptions) => void;
};
 
export const getRootObserver = (
  options: IntersectionObserverInit = {}
): RootObserver | undefined => {
  const callbacks = new WeakMap<Element, ObserveCallback>();
  if (typeof IntersectionObserver === "undefined") return;
  const observer = new IntersectionObserver(getObserverCallback(callbacks), options);
  return {
    unobserve: (target) => unobserve(observer, callbacks, target),
    observe: (target, callback, options = {}) =>
      observe(observer, callbacks, target, callback, options),
  };
};