feat(client): add Pagination component and integrate with Table and pages

- add Pagination component with prev/next, ellipsis page range, and optional page-size selector
- add Pagination and Table pagination stories (WithPagination, WithPaginationAndSizeSelector)
- extend Table with optional pageSize and pageSizeOptions props for built-in client-side pagination
- add Timestamp column to Table story fixtures
- enable pagination on all four pages with pageSize=10 and size selector
- add pagination i18n keys to en.json
This commit is contained in:
2026-04-09 10:43:45 +03:00
parent b4d576c6e9
commit 077cd6ad5f
11 changed files with 358 additions and 35 deletions
+37 -3
View File
@@ -1,4 +1,5 @@
import type { ReactNode } from 'react';
import { useState, type ReactNode } from 'react';
import { Pagination } from '../Pagination/Pagination';
import styles from './Table.module.css';
export interface TableColumn<T> {
@@ -16,6 +17,10 @@ export interface TableProps<T> {
loading?: boolean;
emptyMessage?: string;
className?: string;
/** Enable built-in client-side pagination with this default page size */
pageSize?: number;
/** Offer a page-size selector when pagination is enabled */
pageSizeOptions?: number[];
}
export function Table<T>({
@@ -25,7 +30,19 @@ export function Table<T>({
loading = false,
emptyMessage = 'No data.',
className,
pageSize: defaultPageSize,
pageSizeOptions,
}: TableProps<T>) {
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(defaultPageSize ?? 0);
const paginated = defaultPageSize !== undefined && !loading;
const visibleData = paginated ? data.slice((page - 1) * pageSize, page * pageSize) : data;
// Reset to page 1 when data changes and current page would be empty
const lastPage = pageSize > 0 ? Math.ceil(data.length / pageSize) : 1;
const safePage = Math.min(page, Math.max(1, lastPage));
return (
<div className={`${styles.wrapper} ${className ?? ''}`}>
<table className={styles.table}>
@@ -52,14 +69,14 @@ export function Table<T>({
Loading
</td>
</tr>
) : data.length === 0 ? (
) : visibleData.length === 0 ? (
<tr>
<td colSpan={columns.length} className={styles.empty}>
{emptyMessage}
</td>
</tr>
) : (
data.map((row) => (
visibleData.map((row) => (
<tr key={rowKey(row)} className={styles.tr}>
{columns.map((col) => (
<td
@@ -75,6 +92,23 @@ export function Table<T>({
)}
</tbody>
</table>
{paginated && data.length > 0 && (
<Pagination
page={safePage}
pageSize={pageSize}
total={data.length}
onPageChange={setPage}
onPageSizeChange={
pageSizeOptions
? (size) => {
setPageSize(size);
setPage(1);
}
: undefined
}
pageSizeOptions={pageSizeOptions}
/>
)}
</div>
);
}