[+] Pagination

This commit is contained in:
Azalea
2025-11-14 18:49:56 +08:00
parent d2608472d8
commit 9d30cf1e7d
2 changed files with 102 additions and 5 deletions

View File

@@ -0,0 +1,60 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
export let page: number
export let totalPages: number
const dispatch = createEventDispatcher()
let editing = false
let inputPage: number
function updatePage(newPage: number) {
if (newPage > 0 && newPage <= totalPages) dispatch('updatePage', newPage)
}
function startEditing() {
inputPage = page
editing = true
}
function finishEditing() {
editing = false
if (inputPage !== page) updatePage(inputPage)
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter') finishEditing()
else if (event.key === 'Escape') editing = false
}
</script>
<div class="pagination">
<button on:click={() => updatePage(page - 1)} disabled={page <= 1}>Previous</button>
{#if editing}
<input bind:value={inputPage} on:blur={finishEditing} on:keydown={handleKeydown} min="1" max={totalPages} autofocus/>
{:else}
<span on:click={startEditing} role="button" tabindex="0" on:keydown={(e) => e.key === 'Enter' && startEditing()}>
Page {page} of {totalPages}
</span>
{/if}
<button on:click={() => updatePage(page + 1)} disabled={page >= totalPages}>Next</button>
</div>
<style lang="sass">
.pagination
display: flex
justify-content: center
align-items: center
margin: 1rem 0
gap: 1rem
input
width: 100px
text-align: center
span[role="button"]
cursor: pointer
</style>

View File

@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte";
import { title } from "../libs/ui"; import { title } from "../libs/ui";
import { GAME } from "../libs/sdk"; import { GAME } from "../libs/sdk";
import type { GenericRanking } from "../libs/generalTypes"; import type { GenericRanking } from "../libs/generalTypes";
@@ -8,6 +9,7 @@
import { t } from "../libs/i18n"; import { t } from "../libs/i18n";
import UserCard from "../components/UserCard.svelte"; import UserCard from "../components/UserCard.svelte";
import Tooltip from "../components/Tooltip.svelte"; import Tooltip from "../components/Tooltip.svelte";
import Pagination from "../components/Pagination.svelte";
export let game: GameName = 'mai2'; export let game: GameName = 'mai2';
@@ -15,15 +17,45 @@
let d: { users: GenericRanking[] }; let d: { users: GenericRanking[] };
let error: string | null; let error: string | null;
let page = 1
const perPage = 50
let totalPages = 1
function handleUpdatePage(event: CustomEvent<number>) {
page = event.detail;
const url = new URL(window.location.toString())
url.searchParams.set('page', page.toString())
history.pushState({}, '', url.toString())
window.scrollTo(0, 0)
}
onMount(() => {
const url = new URL(window.location.toString())
const pageParam = url.searchParams.get('page')
if (pageParam) {
page = parseInt(pageParam, 10) || 1
}
window.addEventListener('popstate', () => {
const url = new URL(window.location.toString())
const pageParam = url.searchParams.get('page')
page = parseInt(pageParam, 10) || 1
window.scrollTo(0, 0)
})
})
Promise.all([GAME.ranking(game)]) Promise.all([GAME.ranking(game)])
.then(([users]) => { .then(([users]) => {
console.log(users) d = { users }
d = { users }; totalPages = Math.ceil(users.length / perPage)
}) })
.catch((e) => error = e.message); .catch((e) => error = e.message);
let hoveringUser = ""; let hoveringUser = "";
let hoverLoading = false; let hoverLoading = false;
$: paginatedUsers = d ? d.users.slice((page - 1) * perPage, page * perPage) : []
</script> </script>
<main class="content leaderboard"> <main class="content leaderboard">
@@ -37,8 +69,12 @@
</div> </div>
{#if d} {#if d}
{#if page > 1}
<Pagination {page} {totalPages} on:updatePage={handleUpdatePage} />
{/if}
<div class="leaderboard-container"> <div class="leaderboard-container">
<div class="lb-user" on:mouseenter={() => hoveringUser = d.users[0].username} role="heading" aria-level="2"> <div class="lb-user" on:mouseenter={() => hoveringUser = paginatedUsers[0]?.username} role="heading" aria-level="2">
<span class="rank">{t("Leaderboard.Rank")}</span> <span class="rank">{t("Leaderboard.Rank")}</span>
<span class="name"></span> <span class="name"></span>
<span class="rating">{t("Leaderboard.Rating")}</span> <span class="rating">{t("Leaderboard.Rating")}</span>
@@ -46,7 +82,7 @@
<span class="fc">{t("Leaderboard.FC")}</span> <span class="fc">{t("Leaderboard.FC")}</span>
<span class="ap">{t("Leaderboard.AP")}</span> <span class="ap">{t("Leaderboard.AP")}</span>
</div> </div>
{#each d.users as user, i (user.rank)} {#each paginatedUsers as user, i (user.rank)}
<div class="lb-user" class:alternate={i % 2 === 1} role="listitem" <div class="lb-user" class:alternate={i % 2 === 1} role="listitem"
on:mouseover={() => hoveringUser = user.username} on:focus={() => {}}> on:mouseover={() => hoveringUser = user.username} on:focus={() => {}}>
@@ -70,6 +106,8 @@
{/each} {/each}
</div> </div>
<Pagination {page} {totalPages} on:updatePage={handleUpdatePage} />
<Tooltip triggeredBy=".name" loading={hoverLoading}> <Tooltip triggeredBy=".name" loading={hoverLoading}>
<UserCard username={hoveringUser} {game} setLoading={l => hoverLoading = l} /> <UserCard username={hoveringUser} {game} setLoading={l => hoverLoading = l} />
</Tooltip> </Tooltip>
@@ -132,5 +170,4 @@
&.alternate &.alternate
background-color: vars.$ov-light background-color: vars.$ov-light
</style> </style>