Pagination
Mẫu chuẩnĐi qua các trang của một danh sách. Trang hiện tại và tổng số trang đến từ ngoài; component chỉ phát ra yêu cầu đổi trang.
Playground
Đổi tổng số trang và trang hiện tại để xem cửa sổ số trang, dấu lược và trạng thái vô hiệu của Trước/Sau đổi theo.
Trang 4 / 12 · Trước khả dụng · Sau khả dụng
import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis, getPaginationRange } from "@nexobox/ui";
const range = getPaginationRange(4, 12);
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious disabled={page <= 1} onClick={() => setPage(page - 1)} />
</PaginationItem>
{range.map((item) =>
item === "ellipsis" ? (
<PaginationEllipsis key={item} />
) : (
<PaginationItem key={item}>
<PaginationLink page={item} isActive={item === page} onClick={() => setPage(item)}>
{item}
</PaginationLink>
</PaginationItem>
),
)}
<PaginationItem>
<PaginationNext disabled={page >= totalPages} onClick={() => setPage(page + 1)} />
</PaginationItem>
</PaginationContent>
</Pagination>Khi nào sử dụng
Nên dùng
Chuyển lát dữ liệu đang hiển thị trong cùng một khung nhìn: trang n trên tổng N trang của một danh sách. Component không import Table, chỉ nhận page và totalPages từ ngoài — một lưới thẻ cũng phân trang được như một bảng.
Khi nào không dùng
Tránh dùng
Tabs. Tabs đổi khung nhìn — bấm một tab thay cả bộ nội dung đang hiển thị. Pagination đổi lát dữ liệu của cùng một khung nhìn — bấm "Trang 2" vẫn là cùng một danh sách, chỉ khác đoạn nào của nó đang lộ ra.
Anatomy
| # | Phần | Ghi chú |
|---|---|---|
| 1 | Pagination | <nav aria-label="Phân trang">, ranh giới ngữ nghĩa. |
| 2 | PaginationContent | <ul>, danh sách ngang các mục. |
| 3 | PaginationPrevious | <button> lùi một trang, mũi tên trái (xoay 180° từ glyph mũi tên phải). |
| 4 | PaginationLink | <a> hoặc Slot, một trang cụ thể. |
| 5 | PaginationLink isActive | Trang hiện tại, render <span aria-current="page"> thay vì <a>. |
| 6 | PaginationEllipsis | <li> không tương tác, thay cho một đoạn số trang bị bỏ qua. |
| 7 | PaginationNext | <button> sang trang, mũi tên phải. |
Variants
Không có. Pagination không nhận prop variant — một hình thức trình bày duy nhất. Khác biệt giữa các mục đến từ state (mặc định / hiện tại / vô hiệu), không phải một tập variant để chọn.
States
Ba vị trí thật trong cùng một danh sách 10 trang — lùi vô hiệu ở trang đầu, cả hai khả dụng ở giữa, tiến vô hiệu ở trang cuối.
Previous mang thuộc tính disabled thật — không nhận Tab, không nhận click.
Cả Previous và Next đều khả dụng. Dấu lược xuất hiện ở cả hai phía khi khoảng cách đủ lớn.
Next mang thuộc tính disabled thật, cùng lý do với Previous ở trang đầu.
Di chuột vào một số trang chưa active — nền đổi --nf-component-pagination-item-hover. Giữ chuột xuống — nền đổi --nf-component-pagination-item-pressed. Cả hai lần đầu có trên primitive này; trước DS-018 chỉ có press-scale.
Bàn phím & trợ năng
API & triển khai
| Prop | Mặc định | Quy ước |
|---|---|---|
| Pagination.aria-label | "Phân trang" | Trên <nav>; ghi đè khi có nhiều khối Pagination cùng lúc. |
| PaginationLink.asChild | false | true render qua Slot, dùng cho Link của router thay vì <a>. |
| PaginationLink.isActive | false | true render <span aria-current="page"> không tương tác. |
| PaginationLink.page | — | Sinh aria-label="Trang {n}" khi không tự đặt aria-label. |
| PaginationPrevious / Next.disabled | false | true khi hết trang theo hướng đó; gỡ khỏi Tab. |
| getPaginationRange(page, totalPages, siblingCount?, boundaryCount?) | 1, 1 | Hàm thuần, trả (number | "ellipsis")[]; không tự lưu page. |
Mọi thuộc tính DOM còn lại của a/button/nav/li/ul được truyền thẳng qua ...props. className được nối, không thay thế.
Design tokens
Đọc trực tiếp --nf-foundation-size-touch-target (sàn 44px, không điều kiện theo density — đây là bind một sàn, không phải một chiều cao, nên DS-017 clause 8 chủ ý để nguyên, khác với năm primitive khác từng bind bậc nền tảng), --nf-foundation-size-icon-small, --nf-component-focus-width/-color/-offset, --nf-semantic-action-selected-background/-foreground (trang hiện tại), --nf-semantic-action-disabled-background/-foreground (Trước/Sau vô hiệu). Năm alias --nf-component-pagination-gap, -item-padding-inline, -item-radius, -item-hover, -item-pressed có mặt trong component.tokens.json và đã sinh vào packages/ui/styles/nexoframe.tokens.css. --nf-foundation-motion-press-scale không còn được đọc — DS-018 rút nó khỏi mọi primitive; hover/pressed nay biểu đạt bằng màu, không bằng scale.
Do / Don't
Nên dùng
Cho Pagination nhận page/totalPages từ nơi giữ dữ liệu thật (URL, state cha) và tính lại getPaginationRange mỗi lần đổi trang. Vô hiệu hoá Previous/Next thật bằng disabled khi đã ở biên.
Tránh dùng
Để component tự giữ page trong state nội bộ. Chỉ tô mờ Previous/Next bằng CSS mà vẫn để onClick chạy. Đặt isActive trên nhiều mục cùng lúc, hoặc bỏ hẳn nó.