// Public pages: item detail dialog and grid/list toggle. import { h, icon, openDialog } from "./common.js"; import { previewButton } from "./player.js"; // Each ranked list on the page ships its items as JSON next to the markup. const lists = [...document.querySelectorAll("[data-items]")].map((el) => ({ items: JSON.parse(el.querySelector(":scope > .items-data")?.textContent || "[]"), title: el.dataset.listTitle, square: el.classList.contains("aspect-square"), })); function findItem(id) { for (const list of lists) { const idx = list.items.findIndex((it) => it.id === id); if (idx >= 0) return { list, idx }; } return null; } let current = null; function renderDialog(list, idx) { const it = list.items[idx]; const art = it.image ? h("img", { src: it.image, alt: `Artwork for ${it.title}` }) : h("span", { class: "art-fallback" }, icon("image")); const sub = [it.subtitle, it.year].filter(Boolean).join(" ยท "); const topClass = it.rank <= 3 ? ` top${it.rank}` : ""; const meta = it.meta.filter((m) => m.label !== "Production"); const prev = h("button", { type: "button", class: "icon-btn", "aria-label": "Previous", disabled: idx === 0, onclick: () => show(list, idx - 1) }, icon("chevron-left")); const next = h("button", { type: "button", class: "icon-btn", "aria-label": "Next", disabled: idx === list.items.length - 1, onclick: () => show(list, idx + 1) }, icon("chevron-right")); return h("div", { class: "item-dialog-inner" }, h("div", { class: "dlg-art" }, art), h("div", { class: "dlg-body" }, h("button", { type: "button", class: "icon-btn dlg-close", "aria-label": "Close", onclick: () => current?.close() }, icon("x")), h("p", { class: "dlg-rank" + topClass }, h("strong", {}, `#${it.rank}`), h("span", {}, list.title)), h("h2", { class: "dlg-title" }, it.title), sub && h("p", { class: "dlg-sub" }, sub), it.badge && h("span", { class: "badge" }, it.badge), meta.length > 0 && h("dl", { class: "dlg-meta" }, meta.map((m) => [h("dt", {}, m.label), h("dd", {}, m.value)])), it.note && h("section", { class: "dlg-why" }, h("h3", {}, `Why it's #${it.rank}`), h("blockquote", { class: "dlg-note" }, it.note)), it.description && h("section", { class: "dlg-about" }, h("h3", {}, "About"), h("p", { class: "dlg-desc" }, it.description)), h("div", { class: "dlg-actions" }, it.preview && previewButton(`/preview/${it.id}`, `Play a preview of ${it.title}`, "play-btn play-btn--inline"), it.link && h("a", { class: "btn btn--sm", href: it.link, target: "_blank", rel: "noopener noreferrer" }, icon("external"), it.source ? `View on ${it.source}` : "Learn more"), h("div", { class: "dlg-nav" }, prev, next), ), ), ); } function show(list, idx) { const content = renderDialog(list, idx); if (current) { current.replaceChildren(content); } else { current = openDialog(content, { className: "item-dialog" + (list.square ? " is-square" : ""), onClose: () => { current = null; }, }); current.addEventListener("keydown", (e) => { if (e.key === "ArrowLeft") current.querySelector(".dlg-nav button:first-child")?.click(); if (e.key === "ArrowRight") current.querySelector(".dlg-nav button:last-child")?.click(); }); } current.setAttribute("aria-label", `#${list.items[idx].rank} ${list.items[idx].title}`); } document.addEventListener("click", (e) => { const btn = e.target.closest("[data-item]"); if (!btn) return; const found = findItem(Number(btn.dataset.item)); if (found) show(found.list, found.idx); });