diff --git a/src/router.js b/src/router.js index f5e2522..ac4b38c 100644 --- a/src/router.js +++ b/src/router.js @@ -45,6 +45,9 @@ export function initRouter(host) { const pending = el('div', { class: 'view view--loading' }, 'Loading…'); host.replaceChildren(pending); + // Views manage their own scroll: detail pages jump to the top, the feed + // and search restore where the user left off. + window.scrollTo(0, 0); try { const node = await route.view(match); @@ -64,7 +67,6 @@ export function initRouter(host) { ), ); } - window.scrollTo(0, 0); } rerenderCurrent = render; diff --git a/src/store/ui.js b/src/store/ui.js index 2398b64..ab8c17e 100644 --- a/src/store/ui.js +++ b/src/store/ui.js @@ -9,6 +9,7 @@ export const ui = createStore('pdx.ui', { searchQuery: '', searchScroll: 0, searchTab: 'pokemon', + feedScroll: 0, sort: 'dex', sortDesc: false, filter: 'all', diff --git a/src/styles/layout.css b/src/styles/layout.css index 7909977..e9291a6 100644 --- a/src/styles/layout.css +++ b/src/styles/layout.css @@ -761,6 +761,21 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); font-size: 0.9rem; } +.phero__back { + display: inline-flex; + align-items: center; + gap: 4px; + flex: none; + padding: 8px 14px 8px 11px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.2); + font-size: 1rem; + font-weight: 700; + text-shadow: none; +} +.phero__back:hover { + background: rgba(255, 255, 255, 0.32); +} .phero__num { flex: 1; min-width: 0; diff --git a/src/views/DexGrid.js b/src/views/DexGrid.js index 7eee641..7685ca4 100644 --- a/src/views/DexGrid.js +++ b/src/views/DexGrid.js @@ -108,6 +108,7 @@ export async function DexGrid() { ui.set({ filter: f.key }); chipEls.forEach((c, i) => c.btn.classList.toggle('is-active', FILTERS[i].key === f.key)); paintGrid(); + resetScroll(); }, }, el('span', {}, f.label), @@ -127,6 +128,7 @@ export async function DexGrid() { ui.set({ sort: sortKey, sortDesc }); syncDirBtn(); paintGrid(); + resetScroll(); }, }, ...SORTS.map((s) => el('option', { value: s.key, selected: s.key === sortKey }, s.label)), @@ -140,6 +142,7 @@ export async function DexGrid() { ui.set({ sortDesc }); syncDirBtn(); paintGrid(); + resetScroll(); }, }); function syncDirBtn() { @@ -148,6 +151,12 @@ export async function DexGrid() { } syncDirBtn(); + // Changing the list order/contents makes the old scroll offset meaningless. + function resetScroll() { + ui.set({ feedScroll: 0 }); + window.scrollTo(0, 0); + } + view.append( el( 'header', @@ -262,15 +271,25 @@ export async function DexGrid() { paintGrid(); } + let mountedVG = settings.get().versionGroup; rebuild(); + // Return to where the user last scrolled (e.g. after viewing a Pokémon). + requestAnimationFrame(() => window.scrollTo(0, ui.get().feedScroll || 0)); - const offSettings = settings.subscribe(rebuild); + const offSettings = settings.subscribe((s) => { + if (s.versionGroup !== mountedVG) { + mountedVG = s.versionGroup; + ui.set({ feedScroll: 0 }); + } + rebuild(); + }); const offSelection = selection.subscribe(() => { refreshMeta(); if (filterKey !== 'all' && filterKey !== 'legendary') paintGrid(); else if (sortKey === 'caught') paintGrid(); }); onTeardown(view, () => { + ui.set({ feedScroll: window.scrollY }); offSettings(); offSelection(); });