diff --git a/README.md b/README.md index 24b8b5a..e855fb2 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,8 @@ Working app with a type-themed UI: Moves / Locations); coloured animated stat bars; defensive type matchups; evolution chain re-parented to the selected game's generation; learnset with per-move power/type/accuracy/PP/effect (era-accurate via `past_values`, - incl. pre-Gen-4 physical/special-by-type); wild encounter locations. + incl. pre-Gen-4 physical/special-by-type), the TM/HM group ordered by + TM number for the selected game; wild encounter locations. - **Game-aware** — abilities gated to Gen 3+ (hidden to Gen 5+); type chart applies Gen 1 / pre-Gen 6 rules. - **Games** — overlay picker with stylised version-colour cover tiles, diff --git a/src/components/MovesList.js b/src/components/MovesList.js index 04df0c8..ec7b211 100644 --- a/src/components/MovesList.js +++ b/src/components/MovesList.js @@ -27,6 +27,15 @@ const METHOD_LABEL = { 'form-change': 'On form change', }; const METHOD_ORDER = ['level-up', 'egg', 'machine', 'tutor', 'form-change']; + +/** Numeric sort key from a TM/HM/TR label like "TM25": TMs, then HMs, then TRs. */ +function tmSortKey(label) { + if (!label) return 1e9; // not resolved yet -> sink to the bottom + const m = String(label).match(/^([A-Z]+)0*(\d+)$/); + if (!m) return 1e9 - 1; + const rank = { TM: 0, HM: 1, TR: 2 }[m[1]] ?? 3; + return rank * 1000 + Number(m[2]); +} const DAMAGE_CLASS = { physical: 'Phys', special: 'Spec', status: 'Stat' }; const idFromUrl = (u) => Number(u.replace(/\/$/, '').split('/').pop()); @@ -141,11 +150,12 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = () 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 fillTm = async () => { + if (!tmCell || m._tm != null || !m.data) return; + const n = await machineNumber(m.data, versionGroupKey); + if (n) { + m._tm = n; + tmCell.textContent = n; } }; @@ -167,7 +177,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(); + await fillTm(); } catch { body.replaceChildren(el('span', { class: 'moverow__pending' }, 'Details unavailable.')); } @@ -182,13 +192,15 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = () el('span', { class: 'moverow__chev', 'aria-hidden': 'true' }, '▾'), ); - list.append(el('div', { class: 'movecard' }, row, body)); + const card = el('div', { class: 'movecard' }, row, body); + m._card = card; + list.append(card); enrichers.push(async () => { try { m.data = m.data || (await getMove(m.id)); fillMeta(meta, forGeneration(m.data, gen, genOfVg)); - fillTm(); + await fillTm(); } catch { meta.replaceChildren(el('span', { class: 'moverow__pending' }, '—')); } @@ -212,6 +224,14 @@ export function MovesList(pokemonMoves, { versionGroupKey, gen = 9, genOfVg = () if (enriched) return; enriched = true; await mapLimit(enrichers, 6, (fn) => fn()); + // TM/HM moves land in whatever order the API returned them; reorder + // to TM01, TM02 … once every number has resolved. + if (method === 'machine') { + moves + .slice() + .sort((a, b) => tmSortKey(a._tm) - tmSortKey(b._tm) || a.name.localeCompare(b.name)) + .forEach((mm) => list.append(mm._card)); + } }; group.addEventListener('toggle', () => group.open && runEnrich()); if (method === 'level-up') runEnrich();