Files
dashboards/packages/dashboard-ui/src/components/Pagination.tsx
T
Alireza HassaniandCursor 672091d1f5 Polish business FA layout and wire special-group keys plus SSL sync.
RTL carousels/FABs, special key+title fields, slider gallery fixes, and dashboard SSL sync agent for super-admin.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 00:06:44 +03:30

125 lines
3.4 KiB
TypeScript

import { useContext } from 'react'
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'
import { LocaleContext } from '../context/LocaleContext'
import styles from './Pagination.module.css'
export interface PaginationProps {
currentPage: number
totalPages: number
onPageChange: (page: number) => void
ariaLabel?: string
/** Pages on each side of current. Default 3 → e.g. 1 2 3 [4] 5 6 7 */
siblingCount?: number
disabled?: boolean
/** `inline` for table footers (no top margin). */
variant?: 'default' | 'inline'
className?: string
}
function buildPageWindow(
currentPage: number,
totalPages: number,
siblingCount: number,
): number[] {
const start = Math.max(1, currentPage - siblingCount)
const end = Math.min(totalPages, currentPage + siblingCount)
const pages: number[] = []
for (let page = start; page <= end; page += 1) {
pages.push(page)
}
return pages
}
export function Pagination({
currentPage,
totalPages,
onPageChange,
ariaLabel = 'Pagination',
siblingCount = 3,
disabled = false,
variant = 'default',
className,
}: PaginationProps) {
const locale = useContext(LocaleContext)
const dir =
locale?.dir ??
(typeof document !== 'undefined' && document.documentElement.dir === 'rtl' ? 'rtl' : 'ltr')
const isRtl = dir === 'rtl'
if (totalPages <= 1) return null
const pages = buildPageWindow(currentPage, totalPages, siblingCount)
const rootClass = [
styles.pagination,
variant === 'inline' ? styles.paginationInline : '',
className ?? '',
]
.filter(Boolean)
.join(' ')
const FirstIcon = isRtl ? ChevronsRight : ChevronsLeft
const PrevIcon = isRtl ? ChevronRight : ChevronLeft
const NextIcon = isRtl ? ChevronLeft : ChevronRight
const LastIcon = isRtl ? ChevronsLeft : ChevronsRight
return (
<nav className={rootClass} aria-label={ariaLabel} dir={dir}>
<button
type="button"
className={styles.navBtn}
onClick={() => onPageChange(1)}
disabled={disabled || currentPage === 1}
aria-label="First page"
>
<FirstIcon size={18} />
</button>
<button
type="button"
className={styles.navBtn}
onClick={() => onPageChange(currentPage - 1)}
disabled={disabled || currentPage === 1}
aria-label="Previous page"
>
<PrevIcon size={18} />
</button>
<div className={styles.pages}>
{pages.map((page) => (
<button
key={page}
type="button"
className={`${styles.pageBtn} ${page === currentPage ? styles.active : ''}`}
onClick={() => onPageChange(page)}
disabled={disabled}
aria-label={`Page ${page}`}
aria-current={page === currentPage ? 'page' : undefined}
>
{page}
</button>
))}
</div>
<button
type="button"
className={styles.navBtn}
onClick={() => onPageChange(currentPage + 1)}
disabled={disabled || currentPage === totalPages}
aria-label="Next page"
>
<NextIcon size={18} />
</button>
<button
type="button"
className={styles.navBtn}
onClick={() => onPageChange(totalPages)}
disabled={disabled || currentPage === totalPages}
aria-label="Last page"
>
<LastIcon size={18} />
</button>
</nav>
)
}