const instances = new WeakMap void>();
/** Initialize navbar overflow fades. Returns cleanup; safe to reinitialize. */
export function initNavbarScroll(navbar: HTMLElement): () => void {
instances.get(navbar)?.();
const row = navbar.querySelector(':scope > .nav-wrapper');
if (!row) return () => {};
const left = document.createElement('span');
const right = document.createElement('span');
left.className = 'navbar-scroll-fade navbar-scroll-fade-left';
right.className = 'navbar-scroll-fade navbar-scroll-fade-right';
left.setAttribute('aria-hidden', 'true');
right.setAttribute('aria-hidden', 'true');
navbar.append(left, right);
const previous = ['--scroll-left-fade', '--scroll-right-fade'].map(name => ({
name, value: navbar.style.getPropertyValue(name), priority: navbar.style.getPropertyPriority(name)
}));
const start = navbar.classList.contains('is-scroll-start');
const end = navbar.classList.contains('is-scroll-end');
let disposed = false;
const update = () => {
if (disposed) return;
const max = Math.max(0, row.scrollWidth - row.clientWidth);
const rtl = getComputedStyle(row).direction === 'rtl';
const offset = Math.min(max, Math.max(0, rtl ? -row.scrollLeft : row.scrollLeft));
const atLeft = max <= 1 || (rtl ? offset >= max - 1 : offset <= 1);
const atRight = max <= 1 || (rtl ? offset <= 1 : offset >= max - 1);
navbar.classList.toggle('is-scroll-start', atLeft);
navbar.classList.toggle('is-scroll-end', atRight);
navbar.style.setProperty('--scroll-left-fade', atLeft ? '0' : '1');
navbar.style.setProperty('--scroll-right-fade', atRight ? '0' : '1');
left.classList.toggle('is-hidden', atLeft);
right.classList.toggle('is-hidden', atRight);
};
const resize = typeof ResizeObserver === 'undefined' ? undefined : new ResizeObserver(update);
const observeSizes = () => {
resize?.disconnect();
resize?.observe(row);
Array.from(row.children).forEach(child => resize?.observe(child));
update();
};
const mutation = new MutationObserver(observeSizes);
mutation.observe(row, { childList: true, subtree: true, characterData: true });
row.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update);
document.fonts?.ready.then(update);
observeSizes();
const dispose = () => {
if (disposed) return;
disposed = true;
row.removeEventListener('scroll', update);
window.removeEventListener('resize', update);
resize?.disconnect();
mutation.disconnect();
left.remove();
right.remove();
navbar.classList.toggle('is-scroll-start', start);
navbar.classList.toggle('is-scroll-end', end);
previous.forEach(({ name, value, priority }) => {
if (value) navbar.style.setProperty(name, value, priority);
else navbar.style.removeProperty(name);
});
if (instances.get(navbar) === dispose) instances.delete(navbar);
};
instances.set(navbar, dispose);
return dispose;
}