Files
star-erp/resources/js/Components/shared/Pagination.tsx
sky121113 89183ca124
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 50s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped
feat: 實作使用者管理與公共事業費分頁標準化
2026-01-20 15:53:15 +08:00

99 lines
4.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Link } from "@inertiajs/react";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { cn } from "@/lib/utils";
interface PaginationProps {
links: {
url: string | null;
label: string;
active: boolean;
}[];
className?: string;
}
export default function Pagination({ links, className }: PaginationProps) {
// 如果只有一頁,不顯示分頁
if (links.length <= 3) return null;
return (
<div className={cn("flex flex-wrap justify-center gap-1", className)}>
{links.map((link, key) => {
// 處理特殊標籤
let label = link.label;
if (label.includes("&laquo;")) label = "Previous";
if (label.includes("&raquo;")) label = "Next";
const isPrevious = label === "Previous";
const isNext = label === "Next";
const activeIndex = links.findIndex(l => l.active);
// Tablet/Mobile visibility logic (< md):
// Show: Previous, Next, Active, and up to 2 neighbors (Total ~5 numeric pages)
// Hide others on small screens (hidden md:flex)
// User requested: "small than 800... display 5 pages"
const isVisibleOnTablet =
isPrevious ||
isNext ||
link.active ||
key === activeIndex - 1 ||
key === activeIndex + 1 ||
key === activeIndex - 2 ||
key === activeIndex + 2;
const baseClasses = cn(
isVisibleOnTablet ? "flex" : "hidden md:flex",
"h-9 items-center justify-center rounded-md border px-3 text-sm"
);
// 如果是 Previous/Next 但沒有 URL則不渲染或者渲染為 disabled
if ((isPrevious || isNext) && !link.url) {
return (
<div
key={key}
className={cn(
baseClasses,
"border-input bg-transparent text-muted-foreground opacity-50 cursor-not-allowed",
isPrevious || isNext ? "px-2" : ""
)}
>
{isPrevious && <ChevronLeft className="h-4 w-4" />}
{isNext && <ChevronRight className="h-4 w-4" />}
{!isPrevious && !isNext && <span dangerouslySetInnerHTML={{ __html: link.label }} />}
</div>
);
}
return link.url ? (
<Link
key={key}
href={link.url}
preserveScroll
className={cn(
baseClasses,
"transition-colors hover:bg-accent hover:text-accent-foreground",
link.active
? "border-primary bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground"
: "border-input bg-transparent text-foreground",
isPrevious || isNext ? "px-2" : ""
)}
>
{isPrevious && <ChevronLeft className="h-4 w-4" />}
{isNext && <ChevronRight className="h-4 w-4" />}
{!isPrevious && !isNext && <span dangerouslySetInnerHTML={{ __html: link.label }} />}
</Link>
) : (
<div
key={key}
className={cn(
baseClasses,
"border-input bg-transparent text-foreground"
)}
>
<span dangerouslySetInnerHTML={{ __html: link.label }} />
</div>
);
})}
</div>
);
}