// Mobile filter access: a small floating "Filters" pill (fixed, reachable from // anywhere on the page) that slides the page's existing filter panel up as a bottom // sheet. Desktop is untouched — the pill, scrim and grip are display:none above the // phone breakpoint, so the panel stays inline exactly as before (all styling lives in // the @media(max-width:640px) block in style.css). Shared by compare + calendar. // // initFilterSheet({ panel, label }) // panel — the filter panel element (becomes the sheet on mobile) // label — text on the pill and the sheet header (e.g. "Filters") // returns { setActive(bool), close() } const FUNNEL = ``; export function initFilterSheet({ panel, label = "Filters" }) { if (!panel) return { setActive() {}, close() {} }; // The pill lives in so it floats over the whole page regardless of scroll. const fab = document.createElement("button"); fab.type = "button"; fab.className = "filter-fab"; fab.setAttribute("aria-expanded", "false"); fab.innerHTML = `${FUNNEL}${label}`; const scrim = document.createElement("div"); scrim.className = "filter-scrim"; scrim.hidden = true; // A grab handle + title + close, prepended to the panel; only shown in sheet mode. const grip = document.createElement("div"); grip.className = "filter-sheet-grip"; grip.innerHTML = `` + `${label}` + ``; panel.prepend(grip); document.body.append(scrim, fab); document.body.classList.add("has-filter-sheet"); // scopes the extra bottom padding const isOpen = () => panel.classList.contains("open"); function open() { panel.classList.add("open"); scrim.hidden = false; fab.setAttribute("aria-expanded", "true"); } function close() { panel.classList.remove("open"); scrim.hidden = true; fab.setAttribute("aria-expanded", "false"); } fab.addEventListener("click", () => (isOpen() ? close() : open())); scrim.addEventListener("click", close); grip.querySelector(".fsg-close").addEventListener("click", close); document.addEventListener("keydown", (e) => { if (e.key === "Escape" && isOpen()) close(); }); // The pill only makes sense once the page has data to filter — the panel starts // hidden and the page reveals it. Mirror that onto the pill, and drop the sheet if // the panel goes away. const reflect = () => { fab.classList.toggle("ready", !panel.hidden); if (panel.hidden && isOpen()) close(); }; new MutationObserver(reflect).observe(panel, { attributes: true, attributeFilter: ["hidden"] }); reflect(); return { // Show a dot on the pill when a filter subset is active (so the applied filter is // visible even with the sheet closed). setActive(active) { fab.querySelector(".ff-dot").hidden = !active; }, close, }; }