thread-master/apps/web/src/components/ui/Pager.tsx

91 lines
2.5 KiB
TypeScript
Raw Normal View History

2026-07-10 05:10:31 +00:00
import { Button } from "./Button";
import { Select } from "./Select";
import {
clampPage,
DEFAULT_PAGE_SIZES,
pageCount,
pageRangeLabel,
} from "../../lib/pagination";
import { useI18n } from "../../i18n/I18nContext";
type Props = {
/** 總筆數 */
total: number;
/** 目前頁(從 1 起) */
page: number;
pageSize: number;
onPageChange: (page: number) => void;
/** 有傳才顯示每頁筆數 */
onPageSizeChange?: (size: number) => void;
pageSizeOptions?: readonly number[];
/** 僅一頁時是否仍顯示(預設否) */
showWhenSingle?: boolean;
className?: string;
};
/**
*
* 調 pageSize
*/
export function Pager({
total,
page,
pageSize,
onPageChange,
onPageSizeChange,
pageSizeOptions = DEFAULT_PAGE_SIZES,
showWhenSingle = false,
className = "",
}: Props) {
const { t } = useI18n();
const totalPages = pageCount(total, pageSize);
const safePage = clampPage(page, totalPages);
const single = totalPages <= 1;
const hide = total <= 0 || (single && !showWhenSingle && !onPageSizeChange);
if (hide) return null;
return (
<div className={`hb-pager ${className}`.trim()} role="navigation" aria-label={t("pager.nav")}>
<span className="hb-pager__meta text-muted">{pageRangeLabel(safePage, pageSize, total)}</span>
<div className="hb-pager__controls">
{onPageSizeChange ? (
<Select
label=""
aria-label={t("pager.pageSize")}
value={String(pageSize)}
onChange={(e) => {
onPageSizeChange(Number(e.target.value) || pageSize);
onPageChange(1);
}}
>
{pageSizeOptions.map((n) => (
<option key={n} value={n}>
{t("pager.perPage", { n })}
</option>
))}
</Select>
) : null}
<Button
type="button"
variant="ghost"
disabled={safePage <= 1}
onClick={() => onPageChange(safePage - 1)}
>
{t("pager.prev")}
</Button>
<span className="hb-pager__page" aria-current="page">
{safePage} / {totalPages}
</span>
<Button
type="button"
variant="ghost"
disabled={safePage >= totalPages}
onClick={() => onPageChange(safePage + 1)}
>
{t("pager.next")}
</Button>
</div>
</div>
);
}