mirror of
https://github.com/ceratic/MediaCollectorLibaryFrontend.git
synced 2026-05-13 23:56:45 +02:00
129 lines
4.1 KiB
TypeScript
129 lines
4.1 KiB
TypeScript
import { motion } from 'framer-motion'
|
|
|
|
interface PaginationProps {
|
|
currentPage: number
|
|
lastPage: number
|
|
total: number
|
|
onPageChange: (page: number) => void
|
|
itemsPerPage: number
|
|
onItemsPerPageChange: (items: number) => void
|
|
}
|
|
|
|
export default function Pagination({
|
|
currentPage,
|
|
lastPage,
|
|
total,
|
|
onPageChange,
|
|
itemsPerPage,
|
|
onItemsPerPageChange
|
|
}: PaginationProps) {
|
|
const handlePrevious = () => {
|
|
if (currentPage > 1) {
|
|
onPageChange(currentPage - 1)
|
|
}
|
|
}
|
|
|
|
const handleNext = () => {
|
|
if (currentPage < lastPage) {
|
|
onPageChange(currentPage + 1)
|
|
}
|
|
}
|
|
|
|
const handlePageSelect = (page: number) => {
|
|
onPageChange(page)
|
|
}
|
|
|
|
// Generate page numbers to show
|
|
const getVisiblePages = () => {
|
|
const pages: number[] = []
|
|
const maxVisible = 5
|
|
|
|
if (lastPage <= maxVisible) {
|
|
for (let i = 1; i <= lastPage; i++) {
|
|
pages.push(i)
|
|
}
|
|
} else {
|
|
const start = Math.max(1, currentPage - 2)
|
|
const end = Math.min(lastPage, start + maxVisible - 1)
|
|
|
|
for (let i = start; i <= end; i++) {
|
|
pages.push(i)
|
|
}
|
|
}
|
|
|
|
return pages
|
|
}
|
|
|
|
if (lastPage <= 1) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 mt-8 p-4 bg-white/5 dark:bg-gray-800/50 rounded-xl border border-white/10">
|
|
{/* Items per page selector */}
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">Show:</span>
|
|
<select
|
|
value={itemsPerPage}
|
|
onChange={(e) => onItemsPerPageChange(parseInt(e.target.value))}
|
|
className="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
|
>
|
|
<option value={10}>10</option>
|
|
<option value={20}>20</option>
|
|
<option value={50}>50</option>
|
|
<option value={100}>100</option>
|
|
</select>
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">
|
|
of {total} items
|
|
</span>
|
|
</div>
|
|
|
|
{/* Pagination controls */}
|
|
<div className="flex items-center gap-2">
|
|
<motion.button
|
|
onClick={handlePrevious}
|
|
disabled={currentPage === 1}
|
|
className="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
whileHover={{ scale: currentPage !== 1 ? 1.05 : 1 }}
|
|
whileTap={{ scale: currentPage !== 1 ? 0.95 : 1 }}
|
|
>
|
|
Previous
|
|
</motion.button>
|
|
|
|
<div className="flex items-center gap-1">
|
|
{getVisiblePages().map((page) => (
|
|
<motion.button
|
|
key={page}
|
|
onClick={() => handlePageSelect(page)}
|
|
className={`w-8 h-8 text-sm rounded-lg transition-colors ${
|
|
currentPage === page
|
|
? 'bg-purple-600 text-white'
|
|
: 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-600'
|
|
}`}
|
|
whileHover={{ scale: 1.1 }}
|
|
whileTap={{ scale: 0.9 }}
|
|
>
|
|
{page}
|
|
</motion.button>
|
|
))}
|
|
</div>
|
|
|
|
<motion.button
|
|
onClick={handleNext}
|
|
disabled={currentPage === lastPage}
|
|
className="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
whileHover={{ scale: currentPage !== lastPage ? 1.05 : 1 }}
|
|
whileTap={{ scale: currentPage !== lastPage ? 0.95 : 1 }}
|
|
>
|
|
Next
|
|
</motion.button>
|
|
</div>
|
|
|
|
{/* Page info */}
|
|
<div className="text-sm text-gray-600 dark:text-gray-400">
|
|
Page {currentPage} of {lastPage}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|