{"version":3,"file":"application-DPRJWuge.js","names":[],"sources":["../../../app/frontend/tag-filter/tag-filter.js","../../../app/frontend/entrypoints/application.js"],"sourcesContent":["// Behaviour for the library's tag filter (see home/_tag_filter.html.erb):\n// opens as an action sheet on phones and as a modal from sm up, both driven by\n// the same data-state attribute. Vanilla JS, no framework.\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\n// Matches the sm breakpoint and the .sheet-panel transition in application.css.\nconst DESKTOP = \"(min-width: 40rem)\"\nconst CLOSE_MS = 320\n\nexport function setupTagFilter() {\n const root = document.getElementById(\"tag-filter\")\n if (!root) return\n\n const panel = document.getElementById(\"tag-filter-panel\")\n const trigger = document.getElementById(\"tag-filter-trigger\")\n const search = document.getElementById(\"tag-filter-search\")\n const empty = document.getElementById(\"tag-filter-empty\")\n const items = [...root.querySelectorAll(\"[data-tag-name]\")]\n\n let hideTimer = null\n let scrollY = 0\n\n const isOpen = () => !root.hidden\n\n // A filter left over from last time would hide most of the list, so every\n // opening starts from the full set of tags.\n function resetSearch() {\n if (search) search.value = \"\"\n items.forEach((el) => {\n el.hidden = false\n })\n if (empty) empty.hidden = true\n }\n\n function open() {\n if (isOpen()) return\n clearTimeout(hideTimer)\n resetSearch()\n scrollY = window.scrollY\n root.hidden = false\n // Paint the closed state once, otherwise the panel jumps straight to open.\n void root.offsetHeight\n root.dataset.state = \"open\"\n trigger?.setAttribute(\"aria-expanded\", \"true\")\n document.body.style.overflow = \"hidden\"\n // A phone keyboard would cover the sheet, so only the modal focuses the box.\n if (window.matchMedia(DESKTOP).matches) search?.focus()\n else panel?.focus()\n }\n\n function close() {\n if (!isOpen()) return\n root.dataset.state = \"closed\"\n trigger?.setAttribute(\"aria-expanded\", \"false\")\n document.body.style.overflow = \"\"\n window.scrollTo({ top: scrollY })\n trigger?.focus()\n hideTimer = setTimeout(() => {\n root.hidden = true\n }, CLOSE_MS)\n }\n\n trigger?.addEventListener(\"click\", open)\n\n root.querySelectorAll(\"[data-tag-filter-close]\").forEach((el) => {\n el.addEventListener(\"click\", close)\n })\n\n document.addEventListener(\"keydown\", (event) => {\n if (!isOpen()) return\n\n if (event.key === \"Escape\") {\n event.preventDefault()\n close()\n return\n }\n\n if (event.key !== \"Tab\" || !panel) return\n\n // Keep tabbing inside the sheet while it covers the page.\n const focusable = [...panel.querySelectorAll(FOCUSABLE)].filter(\n (el) => !el.hidden && el.offsetParent !== null\n )\n if (focusable.length === 0) return\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement\n\n if (event.shiftKey && (active === first || active === panel)) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n })\n\n function visibleTags() {\n return items.filter((el) => !el.hidden && el.dataset.tagName)\n }\n\n search?.addEventListener(\"input\", () => {\n const q = search.value.trim().toLowerCase()\n items.forEach((el) => {\n // The \"All stories\" link has no tag name, so it drops out while searching.\n el.hidden = q ? !el.dataset.tagName.includes(q) : false\n })\n if (empty) empty.hidden = q === \"\" || visibleTags().length > 0\n })\n\n // Enter picks the first match, so filtering by keyboard never needs the mouse.\n search?.addEventListener(\"keydown\", (event) => {\n if (event.key !== \"Enter\") return\n event.preventDefault()\n visibleTags()[0]?.click()\n })\n}\n","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport { icon } from './../shared/icons.js'\nimport { setupTagFilter } from './../tag-filter/tag-filter.js'\n\n// Vue app bootstrap — mount islands as needed\n// import App from '~/components/App.vue'\n// const app = createApp(App)\n// app.use(createPinia())\n// app.mount('#app')\n\ndocument.addEventListener('DOMContentLoaded', () => {\n setupMobileNav()\n setupTagFilter()\n})\n\nfunction setupMobileNav(){\n const el = document.getElementById(\"site-header\")\n if (!el) return\n const menu = el.querySelector(\"#mobile-menu\")\n const menuBtn = el.querySelector(\"#menu-toggle\")\n menuBtn?.addEventListener(\"click\", () => {\n const open = menu.classList.toggle(\"hidden\") === false\n menuBtn.setAttribute(\"aria-expanded\", String(open))\n menuBtn.innerHTML = open ? icon(\"x\", 20) : icon(\"list\", 20)\n })\n}"],"mappings":"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"}