refactor: katalog feature'ı — sayfalar senkron kompozisyona geçti (Faz 2)
- 8 katalog bileşeni src/features/katalog/components/ altına taşındı; katalog-queries.ts (server-only) lib/katalog'un feature kapısı oldu - /bolumler ve /universiteler'in sayfa içi fetch+inline bileşenleri BolumKatalogu/UniversiteKatalogu feature bileşenlerine çıkarıldı - 6 sayfa async'ten senkron'a: params.then() + sayfada Suspense, skeleton'lar feature bileşeninin yanında (BolumIcerikSkeleton vb.) - generateStaticParams artık *-queries yerine bileşen dosyasının helper'larını kullanıyor (bolumStaticSlugs, uniSayfaStaticParams) - Davranış değişikliği yok; route durumları aynı (◐), 1917 statik sayfa Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
130
src/features/katalog/components/sayfalama.tsx
Normal file
130
src/features/katalog/components/sayfalama.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
|
||||
/** Programatik tablo sayfaları için satır dilimi boyutu. */
|
||||
export const SAYFA_BOYU = 50;
|
||||
|
||||
/**
|
||||
* Statik path tabanlı sayfalama navigasyonu. 1. sayfa taban URL'in
|
||||
* kendisidir (/bolum/x), sonrakiler /bolum/x/sayfa/2 ... — hepsi gerçek
|
||||
* <Link>, Google her sayfayı ayrı statik URL olarak tarar.
|
||||
*
|
||||
* `onSayfa` verilirse Link yerine aynı görünümde butonlar render edilir —
|
||||
* istemci tarafında sıralanmış listelerin sayfalaması URL değiştirmeden
|
||||
* bu geri çağrıyla ilerler.
|
||||
*/
|
||||
export function Sayfalama({
|
||||
tabanYol,
|
||||
sayfa,
|
||||
toplamSayfa,
|
||||
sorgu,
|
||||
onSayfa,
|
||||
}: {
|
||||
tabanYol: string;
|
||||
sayfa: number;
|
||||
toplamSayfa: number;
|
||||
sorgu?: string;
|
||||
onSayfa?: (n: number) => void;
|
||||
}) {
|
||||
if (toplamSayfa <= 1) return null;
|
||||
const yol = (n: number) => {
|
||||
const path = n === 1 ? tabanYol : `${tabanYol}/sayfa/${n}`;
|
||||
return sorgu ? `${path}?${sorgu}` : path;
|
||||
};
|
||||
const gecis = (
|
||||
n: number,
|
||||
className: string,
|
||||
icerik: ReactNode,
|
||||
ozellikler?: { rel?: string; aktif?: boolean },
|
||||
) =>
|
||||
onSayfa ? (
|
||||
<button
|
||||
key={n}
|
||||
type="button"
|
||||
onClick={() => onSayfa(n)}
|
||||
aria-current={ozellikler?.aktif ? "page" : undefined}
|
||||
className={`${className} cursor-pointer`}
|
||||
>
|
||||
{icerik}
|
||||
</button>
|
||||
) : (
|
||||
<Link
|
||||
key={n}
|
||||
href={yol(n)}
|
||||
rel={ozellikler?.rel}
|
||||
aria-current={ozellikler?.aktif ? "page" : undefined}
|
||||
className={className}
|
||||
>
|
||||
{icerik}
|
||||
</Link>
|
||||
);
|
||||
|
||||
// 7'den çok sayfada pencere: 1 … (c-1) c (c+1) … son
|
||||
const numaralar: (number | "...")[] = [];
|
||||
if (toplamSayfa <= 7) {
|
||||
for (let n = 1; n <= toplamSayfa; n++) numaralar.push(n);
|
||||
} else {
|
||||
numaralar.push(1);
|
||||
if (sayfa > 3) numaralar.push("...");
|
||||
for (
|
||||
let n = Math.max(2, sayfa - 1);
|
||||
n <= Math.min(toplamSayfa - 1, sayfa + 1);
|
||||
n++
|
||||
) {
|
||||
numaralar.push(n);
|
||||
}
|
||||
if (sayfa < toplamSayfa - 2) numaralar.push("...");
|
||||
numaralar.push(toplamSayfa);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
aria-label="Sayfalama"
|
||||
className="mt-6 flex flex-wrap items-center justify-center gap-1.5"
|
||||
>
|
||||
{sayfa > 1
|
||||
? gecis(
|
||||
sayfa - 1,
|
||||
"inline-flex h-9 items-center gap-1 rounded-full border border-slate-200 bg-white px-3.5 text-sm text-slate-700 hover:border-primary/40 hover:text-primary",
|
||||
<>
|
||||
<ChevronLeft className="size-4" aria-hidden />
|
||||
Önceki
|
||||
</>,
|
||||
{ rel: "prev" },
|
||||
)
|
||||
: null}
|
||||
{numaralar.map((n, i) =>
|
||||
n === "..." ? (
|
||||
<span
|
||||
key={`bosluk-${i}`}
|
||||
className="px-1.5 text-sm text-slate-400"
|
||||
aria-hidden
|
||||
>
|
||||
…
|
||||
</span>
|
||||
) : (
|
||||
gecis(
|
||||
n,
|
||||
n === sayfa
|
||||
? "inline-flex h-9 min-w-9 items-center justify-center rounded-full bg-primary px-2 text-sm font-semibold text-primary-foreground"
|
||||
: "inline-flex h-9 min-w-9 items-center justify-center rounded-full border border-slate-200 bg-white px-2 text-sm text-slate-700 hover:border-primary/40 hover:text-primary",
|
||||
n,
|
||||
{ aktif: n === sayfa },
|
||||
)
|
||||
),
|
||||
)}
|
||||
{sayfa < toplamSayfa
|
||||
? gecis(
|
||||
sayfa + 1,
|
||||
"inline-flex h-9 items-center gap-1 rounded-full border border-slate-200 bg-white px-3.5 text-sm text-slate-700 hover:border-primary/40 hover:text-primary",
|
||||
<>
|
||||
Sonraki
|
||||
<ChevronRight className="size-4" aria-hidden />
|
||||
</>,
|
||||
{ rel: "next" },
|
||||
)
|
||||
: null}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user