This repository has no description
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107// SPDX-License-Identifier: AGPL-3.0-or-later
type Callback = (entry: IntersectionObserverEntry) => void;
interface SharedObserverState { observer: IntersectionObserver; callbacks: WeakMap<Element, Set<Callback>>; refCount: number;}
interface ObserverOptions { root?: Element | null; rootMargin?: string; threshold?: number | Array<number>;}
const pool = new Map<string, SharedObserverState>();
function thresholdKey(threshold: number | Array<number> | undefined): string { if (threshold === undefined) return '0'; if (typeof threshold === 'number') return String(threshold); return threshold.join(',');}
function rootKey(root: Element | null | undefined): string { if (!root) return 'document'; const id = ( root as Element & { dataset?: DOMStringMap; } ).dataset?.sharedIoId; if (id) return `el:${id}`; const generated = `gen-${++rootIdCounter}`; ( root as Element & { dataset: DOMStringMap; } ).dataset.sharedIoId = generated; return `el:${generated}`;}
let rootIdCounter = 0;
function buildKey(options: ObserverOptions): string { return `${rootKey(options.root ?? null)}|${options.rootMargin ?? '0px'}|${thresholdKey(options.threshold)}`;}
function getOrCreate(options: ObserverOptions): SharedObserverState { const key = buildKey(options); let state = pool.get(key); if (state) return state; const callbacks = new WeakMap<Element, Set<Callback>>(); const observer = new IntersectionObserver( (entries) => { for (let i = 0; i < entries.length; i++) { const entry = entries[i]; const handlers = callbacks.get(entry.target); if (!handlers) continue; for (const cb of handlers) { try { cb(entry); } catch (error) { console.error('SharedIntersectionObserver callback threw:', error); } } } }, { root: options.root ?? null, rootMargin: options.rootMargin, threshold: Array.isArray(options.threshold) ? options.threshold.slice() : options.threshold, }, ); state = {observer, callbacks, refCount: 0}; pool.set(key, state); return state;}
export function observeIntersection(element: Element, callback: Callback, options: ObserverOptions = {}): () => void { const state = getOrCreate(options); let handlers = state.callbacks.get(element); if (!handlers) { handlers = new Set(); state.callbacks.set(element, handlers); state.observer.observe(element); state.refCount++; } handlers.add(callback); const key = buildKey(options); return () => { const current = pool.get(key); if (!current) return; const set = current.callbacks.get(element); if (!set) return; set.delete(callback); if (set.size === 0) { current.callbacks.delete(element); current.observer.unobserve(element); current.refCount--; if (current.refCount === 0) { current.observer.disconnect(); pool.delete(key); } } };}