mirror of
https://github.com/ThisTine/Snip.git
synced 2026-08-18 23:18:47 +07:00
feat: first commit
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user