feat: improve admin pagination controls
This commit is contained in:
@@ -105,6 +105,10 @@
|
|||||||
if (index >= 0) backupsPage = Math.floor(index / BACKUPS_PAGE_SIZE);
|
if (index >= 0) backupsPage = Math.floor(index / BACKUPS_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setBackupsPage(page) {
|
||||||
|
backupsPage = Math.min(Math.max(0, Number(page) || 0), backupsPageCount - 1);
|
||||||
|
}
|
||||||
|
|
||||||
function warningsText(warnings) {
|
function warningsText(warnings) {
|
||||||
return (warnings || []).filter(Boolean).join("\n");
|
return (warnings || []).filter(Boolean).join("\n");
|
||||||
}
|
}
|
||||||
@@ -336,12 +340,16 @@
|
|||||||
{#if totalArchives > BACKUPS_PAGE_SIZE}
|
{#if totalArchives > BACKUPS_PAGE_SIZE}
|
||||||
<AdminPagination
|
<AdminPagination
|
||||||
meta={paginationMeta()}
|
meta={paginationMeta()}
|
||||||
|
page={backupsPage}
|
||||||
|
pageCount={backupsPageCount}
|
||||||
|
pageLabel={at("page_short", {}, "Стр.")}
|
||||||
|
ofLabel={at("pagination_of", {}, "из")}
|
||||||
|
jumpLabel={at("page_short", {}, "Стр.")}
|
||||||
|
jumpAriaLabel={at("pagination_jump_aria", {}, "Перейти к странице")}
|
||||||
|
goLabel={at("pagination_go", {}, "Перейти")}
|
||||||
prevLabel={at("pagination_prev", {}, "Назад")}
|
prevLabel={at("pagination_prev", {}, "Назад")}
|
||||||
nextLabel={at("pagination_next", {}, "Далее")}
|
nextLabel={at("pagination_next", {}, "Далее")}
|
||||||
prevDisabled={backupsPage <= 0}
|
onPageChange={setBackupsPage}
|
||||||
nextDisabled={backupsPage >= backupsPageCount - 1}
|
|
||||||
onPrev={() => (backupsPage = Math.max(0, backupsPage - 1))}
|
|
||||||
onNext={() => (backupsPage = Math.min(backupsPageCount - 1, backupsPage + 1))}
|
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -15,10 +15,11 @@
|
|||||||
export let onOpenUserCard = () => {};
|
export let onOpenUserCard = () => {};
|
||||||
|
|
||||||
const logsStore = getContext("logsStore");
|
const logsStore = getContext("logsStore");
|
||||||
|
const LOGS_PAGE_SIZE = 50;
|
||||||
|
|
||||||
$: ({ logs, logsTotal, logsPage, logsUserFilter, logsLoading } = $logsStore);
|
$: ({ logs, logsTotal, logsPage, logsUserFilter, logsLoading } = $logsStore);
|
||||||
|
|
||||||
$: logsHasMore = logs.length > 0 && logsTotal > (logsPage + 1) * 50; // 50 is LOGS_PAGE_SIZE
|
$: logsPageCount = Math.max(1, Math.ceil(Number(logsTotal || 0) / LOGS_PAGE_SIZE));
|
||||||
$: logHeaders = [
|
$: logHeaders = [
|
||||||
at("date", {}, "Дата"),
|
at("date", {}, "Дата"),
|
||||||
at("event", {}, "Событие"),
|
at("event", {}, "Событие"),
|
||||||
@@ -165,17 +166,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AdminPagination
|
<AdminPagination
|
||||||
meta={`${at("page_short", {}, "Стр.")} ${logsPage + 1}`}
|
page={logsPage}
|
||||||
|
pageCount={logsPageCount}
|
||||||
|
total={logsTotal}
|
||||||
|
pageLabel={at("page_short", {}, "Стр.")}
|
||||||
|
ofLabel={at("pagination_of", {}, "из")}
|
||||||
|
totalLabel={at("total", {}, "Всего")}
|
||||||
|
jumpLabel={at("page_short", {}, "Стр.")}
|
||||||
|
jumpAriaLabel={at("pagination_jump_aria", {}, "Перейти к странице")}
|
||||||
|
goLabel={at("pagination_go", {}, "Перейти")}
|
||||||
prevLabel={at("back", {}, "Назад")}
|
prevLabel={at("back", {}, "Назад")}
|
||||||
nextLabel={at("next", {}, "Далее")}
|
nextLabel={at("next", {}, "Далее")}
|
||||||
prevDisabled={logsPage === 0}
|
onPageChange={(page) => logsStore.setPage(page)}
|
||||||
nextDisabled={!logsHasMore}
|
|
||||||
onPrev={() => {
|
|
||||||
logsStore.setPage(Math.max(0, logsPage - 1));
|
|
||||||
}}
|
|
||||||
onNext={() => {
|
|
||||||
logsStore.setPage(logsPage + 1);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -17,10 +17,11 @@
|
|||||||
export let onOpenUserCard = () => {};
|
export let onOpenUserCard = () => {};
|
||||||
|
|
||||||
const paymentsStore = getContext("paymentsStore");
|
const paymentsStore = getContext("paymentsStore");
|
||||||
|
const PAYMENTS_PAGE_SIZE = 25;
|
||||||
|
|
||||||
$: ({ payments, paymentsTotal, paymentsPage, paymentsLoading } = $paymentsStore);
|
$: ({ payments, paymentsTotal, paymentsPage, paymentsLoading } = $paymentsStore);
|
||||||
|
|
||||||
$: paymentsHasMore = payments.length > 0 && paymentsTotal > (paymentsPage + 1) * 25; // 25 is PAYMENTS_PAGE_SIZE
|
$: paymentsPageCount = Math.max(1, Math.ceil(Number(paymentsTotal || 0) / PAYMENTS_PAGE_SIZE));
|
||||||
|
|
||||||
/** @param {number|null|undefined} v */
|
/** @param {number|null|undefined} v */
|
||||||
function formatTrafficGbCell(v) {
|
function formatTrafficGbCell(v) {
|
||||||
@@ -177,17 +178,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AdminPagination
|
<AdminPagination
|
||||||
meta={`${at("page_short", {}, "Стр.")} ${paymentsPage + 1} · ${at("total", {}, "Всего")} ${paymentsTotal}`}
|
page={paymentsPage}
|
||||||
|
pageCount={paymentsPageCount}
|
||||||
|
total={paymentsTotal}
|
||||||
|
pageLabel={at("page_short", {}, "Стр.")}
|
||||||
|
ofLabel={at("pagination_of", {}, "из")}
|
||||||
|
totalLabel={at("total", {}, "Всего")}
|
||||||
|
jumpLabel={at("page_short", {}, "Стр.")}
|
||||||
|
jumpAriaLabel={at("pagination_jump_aria", {}, "Перейти к странице")}
|
||||||
|
goLabel={at("pagination_go", {}, "Перейти")}
|
||||||
prevLabel={at("back", {}, "Назад")}
|
prevLabel={at("back", {}, "Назад")}
|
||||||
nextLabel={at("next", {}, "Далее")}
|
nextLabel={at("next", {}, "Далее")}
|
||||||
prevDisabled={paymentsPage === 0}
|
onPageChange={(page) => paymentsStore.setPage(page)}
|
||||||
nextDisabled={!paymentsHasMore}
|
|
||||||
onPrev={() => {
|
|
||||||
paymentsStore.setPage(Math.max(0, paymentsPage - 1));
|
|
||||||
}}
|
|
||||||
onNext={() => {
|
|
||||||
paymentsStore.setPage(paymentsPage + 1);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -129,11 +129,14 @@
|
|||||||
userLogsPageSize,
|
userLogsPageSize,
|
||||||
} = $usersStore);
|
} = $usersStore);
|
||||||
|
|
||||||
$: userLogsHasMore =
|
$: userLogsPageCount = Math.max(
|
||||||
Number(userLogsTotal || 0) > (Number(userLogsPage || 0) + 1) * Number(userLogsPageSize || 20);
|
1,
|
||||||
$: userReferralsHasMore =
|
Math.ceil(Number(userLogsTotal || 0) / Number(userLogsPageSize || 20))
|
||||||
Number(userReferralsTotal || 0) >
|
);
|
||||||
(Number(userReferralsPage || 0) + 1) * Number(userReferralsPageSize || 25);
|
$: userReferralsPageCount = Math.max(
|
||||||
|
1,
|
||||||
|
Math.ceil(Number(userReferralsTotal || 0) / Number(userReferralsPageSize || 25))
|
||||||
|
);
|
||||||
|
|
||||||
$: openedUserAvatarUrl = openedUser ? resolvedAvatarUrl(openedUser) : "";
|
$: openedUserAvatarUrl = openedUser ? resolvedAvatarUrl(openedUser) : "";
|
||||||
$: referralInviter = openedUserDetail?.referral?.inviter || null;
|
$: referralInviter = openedUserDetail?.referral?.inviter || null;
|
||||||
@@ -738,13 +741,19 @@
|
|||||||
|
|
||||||
{#if userLogsLoaded && userLogsTotal > userLogsPageSize}
|
{#if userLogsLoaded && userLogsTotal > userLogsPageSize}
|
||||||
<AdminPagination
|
<AdminPagination
|
||||||
meta={`${at("page_short", {}, "Стр.")} ${userLogsPage + 1}`}
|
page={userLogsPage}
|
||||||
|
pageCount={userLogsPageCount}
|
||||||
|
total={userLogsTotal}
|
||||||
|
pageLabel={at("page_short", {}, "Стр.")}
|
||||||
|
ofLabel={at("pagination_of", {}, "из")}
|
||||||
|
totalLabel={at("total", {}, "Всего")}
|
||||||
|
jumpLabel={at("page_short", {}, "Стр.")}
|
||||||
|
jumpAriaLabel={at("pagination_jump_aria", {}, "Перейти к странице")}
|
||||||
|
goLabel={at("pagination_go", {}, "Перейти")}
|
||||||
prevLabel={at("back", {}, "Назад")}
|
prevLabel={at("back", {}, "Назад")}
|
||||||
nextLabel={at("next", {}, "Далее")}
|
nextLabel={at("next", {}, "Далее")}
|
||||||
prevDisabled={userLogsPage === 0 || userLogsLoading}
|
disabled={userLogsLoading}
|
||||||
nextDisabled={!userLogsHasMore || userLogsLoading}
|
onPageChange={(page) => usersStore.setUserLogsPage(page)}
|
||||||
onPrev={() => usersStore.setUserLogsPage(Math.max(0, userLogsPage - 1))}
|
|
||||||
onNext={() => usersStore.setUserLogsPage(userLogsPage + 1)}
|
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
@@ -1244,20 +1253,19 @@
|
|||||||
|
|
||||||
{#if userReferralsTotal > userReferralsPageSize}
|
{#if userReferralsTotal > userReferralsPageSize}
|
||||||
<AdminPagination
|
<AdminPagination
|
||||||
meta={at(
|
page={userReferralsPage}
|
||||||
"pagination_meta",
|
pageCount={userReferralsPageCount}
|
||||||
{
|
total={userReferralsTotal}
|
||||||
current: userReferralsPage + 1,
|
pageLabel={at("page_short", {}, "Стр.")}
|
||||||
total: Math.max(1, Math.ceil(userReferralsTotal / userReferralsPageSize)),
|
ofLabel={at("pagination_of", {}, "из")}
|
||||||
},
|
totalLabel={at("total", {}, "Всего")}
|
||||||
`${userReferralsPage + 1}/${Math.max(1, Math.ceil(userReferralsTotal / userReferralsPageSize))}`
|
jumpLabel={at("page_short", {}, "Стр.")}
|
||||||
)}
|
jumpAriaLabel={at("pagination_jump_aria", {}, "Перейти к странице")}
|
||||||
|
goLabel={at("pagination_go", {}, "Перейти")}
|
||||||
prevLabel={at("prev_page", {}, "Назад")}
|
prevLabel={at("prev_page", {}, "Назад")}
|
||||||
nextLabel={at("next_page", {}, "Вперёд")}
|
nextLabel={at("next_page", {}, "Вперёд")}
|
||||||
prevDisabled={userReferralsLoading || userReferralsPage <= 0}
|
disabled={userReferralsLoading}
|
||||||
nextDisabled={userReferralsLoading || !userReferralsHasMore}
|
onPageChange={(page) => usersStore.setUserReferralsPage(page)}
|
||||||
onPrev={() => usersStore.setUserReferralsPage(userReferralsPage - 1)}
|
|
||||||
onNext={() => usersStore.setUserReferralsPage(userReferralsPage + 1)}
|
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
const USERS_PAGE_SIZE = 25;
|
const USERS_PAGE_SIZE = 25;
|
||||||
let usersFilterSheetOpen = false;
|
let usersFilterSheetOpen = false;
|
||||||
$: usersHasMore = users.length === USERS_PAGE_SIZE;
|
$: usersPageCount = Math.max(1, Math.ceil(Number(usersTotal || 0) / USERS_PAGE_SIZE));
|
||||||
|
|
||||||
const USERS_FILTER_OPTIONS = [
|
const USERS_FILTER_OPTIONS = [
|
||||||
{ value: "all", label: at("filter_all", {}, "Все") },
|
{ value: "all", label: at("filter_all", {}, "Все") },
|
||||||
@@ -570,17 +570,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AdminPagination
|
<AdminPagination
|
||||||
meta={`${at("page", {}, "Страница")} ${usersPage + 1}`}
|
page={usersPage}
|
||||||
|
pageCount={usersPageCount}
|
||||||
|
total={usersTotal}
|
||||||
|
pageLabel={at("page_short", {}, "Стр.")}
|
||||||
|
ofLabel={at("pagination_of", {}, "из")}
|
||||||
|
totalLabel={at("total", {}, "Всего")}
|
||||||
|
jumpLabel={at("page_short", {}, "Стр.")}
|
||||||
|
jumpAriaLabel={at("pagination_jump_aria", {}, "Перейти к странице")}
|
||||||
|
goLabel={at("pagination_go", {}, "Перейти")}
|
||||||
prevLabel={at("back", {}, "Назад")}
|
prevLabel={at("back", {}, "Назад")}
|
||||||
nextLabel={at("next", {}, "Далее")}
|
nextLabel={at("next", {}, "Далее")}
|
||||||
prevDisabled={usersPage === 0}
|
onPageChange={(page) => {
|
||||||
nextDisabled={!usersHasMore}
|
usersStore.updateState({ usersPage: page });
|
||||||
onPrev={() => {
|
|
||||||
usersStore.updateState({ usersPage: Math.max(0, usersPage - 1) });
|
|
||||||
usersStore.loadUsers();
|
|
||||||
}}
|
|
||||||
onNext={() => {
|
|
||||||
usersStore.updateState({ usersPage: usersPage + 1 });
|
|
||||||
usersStore.loadUsers();
|
usersStore.loadUsers();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,26 +1,198 @@
|
|||||||
<script>
|
<script>
|
||||||
import { ChevronLeft, ChevronRight } from "$components/ui/icons.js";
|
import { ArrowRight, ChevronLeft, ChevronRight } from "$components/ui/icons.js";
|
||||||
import AdminButton from "./AdminButton.svelte";
|
import AdminButton from "./AdminButton.svelte";
|
||||||
|
|
||||||
export let meta = "";
|
export let meta = "";
|
||||||
export let prevLabel = "Back";
|
export let prevLabel = "Back";
|
||||||
export let nextLabel = "Next";
|
export let nextLabel = "Next";
|
||||||
|
export let page = null;
|
||||||
|
export let pageCount = null;
|
||||||
|
export let total = null;
|
||||||
|
export let pageLabel = "Page";
|
||||||
|
export let ofLabel = "of";
|
||||||
|
export let totalLabel = "Total";
|
||||||
|
export let jumpLabel = "Page";
|
||||||
|
export let jumpAriaLabel = "Go to page";
|
||||||
|
export let goLabel = "Go";
|
||||||
|
export let disabled = false;
|
||||||
export let prevDisabled = false;
|
export let prevDisabled = false;
|
||||||
export let nextDisabled = false;
|
export let nextDisabled = false;
|
||||||
export let onPrev = () => {};
|
export let onPrev = () => {};
|
||||||
export let onNext = () => {};
|
export let onNext = () => {};
|
||||||
|
export let onPageChange = null;
|
||||||
|
|
||||||
|
let jumpValue = "";
|
||||||
|
|
||||||
|
$: normalizedPage = Number(page);
|
||||||
|
$: normalizedPageCount = Math.max(1, Math.ceil(Number(pageCount) || 1));
|
||||||
|
$: hasPageNavigation = Number.isFinite(normalizedPage) && typeof onPageChange === "function";
|
||||||
|
$: currentPage = hasPageNavigation
|
||||||
|
? Math.min(Math.max(0, Math.floor(normalizedPage)), normalizedPageCount - 1)
|
||||||
|
: 0;
|
||||||
|
$: pages = hasPageNavigation ? visiblePages(currentPage, normalizedPageCount) : [];
|
||||||
|
$: paginationDisabled = Boolean(disabled);
|
||||||
|
$: computedPrevDisabled =
|
||||||
|
paginationDisabled || prevDisabled || (hasPageNavigation ? currentPage <= 0 : false);
|
||||||
|
$: computedNextDisabled =
|
||||||
|
paginationDisabled ||
|
||||||
|
nextDisabled ||
|
||||||
|
(hasPageNavigation ? currentPage >= normalizedPageCount - 1 : false);
|
||||||
|
$: hasTotal = total !== null && total !== undefined && total !== "";
|
||||||
|
$: totalValue = Number(total);
|
||||||
|
$: showTotal = hasTotal && Number.isFinite(totalValue) && totalValue >= 0;
|
||||||
|
$: jumpTarget = Number(jumpValue);
|
||||||
|
$: canJump =
|
||||||
|
hasPageNavigation &&
|
||||||
|
!paginationDisabled &&
|
||||||
|
jumpValue !== "" &&
|
||||||
|
Number.isFinite(jumpTarget) &&
|
||||||
|
Number.isInteger(jumpTarget) &&
|
||||||
|
jumpTarget >= 1 &&
|
||||||
|
jumpTarget <= normalizedPageCount;
|
||||||
|
|
||||||
|
function visiblePages(activePage, count) {
|
||||||
|
const pageIndexes = new Set([0, count - 1, activePage - 1, activePage, activePage + 1]);
|
||||||
|
|
||||||
|
if (activePage <= 2) {
|
||||||
|
pageIndexes.add(1);
|
||||||
|
pageIndexes.add(2);
|
||||||
|
}
|
||||||
|
if (activePage >= count - 3) {
|
||||||
|
pageIndexes.add(count - 2);
|
||||||
|
pageIndexes.add(count - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = [...pageIndexes]
|
||||||
|
.filter((value) => value >= 0 && value < count)
|
||||||
|
.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
sorted.forEach((value, index) => {
|
||||||
|
const previous = sorted[index - 1];
|
||||||
|
if (index > 0 && value - previous > 1) {
|
||||||
|
result.push({ type: "ellipsis", key: `ellipsis-${previous}-${value}` });
|
||||||
|
}
|
||||||
|
result.push({ type: "page", key: `page-${value}`, index: value, label: value + 1 });
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToPage(nextPage) {
|
||||||
|
if (!hasPageNavigation || paginationDisabled) return;
|
||||||
|
const clamped = Math.min(
|
||||||
|
Math.max(0, Math.floor(Number(nextPage) || 0)),
|
||||||
|
normalizedPageCount - 1
|
||||||
|
);
|
||||||
|
if (clamped === currentPage) return;
|
||||||
|
onPageChange(clamped);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePrev() {
|
||||||
|
if (computedPrevDisabled) return;
|
||||||
|
if (hasPageNavigation) goToPage(currentPage - 1);
|
||||||
|
else onPrev();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNext() {
|
||||||
|
if (computedNextDisabled) return;
|
||||||
|
if (hasPageNavigation) goToPage(currentPage + 1);
|
||||||
|
else onNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitJump() {
|
||||||
|
if (!canJump) return;
|
||||||
|
goToPage(jumpTarget - 1);
|
||||||
|
jumpValue = "";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="admin-pagination">
|
<div class="admin-pagination">
|
||||||
|
<div class="admin-pagination-summary">
|
||||||
|
{#if meta}
|
||||||
<span class="admin-pagination-meta">{meta}</span>
|
<span class="admin-pagination-meta">{meta}</span>
|
||||||
|
{/if}
|
||||||
|
{#if hasPageNavigation}
|
||||||
|
<span class="admin-pagination-count">
|
||||||
|
{pageLabel}
|
||||||
|
{currentPage + 1}
|
||||||
|
{ofLabel}
|
||||||
|
{normalizedPageCount}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{#if showTotal}
|
||||||
|
<span class="admin-pagination-count">{totalLabel} {totalValue}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
<div class="admin-pagination-buttons">
|
<div class="admin-pagination-buttons">
|
||||||
<AdminButton size="sm" disabled={prevDisabled} onclick={onPrev}>
|
<AdminButton
|
||||||
|
class="admin-pagination-nav"
|
||||||
|
size="sm"
|
||||||
|
disabled={computedPrevDisabled}
|
||||||
|
onclick={handlePrev}
|
||||||
|
>
|
||||||
<ChevronLeft size={14} />
|
<ChevronLeft size={14} />
|
||||||
{prevLabel}
|
{prevLabel}
|
||||||
</AdminButton>
|
</AdminButton>
|
||||||
<AdminButton size="sm" disabled={nextDisabled} onclick={onNext}>
|
{#if hasPageNavigation}
|
||||||
|
<div class="admin-pagination-pages" aria-label={pageLabel}>
|
||||||
|
{#each pages as item (item.key)}
|
||||||
|
{#if item.type === "ellipsis"}
|
||||||
|
<span class="admin-pagination-ellipsis" aria-hidden="true">...</span>
|
||||||
|
{:else}
|
||||||
|
<AdminButton
|
||||||
|
class={item.index === currentPage
|
||||||
|
? "admin-pagination-page is-active"
|
||||||
|
: "admin-pagination-page"}
|
||||||
|
size="sm"
|
||||||
|
disabled={paginationDisabled}
|
||||||
|
aria-current={item.index === currentPage ? "page" : undefined}
|
||||||
|
aria-label={`${pageLabel} ${item.label}`}
|
||||||
|
onclick={() => goToPage(item.index)}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</AdminButton>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<AdminButton
|
||||||
|
class="admin-pagination-nav"
|
||||||
|
size="sm"
|
||||||
|
disabled={computedNextDisabled}
|
||||||
|
onclick={handleNext}
|
||||||
|
>
|
||||||
{nextLabel}
|
{nextLabel}
|
||||||
<ChevronRight size={14} />
|
<ChevronRight size={14} />
|
||||||
</AdminButton>
|
</AdminButton>
|
||||||
</div>
|
</div>
|
||||||
|
{#if hasPageNavigation}
|
||||||
|
<form class="admin-pagination-jump" on:submit|preventDefault={submitJump}>
|
||||||
|
<label class="admin-pagination-jump-label">
|
||||||
|
<span>{jumpLabel}</span>
|
||||||
|
<input
|
||||||
|
class="admin-pagination-jump-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max={normalizedPageCount}
|
||||||
|
step="1"
|
||||||
|
inputmode="numeric"
|
||||||
|
aria-label={jumpAriaLabel}
|
||||||
|
placeholder={String(currentPage + 1)}
|
||||||
|
value={jumpValue}
|
||||||
|
on:input={(event) => (jumpValue = event.currentTarget.value)}
|
||||||
|
disabled={paginationDisabled}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<AdminButton
|
||||||
|
class="admin-pagination-jump-button"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
disabled={!canJump}
|
||||||
|
aria-label={goLabel}
|
||||||
|
title={goLabel}
|
||||||
|
>
|
||||||
|
<ArrowRight size={13} />
|
||||||
|
</AdminButton>
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -85,6 +85,14 @@
|
|||||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-screen-wrap .admin-pagination-jump-input,
|
||||||
|
.admin-dialog .admin-pagination-jump-input {
|
||||||
|
width: 64px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-btn {
|
.admin-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1554,21 +1554,135 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.admin-pagination {
|
.admin-pagination {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: minmax(160px, 1fr) auto minmax(128px, auto);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
padding: 12px 4px 0;
|
padding: 12px 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-pagination .admin-pagination-meta {
|
.admin-pagination-summary {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination .admin-pagination-meta,
|
||||||
|
.admin-pagination-count {
|
||||||
color: var(--admin-muted);
|
color: var(--admin-muted);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-count {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 0 8px;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--admin-border) 80%, transparent);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: color-mix(in srgb, var(--admin-surface-2) 64%, transparent);
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-pagination-buttons {
|
.admin-pagination-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-pages {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination .admin-pagination-page.admin-btn,
|
||||||
|
.admin-pagination .admin-pagination-jump-button.admin-btn {
|
||||||
|
width: 32px;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination .admin-pagination-nav.admin-btn {
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination .admin-pagination-page.is-active {
|
||||||
|
border-color: color-mix(in srgb, var(--accent) 62%, var(--admin-border));
|
||||||
|
background: color-mix(in srgb, var(--accent) 18%, var(--admin-surface-2));
|
||||||
|
color: var(--admin-text);
|
||||||
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-ellipsis {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 32px;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-jump {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-jump-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
color: var(--admin-muted);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-jump-input {
|
||||||
|
width: 64px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--admin-border-strong);
|
||||||
|
background: color-mix(in srgb, var(--admin-bg) 82%, var(--admin-surface-2));
|
||||||
|
color: var(--admin-text);
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-jump-input:focus {
|
||||||
|
border-color: var(--admin-ring);
|
||||||
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.admin-pagination {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-buttons {
|
||||||
|
justify-content: flex-start;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-pagination-jump {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-select {
|
.admin-select {
|
||||||
|
|||||||
Reference in New Issue
Block a user