From be24904b78543935b212bad14992157e3c0d2fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Sun, 7 Jun 2026 14:52:05 +0200 Subject: [PATCH] Add swipe and keyboard navigation to the pure image zoom overlay Arrow keys and touch swipe now navigate between plates while the zoom is open, keeping the lightbox in sync underneath. goToZoomSlide clamps to list bounds and delegates to goToSlide for thumb/index sync. Co-Authored-By: Claude Sonnet 4.6 --- static/js/app.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/static/js/app.js b/static/js/app.js index 4b0e154..17409ed 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -212,6 +212,7 @@ let lbBuilt = false; let lbReferrer = null; // URL to return to on close let imgZoomOpen = false; + let imgZoomIdx = -1; function lbOpen(slug, scopedList) { lbList = scopedList || POSTS; @@ -350,8 +351,8 @@ document.addEventListener('keydown', e => { if (lb.dataset.open !== 'true') return; if (e.key === 'Escape') { if (imgZoomOpen) closeImgZoom(); else lbClose(); } - if (e.key === 'ArrowLeft') { e.preventDefault(); goToSlide(lbIdx - 1); } - if (e.key === 'ArrowRight') { e.preventDefault(); goToSlide(lbIdx + 1); } + if (e.key === 'ArrowLeft') { e.preventDefault(); imgZoomOpen ? goToZoomSlide(imgZoomIdx - 1) : goToSlide(lbIdx - 1); } + if (e.key === 'ArrowRight') { e.preventDefault(); imgZoomOpen ? goToZoomSlide(imgZoomIdx + 1) : goToSlide(lbIdx + 1); } }); // Touch swipe @@ -477,6 +478,7 @@ function openImgZoom(src) { if (!imgZoom || !imgZoomImg || !src) return; imgZoomImg.src = src; + imgZoomIdx = lbIdx; imgZoom.dataset.open = 'true'; imgZoomOpen = true; document.body.style.overflow = 'hidden'; @@ -489,6 +491,14 @@ if (!lb || lb.dataset.open !== 'true') document.body.style.overflow = ''; } + function goToZoomSlide(idx) { + if (!lbList.length || !imgZoomImg) return; + imgZoomIdx = Math.max(0, Math.min(idx, lbList.length - 1)); + const p = lbList[imgZoomIdx]; + if (p) imgZoomImg.src = p.card || p.thumb || ''; + goToSlide(imgZoomIdx); + } + // Hero "View full image" button const viewFullBtn = document.getElementById('viewFull'); if (viewFullBtn) { @@ -505,6 +515,16 @@ if (imgZoom) { imgZoom.addEventListener('click', e => { if (e.target === imgZoom) closeImgZoom(); }); document.getElementById('imgZoomClose')?.addEventListener('click', closeImgZoom); + + // Swipe to navigate + let zoomTouchX = null; + imgZoom.addEventListener('touchstart', e => { zoomTouchX = e.touches[0].clientX; }, { passive: true }); + imgZoom.addEventListener('touchend', e => { + if (zoomTouchX === null) return; + const dx = e.changedTouches[0].clientX - zoomTouchX; + if (Math.abs(dx) > 50) dx < 0 ? goToZoomSlide(imgZoomIdx + 1) : goToZoomSlide(imgZoomIdx - 1); + zoomTouchX = null; + }); } // Escape when lightbox is NOT open (standalone zoom)