Games as an overlay sheet; remember the detail tab
- New GameSheet: bottom drop-up overlay (backdrop / grab handle / Esc to dismiss) summoned from the nav 'Games' button or the feed's game pill; picking a game/sub-dex writes settings and the feed updates in place - Removed the #/games route and GamePicker view - Detail page remembers the last-opened tab in a new pdx.ui store and restores it on return; selectTab guards unknown ids Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
404ca7dbbd
commit
5144f760f5
135
src/components/GameSheet.js
Normal file
135
src/components/GameSheet.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import { el, clear } from '../lib/dom.js';
|
||||||
|
import { loadSnapshot } from '../data/snapshot.js';
|
||||||
|
import {
|
||||||
|
versionGroupsByGeneration,
|
||||||
|
dexesForVersionGroup,
|
||||||
|
prettify,
|
||||||
|
} from '../data/pokedex-resolver.js';
|
||||||
|
import { settings } from '../store/settings.js';
|
||||||
|
|
||||||
|
let openInstance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A summonable overlay for picking the game (version group) and, for games
|
||||||
|
* with more than one regional dex, the active sub-dex. Slides up from the
|
||||||
|
* bottom; dismiss with the backdrop, the ✕, or Escape. Selecting writes to
|
||||||
|
* `settings`, which the feed reacts to — no navigation.
|
||||||
|
*/
|
||||||
|
export async function openGameSheet() {
|
||||||
|
if (openInstance) return;
|
||||||
|
const snap = await loadSnapshot();
|
||||||
|
|
||||||
|
const backdrop = el('div', {
|
||||||
|
class: 'sheet-backdrop',
|
||||||
|
onclick: (e) => {
|
||||||
|
if (e.target === backdrop) close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const panel = el('div', {
|
||||||
|
class: 'gsheet',
|
||||||
|
role: 'dialog',
|
||||||
|
'aria-modal': 'true',
|
||||||
|
'aria-label': 'Choose your game',
|
||||||
|
});
|
||||||
|
|
||||||
|
const onKey = (e) => {
|
||||||
|
if (e.key === 'Escape') close();
|
||||||
|
};
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
document.removeEventListener('keydown', onKey);
|
||||||
|
backdrop.classList.remove('is-open');
|
||||||
|
setTimeout(() => {
|
||||||
|
backdrop.remove();
|
||||||
|
openInstance = null;
|
||||||
|
}, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
const subdex = el('div', { class: 'gsheet__subdex' });
|
||||||
|
const list = el('div', { class: 'gsheet__list' });
|
||||||
|
|
||||||
|
function renderSubdex() {
|
||||||
|
const st = settings.get();
|
||||||
|
const dexes = dexesForVersionGroup(snap, st.versionGroup);
|
||||||
|
clear(subdex);
|
||||||
|
if (dexes.length <= 1) return;
|
||||||
|
subdex.append(el('span', { class: 'gsheet__label' }, 'Pokédex'));
|
||||||
|
for (const dex of dexes) {
|
||||||
|
const active = (st.pokedex || dexes[0].key) === dex.key;
|
||||||
|
subdex.append(
|
||||||
|
el(
|
||||||
|
'button',
|
||||||
|
{
|
||||||
|
class: `feed-chip${active ? ' is-active' : ''}`,
|
||||||
|
type: 'button',
|
||||||
|
onclick: () => {
|
||||||
|
settings.set({ pokedex: dex.key });
|
||||||
|
close();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prettify(dex.name || dex.key),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGames() {
|
||||||
|
const st = settings.get();
|
||||||
|
clear(list);
|
||||||
|
for (const { generation, versionGroups } of versionGroupsByGeneration(snap)) {
|
||||||
|
list.append(
|
||||||
|
el('h3', { class: 'gsheet__gen' }, `Gen ${generation.id} · ${generation.name}`),
|
||||||
|
);
|
||||||
|
const grid = el('div', { class: 'gsheet__grid' });
|
||||||
|
for (const vg of versionGroups) {
|
||||||
|
const active = vg.key === st.versionGroup;
|
||||||
|
const dexCount = dexesForVersionGroup(snap, vg.key).length;
|
||||||
|
grid.append(
|
||||||
|
el(
|
||||||
|
'button',
|
||||||
|
{
|
||||||
|
class: `gamecard${active ? ' is-active' : ''}`,
|
||||||
|
type: 'button',
|
||||||
|
onclick: () => {
|
||||||
|
settings.set({ versionGroup: vg.key, pokedex: null });
|
||||||
|
if (dexesForVersionGroup(snap, vg.key).length <= 1) {
|
||||||
|
close();
|
||||||
|
} else {
|
||||||
|
renderGames();
|
||||||
|
renderSubdex();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
el('span', { class: 'gamecard__name' }, prettify(vg.name || vg.key)),
|
||||||
|
el('span', { class: 'gamecard__meta' }, vg.versions.map(prettify).join(' / ')),
|
||||||
|
el('span', { class: 'gamecard__meta' }, `${dexCount} ${dexCount === 1 ? 'dex' : 'dexes'}`),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
list.append(grid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.append(
|
||||||
|
el(
|
||||||
|
'div',
|
||||||
|
{ class: 'gsheet__head' },
|
||||||
|
el('h2', {}, 'Choose your game'),
|
||||||
|
el(
|
||||||
|
'button',
|
||||||
|
{ class: 'gsheet__close', type: 'button', 'aria-label': 'Close', onclick: close },
|
||||||
|
'✕',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subdex,
|
||||||
|
list,
|
||||||
|
);
|
||||||
|
backdrop.append(panel);
|
||||||
|
document.body.append(backdrop);
|
||||||
|
document.addEventListener('keydown', onKey);
|
||||||
|
openInstance = backdrop;
|
||||||
|
|
||||||
|
renderGames();
|
||||||
|
renderSubdex();
|
||||||
|
requestAnimationFrame(() => backdrop.classList.add('is-open'));
|
||||||
|
}
|
||||||
@ -1,25 +1,32 @@
|
|||||||
import { el } from '../lib/dom.js';
|
import { el } from '../lib/dom.js';
|
||||||
|
import { openGameSheet } from './GameSheet.js';
|
||||||
|
|
||||||
const ITEMS = [
|
const ITEMS = [
|
||||||
{ href: '#/', label: 'Dex', icon: '▦', match: (h) => h === '#/' || h === '' || h === '#' },
|
{ label: 'Dex', icon: '▦', href: '#/', match: (h) => h === '#/' || h === '' || h === '#' },
|
||||||
{ href: '#/games', label: 'Games', icon: '◉', match: (h) => h.startsWith('#/games') },
|
{ label: 'Games', icon: '◉', action: openGameSheet },
|
||||||
{ href: '#/search', label: 'Search', icon: '⌕', match: (h) => h.startsWith('#/search') },
|
{ label: 'Search', icon: '⌕', href: '#/search', match: (h) => h.startsWith('#/search') },
|
||||||
{ href: '#/settings', label: 'Settings', icon: '⚙', match: (h) => h.startsWith('#/settings') },
|
{ label: 'Settings', icon: '⚙', href: '#/settings', match: (h) => h.startsWith('#/settings') },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One nav element, styled by CSS into a bottom bar on phones and a sidebar
|
* One nav element, styled by CSS into a bottom bar on phones and a sidebar
|
||||||
* on wide screens. The active item is derived from the hash.
|
* on wide screens. Links derive their active state from the hash; the Games
|
||||||
|
* item summons an overlay instead of navigating.
|
||||||
*/
|
*/
|
||||||
export function Nav() {
|
export function Nav() {
|
||||||
const links = ITEMS.map((item) =>
|
const links = ITEMS.map((item) => {
|
||||||
el(
|
const inner = [
|
||||||
'a',
|
|
||||||
{ class: 'nav__link', href: item.href },
|
|
||||||
el('span', { class: 'nav__icon', 'aria-hidden': 'true' }, item.icon),
|
el('span', { class: 'nav__icon', 'aria-hidden': 'true' }, item.icon),
|
||||||
el('span', { class: 'nav__label' }, item.label),
|
el('span', { class: 'nav__label' }, item.label),
|
||||||
),
|
];
|
||||||
);
|
return item.action
|
||||||
|
? el(
|
||||||
|
'button',
|
||||||
|
{ class: 'nav__link', type: 'button', onclick: () => item.action() },
|
||||||
|
...inner,
|
||||||
|
)
|
||||||
|
: el('a', { class: 'nav__link', href: item.href }, ...inner);
|
||||||
|
});
|
||||||
|
|
||||||
const nav = el(
|
const nav = el(
|
||||||
'nav',
|
'nav',
|
||||||
@ -31,6 +38,7 @@ export function Nav() {
|
|||||||
function sync() {
|
function sync() {
|
||||||
const hash = location.hash || '#/';
|
const hash = location.hash || '#/';
|
||||||
ITEMS.forEach((item, i) => {
|
ITEMS.forEach((item, i) => {
|
||||||
|
if (!item.match) return;
|
||||||
links[i].classList.toggle('is-active', item.match(hash));
|
links[i].classList.toggle('is-active', item.match(hash));
|
||||||
links[i].setAttribute('aria-current', item.match(hash) ? 'page' : 'false');
|
links[i].setAttribute('aria-current', item.match(hash) ? 'page' : 'false');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
import { el } from './lib/dom.js';
|
import { el } from './lib/dom.js';
|
||||||
import { DexGrid } from './views/DexGrid.js';
|
import { DexGrid } from './views/DexGrid.js';
|
||||||
import { PokemonDetail } from './views/PokemonDetail.js';
|
import { PokemonDetail } from './views/PokemonDetail.js';
|
||||||
import { GamePicker } from './views/GamePicker.js';
|
|
||||||
import { SearchView } from './views/SearchView.js';
|
import { SearchView } from './views/SearchView.js';
|
||||||
import { SettingsView } from './views/SettingsView.js';
|
import { SettingsView } from './views/SettingsView.js';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ pattern: /^#?\/?$/, view: () => DexGrid() },
|
{ pattern: /^#?\/?$/, view: () => DexGrid() },
|
||||||
{ pattern: /^#\/pokemon\/(\d+)$/, view: (m) => PokemonDetail(Number(m[1])) },
|
{ pattern: /^#\/pokemon\/(\d+)$/, view: (m) => PokemonDetail(Number(m[1])) },
|
||||||
{ pattern: /^#\/games$/, view: () => GamePicker() },
|
|
||||||
{ pattern: /^#\/search$/, view: () => SearchView() },
|
{ pattern: /^#\/search$/, view: () => SearchView() },
|
||||||
{ pattern: /^#\/settings$/, view: () => SettingsView() },
|
{ pattern: /^#\/settings$/, view: () => SettingsView() },
|
||||||
];
|
];
|
||||||
|
|||||||
9
src/store/ui.js
Normal file
9
src/store/ui.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { createStore } from './createStore.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small view-state that should survive reloads but isn't a user "setting":
|
||||||
|
* e.g. which detail-page tab you last looked at.
|
||||||
|
*/
|
||||||
|
export const ui = createStore('pdx.ui', {
|
||||||
|
detailTab: 'about',
|
||||||
|
});
|
||||||
@ -224,14 +224,17 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 5px 8px 5px 12px;
|
padding: 5px 10px 5px 12px;
|
||||||
|
border: none;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: var(--surface-2);
|
background: var(--surface-2);
|
||||||
color: var(--text-dim);
|
color: var(--text-dim);
|
||||||
|
font: inherit;
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.game-pill:hover {
|
.game-pill:hover {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
@ -1276,3 +1279,114 @@
|
|||||||
left: calc(50% + 110px);
|
left: calc(50% + 110px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Game sheet (overlay) --------------------------- */
|
||||||
|
.sheet-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
.sheet-backdrop.is-open {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.gsheet {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 560px;
|
||||||
|
max-height: 82vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 4px 20px 24px;
|
||||||
|
background: var(--surface);
|
||||||
|
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
transform: translateY(100%);
|
||||||
|
transition: transform 0.24s var(--ease-spring);
|
||||||
|
}
|
||||||
|
.sheet-backdrop.is-open .gsheet {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.sheet-backdrop {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.gsheet {
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
max-height: 78vh;
|
||||||
|
transform: translateY(24px) scale(0.97);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.gsheet {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.gsheet__head {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 0 8px;
|
||||||
|
background: var(--surface);
|
||||||
|
}
|
||||||
|
.gsheet__head::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 38px;
|
||||||
|
height: 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
.gsheet__head h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
.gsheet__close {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--surface-2);
|
||||||
|
color: var(--text-dim);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.gsheet__subdex {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 0 2px;
|
||||||
|
}
|
||||||
|
.gsheet__label {
|
||||||
|
align-self: center;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--text-dim);
|
||||||
|
}
|
||||||
|
.gsheet__list {
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
.gsheet__gen {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: var(--text-dim);
|
||||||
|
margin: 18px 0 8px;
|
||||||
|
}
|
||||||
|
.gsheet__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { settings } from '../store/settings.js';
|
|||||||
import { selection, stats } from '../store/selection.js';
|
import { selection, stats } from '../store/selection.js';
|
||||||
import { Card } from '../components/Card.js';
|
import { Card } from '../components/Card.js';
|
||||||
import { ProgressRing } from '../components/ProgressRing.js';
|
import { ProgressRing } from '../components/ProgressRing.js';
|
||||||
|
import { openGameSheet } from '../components/GameSheet.js';
|
||||||
|
|
||||||
const FILTERS = [
|
const FILTERS = [
|
||||||
{ key: 'all', label: 'All', test: () => true },
|
{ key: 'all', label: 'All', test: () => true },
|
||||||
@ -30,8 +31,8 @@ export async function DexGrid() {
|
|||||||
|
|
||||||
const title = el('h1', {});
|
const title = el('h1', {});
|
||||||
const gamePill = el(
|
const gamePill = el(
|
||||||
'a',
|
'button',
|
||||||
{ class: 'game-pill', href: '#/games' },
|
{ class: 'game-pill', type: 'button', onclick: () => openGameSheet() },
|
||||||
el('span', {}),
|
el('span', {}),
|
||||||
el('span', { class: 'game-pill__chev', 'aria-hidden': 'true' }, '⌄'),
|
el('span', { class: 'game-pill__chev', 'aria-hidden': 'true' }, '⌄'),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,109 +0,0 @@
|
|||||||
import { el, onTeardown } from '../lib/dom.js';
|
|
||||||
import { loadSnapshot } from '../data/snapshot.js';
|
|
||||||
import {
|
|
||||||
versionGroupsByGeneration,
|
|
||||||
dexesForVersionGroup,
|
|
||||||
prettify,
|
|
||||||
} from '../data/pokedex-resolver.js';
|
|
||||||
import { settings } from '../store/settings.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Two-tier selection: pick a game (version group), then — if it has more
|
|
||||||
* than one regional dex — pick which dex is active. Both persist to
|
|
||||||
* settings, so the dex grid resumes here on next launch.
|
|
||||||
*/
|
|
||||||
export async function GamePicker() {
|
|
||||||
const snap = await loadSnapshot();
|
|
||||||
const view = el('section', { class: 'view gamepicker' });
|
|
||||||
|
|
||||||
const subdexBar = el('div', { class: 'gamepicker__subdex' });
|
|
||||||
|
|
||||||
function renderSubdex() {
|
|
||||||
const st = settings.get();
|
|
||||||
const dexes = dexesForVersionGroup(snap, st.versionGroup);
|
|
||||||
subdexBar.replaceChildren();
|
|
||||||
if (dexes.length <= 1) return;
|
|
||||||
|
|
||||||
subdexBar.append(el('span', { class: 'gamepicker__subdex-label' }, 'Dex:'));
|
|
||||||
for (const dex of dexes) {
|
|
||||||
const active = (st.pokedex || dexes[0].key) === dex.key;
|
|
||||||
subdexBar.append(
|
|
||||||
el(
|
|
||||||
'button',
|
|
||||||
{
|
|
||||||
class: `chip${active ? ' is-active' : ''}`,
|
|
||||||
type: 'button',
|
|
||||||
onclick: () => {
|
|
||||||
settings.set({ pokedex: dex.key });
|
|
||||||
renderSubdex();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
prettify(dex.name || dex.key),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderGames() {
|
|
||||||
const st = settings.get();
|
|
||||||
list.replaceChildren();
|
|
||||||
for (const { generation, versionGroups } of versionGroupsByGeneration(snap)) {
|
|
||||||
list.append(el('h2', { class: 'gamepicker__gen' }, `Gen ${generation.id} — ${generation.name}`));
|
|
||||||
const groupWrap = el('div', { class: 'gamepicker__grid' });
|
|
||||||
for (const vg of versionGroups) {
|
|
||||||
const active = vg.key === st.versionGroup;
|
|
||||||
const dexCount = dexesForVersionGroup(snap, vg.key).length;
|
|
||||||
groupWrap.append(
|
|
||||||
el(
|
|
||||||
'button',
|
|
||||||
{
|
|
||||||
class: `gamecard${active ? ' is-active' : ''}`,
|
|
||||||
type: 'button',
|
|
||||||
onclick: () => {
|
|
||||||
settings.set({ versionGroup: vg.key, pokedex: null });
|
|
||||||
renderGames();
|
|
||||||
renderSubdex();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
el('span', { class: 'gamecard__name' }, prettify(vg.name || vg.key)),
|
|
||||||
el(
|
|
||||||
'span',
|
|
||||||
{ class: 'gamecard__meta' },
|
|
||||||
vg.versions.map((v) => prettify(v)).join(' / '),
|
|
||||||
),
|
|
||||||
el(
|
|
||||||
'span',
|
|
||||||
{ class: 'gamecard__meta' },
|
|
||||||
`${dexCount} ${dexCount === 1 ? 'dex' : 'dexes'}`,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
list.append(groupWrap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = el('div', { class: 'gamepicker__list' });
|
|
||||||
|
|
||||||
view.append(
|
|
||||||
el(
|
|
||||||
'header',
|
|
||||||
{ class: 'view__header' },
|
|
||||||
el('h1', {}, 'Choose your game'),
|
|
||||||
el('p', {}, 'Sets which Pokédex, numbering and flavor text the app shows.'),
|
|
||||||
),
|
|
||||||
subdexBar,
|
|
||||||
list,
|
|
||||||
);
|
|
||||||
|
|
||||||
renderGames();
|
|
||||||
renderSubdex();
|
|
||||||
|
|
||||||
const off = settings.subscribe(() => {
|
|
||||||
renderGames();
|
|
||||||
renderSubdex();
|
|
||||||
});
|
|
||||||
onTeardown(view, off);
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
@ -3,6 +3,7 @@ import { loadSnapshot } from '../data/snapshot.js';
|
|||||||
import { resolvePokedex, dexRows, prettify } from '../data/pokedex-resolver.js';
|
import { resolvePokedex, dexRows, prettify } from '../data/pokedex-resolver.js';
|
||||||
import { getPokemon, getSpecies, getEvolutionChain, getEncounters } from '../data/api.js';
|
import { getPokemon, getSpecies, getEvolutionChain, getEncounters } from '../data/api.js';
|
||||||
import { settings } from '../store/settings.js';
|
import { settings } from '../store/settings.js';
|
||||||
|
import { ui } from '../store/ui.js';
|
||||||
import { entry, toggle } from '../store/selection.js';
|
import { entry, toggle } from '../store/selection.js';
|
||||||
import { Sprite } from '../components/Sprite.js';
|
import { Sprite } from '../components/Sprite.js';
|
||||||
import { TypeChip } from '../components/TypeChip.js';
|
import { TypeChip } from '../components/TypeChip.js';
|
||||||
@ -256,14 +257,15 @@ export async function PokemonDetail(nationalId) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
function selectTab(id) {
|
function selectTab(id) {
|
||||||
|
const tab = TABS.find((t) => t.id === id) || TABS[0];
|
||||||
TABS.forEach((t, i) => {
|
TABS.forEach((t, i) => {
|
||||||
const active = t.id === id;
|
const active = t.id === tab.id;
|
||||||
tabButtons[i].classList.toggle('is-active', active);
|
tabButtons[i].classList.toggle('is-active', active);
|
||||||
tabButtons[i].setAttribute('aria-selected', String(active));
|
tabButtons[i].setAttribute('aria-selected', String(active));
|
||||||
});
|
});
|
||||||
const tab = TABS.find((t) => t.id === id);
|
|
||||||
body.replaceChildren(tab.node);
|
body.replaceChildren(tab.node);
|
||||||
if (id === 'stats') animateStats();
|
if (tab.id === 'stats') animateStats();
|
||||||
|
ui.set({ detailTab: tab.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function animateStats() {
|
function animateStats() {
|
||||||
@ -329,7 +331,7 @@ export async function PokemonDetail(nationalId) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
selectTab('about');
|
selectTab(ui.get().detailTab);
|
||||||
|
|
||||||
const off = settings.subscribe(() => syncTrack());
|
const off = settings.subscribe(() => syncTrack());
|
||||||
onTeardown(view, off);
|
onTeardown(view, off);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user