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 { 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';
|
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 = {
|
const METHOD_LABEL = {
|
||||||
'level-up': 'By level up',
|
'level-up': 'By level up',
|
||||||
machine: 'By TM / HM',
|
machine: 'By TM / HM',
|
||||||
@ -106,8 +123,17 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = ()
|
|||||||
for (const m of moves) {
|
for (const m of moves) {
|
||||||
const meta = el('span', { class: 'moverow__meta' }, el('span', { class: 'moverow__pending' }, '…'));
|
const meta = el('span', { class: 'moverow__meta' }, el('span', { class: 'moverow__pending' }, '…'));
|
||||||
const body = el('div', { class: 'movebody', hidden: true });
|
const body = el('div', { class: 'movebody', hidden: true });
|
||||||
|
const tmCell = method === 'machine' ? el('span', { class: 'moverow__tm' }) : null;
|
||||||
let bodyFilled = false;
|
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(
|
const row = el(
|
||||||
'button',
|
'button',
|
||||||
{
|
{
|
||||||
@ -126,6 +152,7 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = ()
|
|||||||
const v = forGeneration(m.data, gen, genOfVg);
|
const v = forGeneration(m.data, gen, genOfVg);
|
||||||
fillMeta(meta, v);
|
fillMeta(meta, v);
|
||||||
fillBody(body, m.data, v);
|
fillBody(body, m.data, v);
|
||||||
|
fillTm();
|
||||||
} catch {
|
} catch {
|
||||||
body.replaceChildren(el('span', { class: 'moverow__pending' }, 'Details unavailable.'));
|
body.replaceChildren(el('span', { class: 'moverow__pending' }, 'Details unavailable.'));
|
||||||
}
|
}
|
||||||
@ -134,7 +161,7 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = ()
|
|||||||
},
|
},
|
||||||
method === 'level-up'
|
method === 'level-up'
|
||||||
? el('span', { class: 'moverow__lv' }, m.level ? `Lv ${m.level}` : '—')
|
? el('span', { class: 'moverow__lv' }, m.level ? `Lv ${m.level}` : '—')
|
||||||
: null,
|
: tmCell,
|
||||||
el('span', { class: 'moverow__name' }, m.name),
|
el('span', { class: 'moverow__name' }, m.name),
|
||||||
meta,
|
meta,
|
||||||
el('span', { class: 'moverow__chev', 'aria-hidden': 'true' }, '▾'),
|
el('span', { class: 'moverow__chev', 'aria-hidden': 'true' }, '▾'),
|
||||||
@ -146,6 +173,7 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = ()
|
|||||||
try {
|
try {
|
||||||
m.data = m.data || (await getMove(m.id));
|
m.data = m.data || (await getMove(m.id));
|
||||||
fillMeta(meta, forGeneration(m.data, gen, genOfVg));
|
fillMeta(meta, forGeneration(m.data, gen, genOfVg));
|
||||||
|
fillTm();
|
||||||
} catch {
|
} catch {
|
||||||
meta.replaceChildren(el('span', { class: 'moverow__pending' }, '—'));
|
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 getEvolutionChain = (id) => getJSON(`evolution-chain/${id}`);
|
||||||
export const getMove = (idOrName) => getJSON(`move/${idOrName}`);
|
export const getMove = (idOrName) => getJSON(`move/${idOrName}`);
|
||||||
export const getItem = (idOrName) => getJSON(`item/${idOrName}`);
|
export const getItem = (idOrName) => getJSON(`item/${idOrName}`);
|
||||||
|
export const getMachine = (id) => getJSON(`machine/${id}`);
|
||||||
export const getEncounters = (id) => getJSON(`pokemon/${id}/encounters`);
|
export const getEncounters = (id) => getJSON(`pokemon/${id}/encounters`);
|
||||||
|
|
||||||
// Full name indexes for the lookup tabs — one request each, then the
|
// 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)
|
* - locale: reserved for localized names (PokéAPI supports many)
|
||||||
*/
|
*/
|
||||||
export const settings = createStore('pdx.settings', {
|
export const settings = createStore('pdx.settings', {
|
||||||
versionGroup: 'scarlet-violet',
|
versionGroup: 'all', // 'all' = National Dex, newest data, no generation limits
|
||||||
pokedex: null,
|
pokedex: null,
|
||||||
theme: 'system', // system | light | dark | black | sepia
|
theme: 'system', // system | light | dark | black | sepia
|
||||||
accent: 'red', // red | blue | green | amber | violet | rose
|
accent: 'red', // red | blue | green | amber | violet | rose
|
||||||
|
|||||||
@ -1256,6 +1256,13 @@
|
|||||||
color: var(--text-dim);
|
color: var(--text-dim);
|
||||||
font-variant-numeric: tabular-nums;
|
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 {
|
.moverow__name {
|
||||||
flex: 1 1 7rem;
|
flex: 1 1 7rem;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { el, clear } from '../lib/dom.js';
|
import { el, clear } from '../lib/dom.js';
|
||||||
import { getMove } from '../data/api.js';
|
import { getMove } from '../data/api.js';
|
||||||
import { prettify } from '../data/pokedex-resolver.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 { TypeChip } from '../components/TypeChip.js';
|
||||||
import { typeHex } from '../lib/type-color.js';
|
import { typeHex } from '../lib/type-color.js';
|
||||||
|
|
||||||
@ -62,6 +64,30 @@ export async function MoveDetail(id) {
|
|||||||
.join(', ');
|
.join(', ');
|
||||||
if (statChanges) bits.push(statChanges);
|
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(
|
clear(view).append(
|
||||||
el('nav', { class: 'lookup__nav' }, el('a', { class: 'link', href: '#/search' }, '‹ Lookup')),
|
el('nav', { class: 'lookup__nav' }, el('a', { class: 'link', href: '#/search' }, '‹ Lookup')),
|
||||||
el(
|
el(
|
||||||
@ -75,16 +101,7 @@ export async function MoveDetail(id) {
|
|||||||
el('span', { class: 'lookup__class' }, DMG[d.damage_class?.name] || '—'),
|
el('span', { class: 'lookup__class' }, DMG[d.damage_class?.name] || '—'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
el(
|
facts,
|
||||||
'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`),
|
|
||||||
),
|
|
||||||
el(
|
el(
|
||||||
'section',
|
'section',
|
||||||
{ class: 'detail__section' },
|
{ class: 'detail__section' },
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user