'use client' import * as React from 'react' import clsx from 'clsx' import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/20/solid' type PageItem = number | 'ellipsis' export type PaginationProps = { page: number // 1-based pageSize: number totalItems: number onPageChange: (page: number) => void /** wie viele Seiten links/rechts neben aktueller Seite */ siblingCount?: number /** wie viele Seiten am Anfang/Ende immer gezeigt werden */ boundaryCount?: number /** Summary "Showing x to y of z" anzeigen */ showSummary?: boolean /** Wrapper Klassen */ className?: string /** Labels (optional) */ ariaLabel?: string prevLabel?: string nextLabel?: string } function clamp(n: number, min: number, max: number) { return Math.max(min, Math.min(max, n)) } function range(start: number, end: number): number[] { const out: number[] = [] for (let i = start; i <= end; i++) out.push(i) return out } function getPageItems( totalPages: number, current: number, boundaryCount: number, siblingCount: number ): PageItem[] { if (totalPages <= 1) return [1] const first = 1 const last = totalPages const startPages = range(first, Math.min(boundaryCount, last)) const endPages = range(Math.max(last - boundaryCount + 1, boundaryCount + 1), last) const siblingsStart = Math.max( Math.min( current - siblingCount, last - boundaryCount - siblingCount * 2 - 1 ), boundaryCount + 1 ) const siblingsEnd = Math.min( Math.max( current + siblingCount, boundaryCount + siblingCount * 2 + 2 ), last - boundaryCount ) const items: PageItem[] = [] // start items.push(...startPages) // left gap if (siblingsStart > boundaryCount + 1) { items.push('ellipsis') } else if (boundaryCount + 1 < last - boundaryCount) { items.push(boundaryCount + 1) } // siblings items.push(...range(siblingsStart, siblingsEnd)) // right gap if (siblingsEnd < last - boundaryCount) { items.push('ellipsis') } else if (last - boundaryCount > boundaryCount) { items.push(last - boundaryCount) } // end items.push(...endPages) // dedupe + keep order const seen = new Set() return items.filter((x) => { const k = String(x) if (seen.has(k)) return false seen.add(k) return true }) } function PageButton({ active, disabled, rounded, onClick, children, title, }: { active?: boolean disabled?: boolean rounded?: 'l' | 'r' | 'none' onClick?: () => void children: React.ReactNode title?: string }) { const roundedCls = rounded === 'l' ? 'rounded-l-md' : rounded === 'r' ? 'rounded-r-md' : '' return ( ) } export default function Pagination({ page, pageSize, totalItems, onPageChange, siblingCount = 1, boundaryCount = 1, showSummary = true, className, ariaLabel = 'Pagination', prevLabel = 'Previous', nextLabel = 'Next', }: PaginationProps) { const totalPages = Math.max(1, Math.ceil((totalItems || 0) / Math.max(1, pageSize || 1))) const current = clamp(page || 1, 1, totalPages) if (totalPages <= 1) return null const from = totalItems === 0 ? 0 : (current - 1) * pageSize + 1 const to = Math.min(current * pageSize, totalItems) const items = getPageItems(totalPages, current, boundaryCount, siblingCount) const go = (p: number) => onPageChange(clamp(p, 1, totalPages)) return (
{/* Mobile: nur Previous/Next */}
{/* Desktop: Summary + Zahlen */}
{showSummary ? (

Showing {from} to{' '} {to} of{' '} {totalItems} results

) : null}
) }