import type { Attachment } from 'svelte/attachments'; /** * A row of tabs or chips that scrolls sideways when it is wider than the * screen: the one picked brought into view, `at` naming it so a new pick * brings that one, and the row marked `more-start` or `more-end` while * there is more past either edge, which app.css fades. */ export function scrollRow(at: unknown): Attachment { return (row) => { void at; const edges = () => { row.classList.toggle('more-start', row.scrollLeft > 1); row.classList.toggle('more-end', row.scrollLeft + row.clientWidth < row.scrollWidth - 1); }; const picked = row.querySelector('[aria-current]'); if (picked && row.scrollWidth > row.clientWidth) { const box = row.getBoundingClientRect(); const it = picked.getBoundingClientRect(); row.scrollLeft += it.left - box.left - (box.width - it.width) / 2; } edges(); row.addEventListener('scroll', edges, { passive: true }); const sized = new ResizeObserver(edges); sized.observe(row); return () => { row.removeEventListener('scroll', edges); sized.disconnect(); }; }; } /** * A list that scrolls within a box of its own, as a dialog's: marked * `more-above` or `more-below` while there is more past its top or its * bottom, which app.css fades, and again as what it holds changes. */ export const scrollFades: Attachment = (list) => { const edges = () => { list.classList.toggle('more-above', list.scrollTop > 1); list.classList.toggle('more-below', list.scrollTop + list.clientHeight < list.scrollHeight - 1); }; edges(); list.addEventListener('scroll', edges, { passive: true }); const sized = new ResizeObserver(edges); sized.observe(list); const changed = new MutationObserver(edges); changed.observe(list, { childList: true, subtree: true }); return () => { list.removeEventListener('scroll', edges); sized.disconnect(); changed.disconnect(); }; };