dex/src/views/AbilityDetail.js
chris 262e1a454e Align ability page with move/item; link Team at-a-glance names
- AbilityDetail: "Pokémon with this ability" grid now gets a filter box
  and the same game/generation narrowing as the move and item pages, plus
  a note when the ability postdates the selected game (Libero in
  Ruby/Sapphire -> "introduced in Gen 8").
- TeamView "At a glance": Fastest / Bulkiest names now link to their
  Pokémon pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Ve7HLspzeG2xDPtJQ8vmu
2026-09-09 17:50:54 -04:00

111 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { el, clear } from '../lib/dom.js';
import { loadSnapshot } from '../data/snapshot.js';
import { settings } from '../store/settings.js';
import { prettify } from '../data/pokedex-resolver.js';
import { Sprite } from '../components/Sprite.js';
export async function AbilityDetail(id) {
const view = el('section', { class: 'view lookup' });
const snap = await loadSnapshot();
const a = snap.abilityById.get(id);
if (!a) {
view.append(
el(
'div',
{ class: 'view--error' },
el('h1', {}, 'Unknown ability'),
el('a', { class: 'button', href: '#/search' }, 'Back to lookup'),
),
);
return view;
}
const style = settings.get().spriteStyle;
const vg = settings.get().versionGroup;
const gen = snap.versionGroupByKey.get(vg)?.generation ?? 9;
// All-games: every species with the ability. A specific game: only those
// that existed by its generation.
let mons = a.pokemon.map((pid) => snap.speciesById.get(pid)).filter(Boolean);
if (vg !== 'all') mons = mons.filter((sp) => sp.generation <= gen);
mons.sort((x, y) => x.id - y.id);
const CAP = 400;
const grid = el('div', { class: 'ab-mons' });
function paint(q) {
const qq = (q || '').trim().toLowerCase().replace(/\s+/g, '-');
const shown = qq ? mons.filter((sp) => sp.name.includes(qq)) : mons;
clear(grid);
if (!shown.length) {
grid.append(el('p', { class: 'detail__muted' }, 'No matches.'));
return;
}
for (const sp of shown.slice(0, CAP)) {
grid.append(
el(
'a',
{ class: 'ab-mon', href: `#/pokemon/${sp.id}` },
Sprite(sp.id, { style, alt: sp.name, size: 48 }),
el('span', {}, sp.name.replace(/-/g, ' ')),
),
);
}
if (shown.length > CAP) {
grid.append(
el('p', { class: 'detail__muted' }, `+${shown.length - CAP} more — filter to narrow.`),
);
}
}
paint('');
const filterInput = el('input', {
type: 'search',
class: 'search__input learners__filter',
placeholder: 'Filter Pokémon…',
oninput: (e) => paint(e.target.value),
});
const predatesGame = vg !== 'all' && a.generation > gen;
clear(view).append(
el('nav', { class: 'lookup__nav' }, el('a', { class: 'link', href: '#/search' }, ' Lookup')),
el(
'header',
{ class: 'lookup__head lookup__head--item' },
el(
'div',
{},
el('h1', {}, a.name.replace(/-/g, ' ')),
el('span', { class: 'lookup__class' }, `Introduced Gen ${a.generation}`),
),
),
el(
'section',
{ class: 'detail__section' },
el('h2', {}, 'Effect'),
el('p', {}, a.effect || 'No description available.'),
),
el(
'section',
{ class: 'detail__section' },
el(
'h2',
{},
`Pokémon with this ability${vg === 'all' ? '' : ` in ${prettify(vg)}`} (${mons.length})`,
),
predatesGame
? el(
'p',
{ class: 'detail__muted' },
`Not in ${prettify(vg)} — this ability was introduced in Gen ${a.generation}.`,
)
: null,
mons.length > 24 ? filterInput : null,
mons.length
? grid
: el('p', { class: 'detail__muted' }, 'None on record.'),
),
);
return view;
}