fix(lightbox): slideshow navigation writes browser history
goToSlide() was replaceState()-ing, so paging through the viewer left a single history entry — browser Back skipped every plate you'd looked at. It now pushState()s on each user-driven slide (guarded to fire only when the index actually changes), popstate slides the viewer back/forward without stacking entries, and the first entry on a directly-loaded plate page is tagged with its slug so Back to it is handled in-place. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DZPmxGywFnAhmYJB1eh9fm
This commit is contained in:
+18
-7
@@ -289,9 +289,14 @@
|
||||
preloadNeighbors(lbIdx);
|
||||
}
|
||||
|
||||
function goToSlide(idx, smooth = true) {
|
||||
// smooth — animate the track (false = jump, used when first opening)
|
||||
// record — push a history entry + sync <title>/meta (default follows `smooth`;
|
||||
// popstate passes false so back/forward doesn't stack entries)
|
||||
function goToSlide(idx, smooth = true, record = smooth) {
|
||||
if (!lbTrack) return;
|
||||
lbIdx = Math.max(0, Math.min(idx, lbList.length - 1));
|
||||
const next = Math.max(0, Math.min(idx, lbList.length - 1));
|
||||
const changed = next !== lbIdx;
|
||||
lbIdx = next;
|
||||
lbTrack.style.transition = smooth ? '' : 'none';
|
||||
lbTrack.style.transform = `translateX(-${lbIdx * 100}%)`;
|
||||
if (!smooth) lbTrack.getBoundingClientRect(); // force reflow
|
||||
@@ -302,17 +307,17 @@
|
||||
lbBuildMeta(p);
|
||||
syncThumbs();
|
||||
preloadNeighbors(lbIdx);
|
||||
if (p && smooth) {
|
||||
if (p) {
|
||||
const postTitle = p.title + ' — Roux';
|
||||
const postUrl = new URL(p.url, location.origin).href;
|
||||
document.title = postTitle;
|
||||
history.replaceState({ slug: p.slug }, '', p.url);
|
||||
setMeta('name', 'description', p.description || null);
|
||||
setMeta('property', 'og:title', postTitle);
|
||||
setMeta('property', 'og:description', p.description || null);
|
||||
setMeta('property', 'og:url', postUrl);
|
||||
const canon = document.querySelector('link[rel="canonical"]');
|
||||
if (canon) canon.href = postUrl;
|
||||
if (record && changed) history.pushState({ slug: p.slug }, '', p.url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,15 +445,19 @@
|
||||
// Handle browser back/forward.
|
||||
window.addEventListener('popstate', e => {
|
||||
const slug = e.state && e.state.slug;
|
||||
// Entry points at a plate we already know about → show it in the lightbox now,
|
||||
// no page fetch needed (covers sliding + card-opened plates).
|
||||
if (slug && POSTS.some(p => p.slug === slug)) {
|
||||
// Already in the viewer with this plate in the list → just slide to it.
|
||||
if (lb && lb.dataset.open === 'true') {
|
||||
const i = lbList.findIndex(p => p.slug === slug);
|
||||
if (i !== -1) { goToSlide(i, true, false); return; }
|
||||
}
|
||||
// Otherwise (re)open the viewer at this plate — no page fetch needed.
|
||||
const opened = POSTS.find(p => p.slug === slug);
|
||||
const scoped = POSTS.filter(p => p.issue === opened.issue);
|
||||
lbOpen(slug, scoped.length ? scoped : POSTS);
|
||||
return;
|
||||
}
|
||||
// Otherwise re-render #content for the URL we landed on (also closes the lightbox).
|
||||
// Not a plate → re-render #content for the URL we landed on (also closes the viewer).
|
||||
navigate(location.href, { push: false });
|
||||
});
|
||||
|
||||
@@ -562,6 +571,8 @@
|
||||
const opened = POSTS.find(p => p.slug === window.__ROUX_OPEN_SLUG);
|
||||
const issueId = opened ? opened.issue : null;
|
||||
const scoped = issueId ? POSTS.filter(p => p.issue === issueId) : POSTS;
|
||||
// Tag this first history entry so back-navigation to it is handled by the viewer.
|
||||
if (opened) history.replaceState({ slug: opened.slug }, '', location.href);
|
||||
lbOpen(window.__ROUX_OPEN_SLUG, scoped.length ? scoped : POSTS);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user