diff --git a/src/components/MovesList.js b/src/components/MovesList.js index 0f45194..65ebcc2 100644 --- a/src/components/MovesList.js +++ b/src/components/MovesList.js @@ -1,7 +1,24 @@ import { el } from '../lib/dom.js'; -import { getMove, mapLimit } from '../data/api.js'; +import { getMove, getMachine, mapLimit } from '../data/api.js'; import { TypeChip } from './TypeChip.js'; +/** Resolve the TM/HM/TR number for a move in a given game, e.g. "TM25". */ +export async function machineNumber(moveData, versionGroupKey) { + const list = moveData.machines || []; + if (!list.length) return null; + const hit = + list.find((x) => x.version_group.name === versionGroupKey) || list[list.length - 1]; + try { + const mc = await getMachine(Number(hit.machine.url.replace(/\/$/, '').split('/').pop())); + const m = mc.item.name.match(/^([a-z]+)0*(\d+)$/i); + return m + ? `${m[1].toUpperCase()}${String(Number(m[2])).padStart(2, '0')}` + : mc.item.name.toUpperCase(); + } catch { + return null; + } +} + const METHOD_LABEL = { 'level-up': 'By level up', machine: 'By TM / HM', @@ -106,8 +123,17 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = () for (const m of moves) { const meta = el('span', { class: 'moverow__meta' }, el('span', { class: 'moverow__pending' }, '…')); const body = el('div', { class: 'movebody', hidden: true }); + const tmCell = method === 'machine' ? el('span', { class: 'moverow__tm' }) : null; let bodyFilled = false; + const fillTm = () => { + if (tmCell && !tmCell.textContent && m.data) { + machineNumber(m.data, versionGroupKey).then((n) => { + if (n) tmCell.textContent = n; + }); + } + }; + const row = el( 'button', { @@ -126,6 +152,7 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = () const v = forGeneration(m.data, gen, genOfVg); fillMeta(meta, v); fillBody(body, m.data, v); + fillTm(); } catch { body.replaceChildren(el('span', { class: 'moverow__pending' }, 'Details unavailable.')); } @@ -134,7 +161,7 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = () }, method === 'level-up' ? el('span', { class: 'moverow__lv' }, m.level ? `Lv ${m.level}` : '—') - : null, + : tmCell, el('span', { class: 'moverow__name' }, m.name), meta, el('span', { class: 'moverow__chev', 'aria-hidden': 'true' }, '▾'), @@ -146,6 +173,7 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = () try { m.data = m.data || (await getMove(m.id)); fillMeta(meta, forGeneration(m.data, gen, genOfVg)); + fillTm(); } catch { meta.replaceChildren(el('span', { class: 'moverow__pending' }, '—')); } diff --git a/src/data/api.js b/src/data/api.js index 017a866..abe7470 100644 --- a/src/data/api.js +++ b/src/data/api.js @@ -26,6 +26,7 @@ export const getSpecies = (idOrName) => getJSON(`pokemon-species/${idOrName}`); export const getEvolutionChain = (id) => getJSON(`evolution-chain/${id}`); export const getMove = (idOrName) => getJSON(`move/${idOrName}`); export const getItem = (idOrName) => getJSON(`item/${idOrName}`); +export const getMachine = (id) => getJSON(`machine/${id}`); export const getEncounters = (id) => getJSON(`pokemon/${id}/encounters`); // Full name indexes for the lookup tabs — one request each, then the diff --git a/src/store/settings.js b/src/store/settings.js index ac7e1bf..0f76b8b 100644 --- a/src/store/settings.js +++ b/src/store/settings.js @@ -11,7 +11,7 @@ import { createStore } from './createStore.js'; * - locale: reserved for localized names (PokéAPI supports many) */ export const settings = createStore('pdx.settings', { - versionGroup: 'scarlet-violet', + versionGroup: 'all', // 'all' = National Dex, newest data, no generation limits pokedex: null, theme: 'system', // system | light | dark | black | sepia accent: 'red', // red | blue | green | amber | violet | rose diff --git a/src/styles/layout.css b/src/styles/layout.css index ab8a443..7909977 100644 --- a/src/styles/layout.css +++ b/src/styles/layout.css @@ -1256,6 +1256,13 @@ color: var(--text-dim); font-variant-numeric: tabular-nums; } +.moverow__tm { + flex: 0 0 3.4rem; + font-weight: 700; + font-size: 0.74rem; + color: var(--accent); + font-variant-numeric: tabular-nums; +} .moverow__name { flex: 1 1 7rem; min-width: 0; diff --git a/src/views/MoveDetail.js b/src/views/MoveDetail.js index 4174abb..f44ba63 100644 --- a/src/views/MoveDetail.js +++ b/src/views/MoveDetail.js @@ -1,6 +1,8 @@ import { el, clear } from '../lib/dom.js'; import { getMove } from '../data/api.js'; import { prettify } from '../data/pokedex-resolver.js'; +import { settings } from '../store/settings.js'; +import { machineNumber } from '../components/MovesList.js'; import { TypeChip } from '../components/TypeChip.js'; import { typeHex } from '../lib/type-color.js'; @@ -62,6 +64,30 @@ export async function MoveDetail(id) { .join(', '); if (statChanges) bits.push(statChanges); + const facts = el( + 'dl', + { class: 'pfacts' }, + fact('Power', d.power ?? '—'), + fact('Accuracy', d.accuracy != null ? `${d.accuracy}%` : '—'), + fact('PP', d.pp ?? '—'), + fact('Priority', d.priority ?? 0), + fact('Introduced', prettify(d.generation.name)), + fact('Learned by', `${d.learned_by_pokemon.length} Pokémon`), + ); + if (d.machines?.length) { + const vg = settings.get().versionGroup; + const machineFact = fact('TM / HM', '…'); + facts.append(machineFact); + const exact = d.machines.some((x) => x.version_group.name === vg); + machineNumber(d, vg).then((label) => { + machineFact.querySelector('dd').textContent = label + ? exact + ? label + : `${label} (latest)` + : '—'; + }); + } + clear(view).append( el('nav', { class: 'lookup__nav' }, el('a', { class: 'link', href: '#/search' }, '‹ Lookup')), el( @@ -75,16 +101,7 @@ export async function MoveDetail(id) { el('span', { class: 'lookup__class' }, DMG[d.damage_class?.name] || '—'), ), ), - el( - 'dl', - { class: 'pfacts' }, - fact('Power', d.power ?? '—'), - fact('Accuracy', d.accuracy != null ? `${d.accuracy}%` : '—'), - fact('PP', d.pp ?? '—'), - fact('Priority', d.priority ?? 0), - fact('Introduced', prettify(d.generation.name)), - fact('Learned by', `${d.learned_by_pokemon.length} Pokémon`), - ), + facts, el( 'section', { class: 'detail__section' },