Bỏ qua tới nội dung
Component / 033 / NEXOFRAME

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.

Tailwind + HugeiconsKhông variantKhông fetch dữ liệuĐi kèm Table

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.

LIVE PREVIEW

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ầnGhi chú
1Pagination<nav aria-label="Phân trang">, ranh giới ngữ nghĩa.
2PaginationContent<ul>, danh sách ngang các mục.
3PaginationPrevious<button> lùi một trang, mũi tên trái (xoay 180° từ glyph mũi tên phải).
4PaginationLink<a> hoặc Slot, một trang cụ thể.
5PaginationLink isActiveTrang hiện tại, render <span aria-current="page"> thay vì <a>.
6PaginationEllipsis<li> không tương tác, thay cho một đoạn số trang bị bỏ qua.
7PaginationNext<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.

TRANG ĐẦU

Previous mang thuộc tính disabled thật — không nhận Tab, không nhận click.

TRANG GIỮA

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.

TRANG CUỐI

Next mang thuộc tính disabled thật, cùng lý do với Previous ở trang đầu.

HOVER / PRESSED

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

Tab / Shift+Tab di chuyển qua PaginationLink không active, PaginationPrevious và PaginationNext khi không disabled.
Trang hiện tại (span aria-current="page") và PaginationEllipsis không nhận focus -- không phải control.
Enter / Space kích hoạt mục đang focus: theo href nếu là thẻ a, gọi onClick nếu là button.
Previous/Next disabled bị gỡ khỏi luồng Tab hoàn toàn qua thuộc tính disabled gốc, không chỉ tô mờ bằng CSS.
Mỗi PaginationLink nhận aria-label="Trang {n}" tự động từ prop page trừ khi ghi đè.

API & triển khai

PropMặc địnhQuy ước
Pagination.aria-label"Phân trang"Trên <nav>; ghi đè khi có nhiều khối Pagination cùng lúc.
PaginationLink.asChildfalsetrue render qua Slot, dùng cho Link của router thay vì <a>.
PaginationLink.isActivefalsetrue 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.disabledfalsetrue khi hết trang theo hướng đó; gỡ khỏi Tab.
getPaginationRange(page, totalPages, siblingCount?, boundaryCount?)1, 1Hà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ó.