TM/HM numbers; default to the National Dex
- Learnset 'By TM / HM' rows show the machine number for the selected game (TM25 / HM05 / TR10), resolved from /machine when the group is expanded - Move detail page gains a 'TM / HM' fact (marked '(latest)' when the move isn't a machine in the currently-selected game) - machineNumber() exported from MovesList and reused by MoveDetail - Fresh installs now open on the National Dex (versionGroup 'all') instead of Scarlet/Violet Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
0f00b50198
commit
0d95f9c93a
@ -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' }, '—'));
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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' },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user