// SPDX-License-Identifier: CC0-1.0 export const scrollFade = (element: HTMLElement, enabled = true) => { const update = () => { const remaining = Math.max( 0, element.scrollHeight - element.clientHeight - element.scrollTop, ); element.style.setProperty("--scroll-fade-top", `${element.scrollTop}px`); element.style.setProperty("--scroll-fade-bottom", `${remaining}px`); }; let cleanup: (() => void) | undefined; const enable = () => { if (cleanup) return; let frame: number | undefined; const scheduleUpdate = () => { if (frame !== undefined) return; frame = requestAnimationFrame(() => { frame = undefined; update(); }); }; const resizeObserver = new ResizeObserver(scheduleUpdate); const mutationObserver = new MutationObserver(scheduleUpdate); resizeObserver.observe(element); mutationObserver.observe(element, { childList: true, subtree: true }); element.addEventListener("scroll", scheduleUpdate, { passive: true }); update(); cleanup = () => { if (frame !== undefined) cancelAnimationFrame(frame); resizeObserver.disconnect(); mutationObserver.disconnect(); element.removeEventListener("scroll", scheduleUpdate); element.style.removeProperty("--scroll-fade-top"); element.style.removeProperty("--scroll-fade-bottom"); }; }; const disable = () => { cleanup?.(); cleanup = undefined; }; if (enabled) enable(); return { update(nextEnabled: boolean) { if (nextEnabled) enable(); else disable(); }, destroy: disable, }; };