Pure formatting — no behaviour change. Verified: build passes, all 14 routes render, service worker active, no console errors. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ve7HLspzeG2xDPtJQ8vmu
105 lines
2.9 KiB
JavaScript
105 lines
2.9 KiB
JavaScript
import { el, clear } from '../lib/dom.js';
|
|
import { loadSnapshot } from '../data/snapshot.js';
|
|
import { settings } from '../store/settings.js';
|
|
import { Sprite } from './Sprite.js';
|
|
import { TypeChip } from './TypeChip.js';
|
|
import { dragToClose } from '../lib/sheet-drag.js';
|
|
|
|
let openInstance = null;
|
|
|
|
/**
|
|
* A summonable overlay to pick a Pokémon from the full snapshot list.
|
|
* `onPick(id)` fires per selection; the sheet stays open so you can add
|
|
* several. Dismiss with the backdrop, ✕, or Escape.
|
|
*/
|
|
export async function openPokemonPicker(onPick, { closeAfterPick = false } = {}) {
|
|
if (openInstance) return;
|
|
const snap = await loadSnapshot();
|
|
|
|
const backdrop = el('div', {
|
|
class: 'sheet-backdrop',
|
|
onclick: (e) => {
|
|
if (e.target === backdrop) close();
|
|
},
|
|
});
|
|
const panel = el('div', {
|
|
class: 'gsheet',
|
|
role: 'dialog',
|
|
'aria-modal': 'true',
|
|
'aria-label': 'Add a Pokémon',
|
|
});
|
|
|
|
const onKey = (e) => {
|
|
if (e.key === 'Escape') close();
|
|
};
|
|
function close() {
|
|
document.removeEventListener('keydown', onKey);
|
|
backdrop.classList.remove('is-open');
|
|
setTimeout(() => {
|
|
backdrop.remove();
|
|
openInstance = null;
|
|
}, 220);
|
|
}
|
|
|
|
const results = el('div', { class: 'picker__list' });
|
|
const input = el('input', {
|
|
class: 'search__input',
|
|
type: 'search',
|
|
placeholder: 'Name, number or type…',
|
|
autofocus: true,
|
|
oninput: (e) => run(e.target.value),
|
|
});
|
|
|
|
function run(raw) {
|
|
const q = raw.trim().toLowerCase();
|
|
clear(results);
|
|
if (!q) return;
|
|
const style = settings.get().spriteStyle;
|
|
const matches = snap.species
|
|
.filter(
|
|
(s) =>
|
|
s.name.replace(/-/g, ' ').includes(q) ||
|
|
String(s.id) === q ||
|
|
(s.types || []).some((t) => t === q),
|
|
)
|
|
.slice(0, 50);
|
|
for (const s of matches) {
|
|
results.append(
|
|
el(
|
|
'button',
|
|
{
|
|
class: 'picker__row',
|
|
type: 'button',
|
|
onclick: () => {
|
|
onPick(s.id);
|
|
if (closeAfterPick) close();
|
|
},
|
|
},
|
|
Sprite(s.id, { style, alt: s.name, size: 44 }),
|
|
el('span', { class: 'search__num' }, `#${String(s.id).padStart(4, '0')}`),
|
|
el('span', { class: 'search__name' }, s.name.replace(/-/g, ' ')),
|
|
el('span', { class: 'search__types' }, ...(s.types || []).map(TypeChip)),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
const head = el(
|
|
'div',
|
|
{ class: 'gsheet__head' },
|
|
el('h2', {}, 'Add a Pokémon'),
|
|
el(
|
|
'button',
|
|
{ class: 'gsheet__close', type: 'button', 'aria-label': 'Close', onclick: close },
|
|
'✕',
|
|
),
|
|
);
|
|
dragToClose(panel, head, close);
|
|
panel.append(head, input, results);
|
|
backdrop.append(panel);
|
|
document.body.append(backdrop);
|
|
document.addEventListener('keydown', onKey);
|
|
openInstance = backdrop;
|
|
requestAnimationFrame(() => backdrop.classList.add('is-open'));
|
|
}
|