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:
parent
8df130a607
commit
bc8edbcd50
1 changed files with 48 additions and 3 deletions
|
|
@ -1,6 +1,51 @@
|
||||||
// URL-addressable modal router (see docs/superpowers/specs/2026-05-17-url-addressable-modals-design.md).
|
// URL-addressable modal router (see spec 2026-05-17-url-addressable-modals).
|
||||||
// Implementation lands in subsequent tasks; this stub keeps base.html's
|
// Click intercept on a[data-modal] → ?modal=<path> in URL → htmx swap into
|
||||||
// script include from 404'ing during the staged rollout.
|
// #modal-content → showModal(). Close/popstate/bootstrap in later tasks.
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"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;
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue