New lib/anim.js (countUp, fadeSlideIn, segThumb), all reduced-motion aware: - Shared-element morph: tapping a card tags its art with view-transition-name; the detail skeleton now renders the real sprite at hero size (router passes the match, animates the skeleton mount), so the sprite flies from the grid into the hero and the skeleton→content swap is seamless. - Segmented controls (Team / Search / Breeding) get a sliding pill that measures each button and eases between them. - Count-ups: the dex-feed progress number and the detail Stats total tick up. - Detail tab content fades/rises on switch. - Global :active scale-press on buttons/chips/rows, plus soft background/border transitions. - Hero sprite has a slow idle bob. - Router swallows the AbortError when a fast follow-up nav skips an in-flight view transition. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ve7HLspzeG2xDPtJQ8vmu
67 lines
1.9 KiB
JavaScript
67 lines
1.9 KiB
JavaScript
import { el } from '../lib/dom.js';
|
|
import { Sprite } from './Sprite.js';
|
|
import { settings } from '../store/settings.js';
|
|
|
|
const bar = (w, h = 14) =>
|
|
el('span', { class: 'sk-bar', style: `width:${w};height:${h}px` });
|
|
|
|
const facts = () =>
|
|
el(
|
|
'div',
|
|
{ class: 'pfacts' },
|
|
...Array.from({ length: 6 }, () =>
|
|
el('div', { class: 'fact' }, bar('42px', 9), bar('72px', 15)),
|
|
),
|
|
);
|
|
|
|
/** Placeholder shown while a Pokémon detail page loads. */
|
|
export function detailSkeleton(match) {
|
|
const id = match && Number(match[1]);
|
|
// Real sprite in the hero spot (cheap, usually cached) so the tapped
|
|
// card's art can morph straight into it.
|
|
const art = id
|
|
? el(
|
|
'div',
|
|
{ class: 'sk-art sk-art--live', style: 'view-transition-name: pkmn-sprite' },
|
|
Sprite(id, {
|
|
style: settings.get().spriteStyle === 'default' ? 'official' : settings.get().spriteStyle,
|
|
alt: '',
|
|
size: 220,
|
|
}),
|
|
)
|
|
: el('div', { class: 'sk-art' });
|
|
return el(
|
|
'section',
|
|
{ class: 'view pdetail sk' },
|
|
el(
|
|
'div',
|
|
{ class: 'phero sk-hero' },
|
|
el('div', { class: 'phero__top' }, bar('72px', 34), bar('130px', 14), bar('34px', 34)),
|
|
el('div', { class: 'phero__head' }, bar('55%', 30)),
|
|
art,
|
|
),
|
|
el(
|
|
'div',
|
|
{ class: 'psheet' },
|
|
el(
|
|
'div',
|
|
{ class: 'psheet__tabs' },
|
|
...Array.from({ length: 5 }, () => bar('58px', 18)),
|
|
),
|
|
el('div', { class: 'ppanel' }, bar('92%'), bar('84%'), bar('66%'), facts()),
|
|
),
|
|
);
|
|
}
|
|
|
|
/** Placeholder for move / item detail pages. */
|
|
export function lookupSkeleton() {
|
|
return el(
|
|
'section',
|
|
{ class: 'view lookup sk' },
|
|
bar('84px', 16),
|
|
el('div', { class: 'sk-hero sk-hero--sm' }),
|
|
facts(),
|
|
el('div', { class: 'detail__section' }, bar('90px', 16), bar('96%'), bar('70%')),
|
|
);
|
|
}
|