feat(modals): click intercept + openModal + fetchAndShow

a[data-modal] clicks push ?modal=<path> to URL and trigger htmx.ajax
into #modal-content with the HX-Modal header. window.openModal exposed
for non-<a> trigger sites (files-overlay row clicks). Race guard via
currentModalPath token. Close/popstate/bootstrap follow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-17 12:00:28 +02:00
parent 8df130a607
commit bc8edbcd50
No known key found for this signature in database

View file

@ -1,6 +1,51 @@
// URL-addressable modal router (see docs/superpowers/specs/2026-05-17-url-addressable-modals-design.md).
// Implementation lands in subsequent tasks; this stub keeps base.html's
// script include from 404'ing during the staged rollout.
// URL-addressable modal router (see spec 2026-05-17-url-addressable-modals).
// Click intercept on a[data-modal] → ?modal=<path> in URL → htmx swap into
// #modal-content → showModal(). Close/popstate/bootstrap in later tasks.
(function () {
"use strict";
let currentModalPath = null; // race-guard against stale swaps
function openModal(path) {
const url = new URL(window.location.href);
url.searchParams.set("modal", path);
history.pushState({ modal: path }, "", url.toString());
fetchAndShow(path);
}
function fetchAndShow(path) {
currentModalPath = path;
if (typeof window.htmx === "undefined") {
console.error("[modal-router] htmx not loaded; cannot fetch modal");
return;
}
window.htmx.ajax("GET", path, {
target: "#modal-content",
swap: "innerHTML",
headers: { "HX-Modal": "1" },
}).then(() => {
// Race guard: if the user clicked again during the fetch, abandon
// this swap; the newer click will win.
if (currentModalPath !== path) return;
const dlg = document.getElementById("modal-container");
if (dlg && !dlg.open) dlg.showModal();
}).catch((err) => {
console.error("[modal-router] fetch failed", err);
});
}
document.addEventListener("click", (event) => {
const link = event.target.closest("a[data-modal]");
if (!link) return;
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
if (event.button !== 0) return;
const href = link.getAttribute("href");
if (!href) return;
event.preventDefault();
openModal(href);
});
// Public API — used by files-overlay.js to open the editor from row clicks
// that aren't a literal <a data-modal> (existing event delegation).
window.openModal = openModal;
})();