feat: first commit

This commit is contained in:
sittichok Ouamsiri
2026-06-15 21:25:57 +07:00
commit 3395ab6dd3
88 changed files with 10034 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
import { motion } from "framer-motion";
interface Props {
page: number; // 1-based
pageCount: number;
onPage: (page: number) => void;
}
/** Build a windowed page list with ellipses, e.g. 1 … 4 5 6 … 12 */
function pageItems(page: number, count: number): (number | "…")[] {
if (count <= 7) return Array.from({ length: count }, (_, i) => i + 1);
const items: (number | "…")[] = [1];
const start = Math.max(2, page - 1);
const end = Math.min(count - 1, page + 1);
if (start > 2) items.push("…");
for (let i = start; i <= end; i++) items.push(i);
if (end < count - 1) items.push("…");
items.push(count);
return items;
}
export function Pagination({ page, pageCount, onPage }: Props) {
if (pageCount <= 1) return null;
const items = pageItems(page, pageCount);
return (
<div className="mt-6 flex items-center justify-center gap-1.5">
<button
onClick={() => onPage(page - 1)}
disabled={page === 1}
className="focusable grid h-9 w-9 place-items-center rounded-xl border-[1.5px] border-line bg-surface text-ink transition-opacity hover:bg-surface-2 disabled:opacity-30"
aria-label="Previous page"
>
</button>
{items.map((it, i) =>
it === "…" ? (
<span key={`e${i}`} className="px-1 text-sm text-muted">
</span>
) : (
<button
key={it}
onClick={() => onPage(it)}
aria-current={it === page}
className="focusable relative grid h-9 min-w-9 place-items-center rounded-xl px-2 text-sm font-semibold"
>
{it === page && (
<motion.span
layoutId="page-pill"
transition={{ type: "spring", stiffness: 480, damping: 32 }}
className="absolute inset-0 rounded-xl bg-accent"
/>
)}
<span
className={`relative z-10 ${
it === page ? "text-accent-ink" : "text-muted hover:text-ink"
}`}
>
{it}
</span>
</button>
),
)}
<button
onClick={() => onPage(page + 1)}
disabled={page === pageCount}
className="focusable grid h-9 w-9 place-items-center rounded-xl border-[1.5px] border-line bg-surface text-ink transition-opacity hover:bg-surface-2 disabled:opacity-30"
aria-label="Next page"
>
</button>
</div>
);
}