docs: click-to-zoom lightbox for diagrams (scroll-zoom + drag-pan)
Graphviz renders SVG as <object>, which swallows clicks and can't be lightboxed. Vendored, dependency-free zoom.js converts each graphviz <object> to a clickable <img> (crisp vector) and adds a fullscreen overlay: click to open, scroll to zoom toward the cursor, drag to pan, Esc / dbl-click / background-click to close. Works for any img.zoomable, so future charts get it free. Degrades gracefully (no JS -> inline image still renders). Wired via html_css_files / html_js_files.
This commit is contained in:
parent
5dd9c18f79
commit
2a37c6ec51
3 changed files with 160 additions and 0 deletions
112
docs/_source/_static/zoom.js
Normal file
112
docs/_source/_static/zoom.js
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/* Click-to-zoom lightbox for docs diagrams. Dependency-free.
|
||||
*
|
||||
* Sphinx renders graphviz SVG as <object>, which swallows clicks and
|
||||
* can't be lightboxed. We replace each with an <img> (crisp vector,
|
||||
* clickable), then a click opens a fullscreen overlay with
|
||||
* scroll-to-zoom + drag-to-pan so dense diagrams can be read in detail.
|
||||
* Esc / double-click / background-click closes. Degrades gracefully:
|
||||
* if JS is off, the inline image still renders. */
|
||||
(function () {
|
||||
function ready(fn) {
|
||||
if (document.readyState !== "loading") fn();
|
||||
else document.addEventListener("DOMContentLoaded", fn);
|
||||
}
|
||||
|
||||
ready(function () {
|
||||
// 1. graphviz <object> SVG -> clickable <img>
|
||||
document
|
||||
.querySelectorAll('.graphviz object[type="image/svg+xml"]')
|
||||
.forEach(function (obj) {
|
||||
var img = document.createElement("img");
|
||||
img.src = obj.getAttribute("data");
|
||||
img.className = "zoomable";
|
||||
img.alt = "diagram — click to zoom";
|
||||
obj.parentNode.replaceChild(img, obj);
|
||||
});
|
||||
|
||||
// 2. overlay scaffold
|
||||
var ov = document.createElement("div");
|
||||
ov.id = "arb-zoom-overlay";
|
||||
var big = document.createElement("img");
|
||||
ov.appendChild(big);
|
||||
var hint = document.createElement("div");
|
||||
hint.id = "arb-zoom-hint";
|
||||
hint.textContent = "scroll = zoom · drag = pan · Esc / dbl-click = close";
|
||||
document.body.appendChild(ov);
|
||||
document.body.appendChild(hint);
|
||||
|
||||
var scale = 1, tx = 0, ty = 0;
|
||||
var dragging = false, moved = false, sx = 0, sy = 0;
|
||||
|
||||
function apply() {
|
||||
big.style.transform =
|
||||
"translate(" + tx + "px," + ty + "px) scale(" + scale + ")";
|
||||
}
|
||||
function open(src) {
|
||||
ov.classList.add("open");
|
||||
big.onload = function () {
|
||||
var s =
|
||||
Math.min(
|
||||
window.innerWidth / big.naturalWidth,
|
||||
window.innerHeight / big.naturalHeight
|
||||
) * 0.95;
|
||||
scale = s > 0 ? s : 1;
|
||||
tx = (window.innerWidth - big.naturalWidth * scale) / 2;
|
||||
ty = (window.innerHeight - big.naturalHeight * scale) / 2;
|
||||
apply();
|
||||
};
|
||||
big.src = src;
|
||||
}
|
||||
function close() {
|
||||
ov.classList.remove("open");
|
||||
big.src = "";
|
||||
}
|
||||
|
||||
document.body.addEventListener("click", function (e) {
|
||||
var t = e.target;
|
||||
if (t && t.classList && t.classList.contains("zoomable")) open(t.src);
|
||||
});
|
||||
|
||||
ov.addEventListener(
|
||||
"wheel",
|
||||
function (e) {
|
||||
e.preventDefault();
|
||||
var f = e.deltaY < 0 ? 1.15 : 1 / 1.15;
|
||||
// zoom toward the cursor
|
||||
tx = e.clientX - (e.clientX - tx) * f;
|
||||
ty = e.clientY - (e.clientY - ty) * f;
|
||||
scale *= f;
|
||||
apply();
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
|
||||
ov.addEventListener("mousedown", function (e) {
|
||||
dragging = true;
|
||||
moved = false;
|
||||
sx = e.clientX - tx;
|
||||
sy = e.clientY - ty;
|
||||
ov.classList.add("grabbing");
|
||||
});
|
||||
window.addEventListener("mousemove", function (e) {
|
||||
if (!dragging) return;
|
||||
moved = true;
|
||||
tx = e.clientX - sx;
|
||||
ty = e.clientY - sy;
|
||||
apply();
|
||||
});
|
||||
window.addEventListener("mouseup", function () {
|
||||
dragging = false;
|
||||
ov.classList.remove("grabbing");
|
||||
});
|
||||
|
||||
// background click (no drag) closes; dbl-click + Esc close
|
||||
ov.addEventListener("click", function (e) {
|
||||
if (e.target === ov && !moved) close();
|
||||
});
|
||||
ov.addEventListener("dblclick", close);
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Escape") close();
|
||||
});
|
||||
});
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue