feat: grid skeletons on the author and collection detail pages
Both loaders now stream their manga grid instead of blocking on it: the cheap header renders immediately (and still handles 404/401 in the loader) while a MangaGridSkeleton stands in until the grid resolves, then swaps in without a layout shift. Collection detail seeds its optimistic-removal state from the stream via a guarded effect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import MangaCard from '$lib/components/MangaCard.svelte';
|
||||
import MangaGridSkeleton from '$lib/components/MangaGridSkeleton.svelte';
|
||||
import Pager from '$lib/components/Pager.svelte';
|
||||
import ArrowLeft from '@lucide/svelte/icons/arrow-left';
|
||||
import { goto } from '$app/navigation';
|
||||
@@ -7,15 +8,8 @@
|
||||
|
||||
let { data } = $props();
|
||||
const author = $derived(data.author);
|
||||
const mangas = $derived(data.mangas);
|
||||
const total = $derived(data.total);
|
||||
const currentPage = $derived(data.currentPage);
|
||||
const pageSize = $derived(data.pageSize);
|
||||
const totalPages = $derived(
|
||||
total != null && total > 0 ? Math.ceil(total / pageSize) : 1
|
||||
);
|
||||
const rangeStart = $derived(mangas.length === 0 ? 0 : (currentPage - 1) * pageSize + 1);
|
||||
const rangeEnd = $derived((currentPage - 1) * pageSize + mangas.length);
|
||||
|
||||
function goToPage(p: number) {
|
||||
if (p === currentPage) return;
|
||||
@@ -45,28 +39,36 @@
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{#if mangas.length === 0}
|
||||
<p class="status" data-testid="author-no-mangas">
|
||||
No mangas attributed to this author.
|
||||
</p>
|
||||
{:else}
|
||||
{#if total != null}
|
||||
<p class="meta" data-testid="author-shown-of-total">
|
||||
Showing {rangeStart}–{rangeEnd} of {total}
|
||||
{#await data.mangas}
|
||||
<MangaGridSkeleton />
|
||||
{:then result}
|
||||
{@const mangas = result.items}
|
||||
{@const total = result.total}
|
||||
{@const totalPages = total != null && total > 0 ? Math.ceil(total / pageSize) : 1}
|
||||
{@const rangeStart = mangas.length === 0 ? 0 : (currentPage - 1) * pageSize + 1}
|
||||
{@const rangeEnd = (currentPage - 1) * pageSize + mangas.length}
|
||||
{#if mangas.length === 0}
|
||||
<p class="status" data-testid="author-no-mangas">
|
||||
No mangas attributed to this author.
|
||||
</p>
|
||||
{:else}
|
||||
{#if total != null}
|
||||
<p class="meta" data-testid="author-shown-of-total">
|
||||
Showing {rangeStart}–{rangeEnd} of {total}
|
||||
</p>
|
||||
{/if}
|
||||
<ul class="manga-grid" data-testid="author-manga-list">
|
||||
{#each mangas as m (m.id)}
|
||||
<MangaCard manga={m} testid={`author-manga-${m.id}`} />
|
||||
{/each}
|
||||
</ul>
|
||||
<Pager page={currentPage} {totalPages} onChange={goToPage} testid="author-pager" />
|
||||
{/if}
|
||||
<ul class="manga-grid" data-testid="author-manga-list">
|
||||
{#each mangas as m (m.id)}
|
||||
<MangaCard manga={m} testid={`author-manga-${m.id}`} />
|
||||
{/each}
|
||||
</ul>
|
||||
<Pager
|
||||
page={currentPage}
|
||||
{totalPages}
|
||||
onChange={goToPage}
|
||||
testid="author-pager"
|
||||
/>
|
||||
{/if}
|
||||
{:catch}
|
||||
<p class="status" role="alert" data-testid="author-error">
|
||||
Could not load this author's mangas.
|
||||
</p>
|
||||
{/await}
|
||||
|
||||
<style>
|
||||
.back {
|
||||
|
||||
Reference in New Issue
Block a user