1+ import React from 'react' ;
2+
3+ interface PaginationProps {
4+ currentPage : number ;
5+ totalPages : number ;
6+ onPageChange : ( page : number ) => void ;
7+ }
8+
9+ export const Pagination : React . FC < PaginationProps > = ( {
10+ currentPage,
11+ totalPages,
12+ onPageChange,
13+ } ) => {
14+ const handlePrevious = ( ) => {
15+ if ( currentPage > 1 ) {
16+ onPageChange ( currentPage - 1 ) ;
17+ }
18+ } ;
19+
20+ const handleNext = ( ) => {
21+ if ( currentPage < totalPages ) {
22+ onPageChange ( currentPage + 1 ) ;
23+ }
24+ } ;
25+
26+ const getPageNumbers = ( ) => {
27+ const pages : ( number | string ) [ ] = [ ] ;
28+ const maxVisible = 7 ;
29+
30+ if ( totalPages <= maxVisible ) {
31+ for ( let i = 1 ; i <= totalPages ; i ++ ) {
32+ pages . push ( i ) ;
33+ }
34+ } else {
35+ if ( currentPage <= 4 ) {
36+ for ( let i = 1 ; i <= 5 ; i ++ ) {
37+ pages . push ( i ) ;
38+ }
39+ pages . push ( '...' ) ;
40+ pages . push ( totalPages ) ;
41+ } else if ( currentPage >= totalPages - 3 ) {
42+ pages . push ( 1 ) ;
43+ pages . push ( '...' ) ;
44+ for ( let i = totalPages - 4 ; i <= totalPages ; i ++ ) {
45+ pages . push ( i ) ;
46+ }
47+ } else {
48+ pages . push ( 1 ) ;
49+ pages . push ( '...' ) ;
50+ for ( let i = currentPage - 1 ; i <= currentPage + 1 ; i ++ ) {
51+ pages . push ( i ) ;
52+ }
53+ pages . push ( '...' ) ;
54+ pages . push ( totalPages ) ;
55+ }
56+ }
57+
58+ return pages ;
59+ } ;
60+
61+ if ( totalPages <= 1 ) {
62+ return null ;
63+ }
64+
65+ return (
66+ < div className = "flex items-center justify-center gap-2" >
67+ < button
68+ onClick = { handlePrevious }
69+ disabled = { currentPage === 1 }
70+ className = "px-4 py-2 rounded-md border border-gray-300 bg-white text-gray-700 font-medium hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white transition-colors"
71+ aria-label = "Previous page"
72+ >
73+ Prev
74+ </ button >
75+
76+ < div className = "flex items-center gap-1" >
77+ { getPageNumbers ( ) . map ( ( page , index ) => {
78+ if ( page === '...' ) {
79+ return (
80+ < span
81+ key = { `ellipsis-${ index } ` }
82+ className = "px-3 py-2 text-gray-500"
83+ >
84+ ...
85+ </ span >
86+ ) ;
87+ }
88+
89+ const pageNum = page as number ;
90+ const isActive = pageNum === currentPage ;
91+
92+ return (
93+ < button
94+ key = { pageNum }
95+ onClick = { ( ) => onPageChange ( pageNum ) }
96+ className = { `min-w-[40px] px-3 py-2 rounded-md font-medium transition-colors ${
97+ isActive
98+ ? 'bg-blue-600 text-white hover:bg-blue-700'
99+ : 'border border-gray-300 bg-white text-gray-700 hover:bg-gray-50'
100+ } `}
101+ aria-label = { `Page ${ pageNum } ` }
102+ aria-current = { isActive ? 'page' : undefined }
103+ >
104+ { pageNum }
105+ </ button >
106+ ) ;
107+ } ) }
108+ </ div >
109+
110+ < button
111+ onClick = { handleNext }
112+ disabled = { currentPage === totalPages }
113+ className = "px-4 py-2 rounded-md border border-gray-300 bg-white text-gray-700 font-medium hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white transition-colors"
114+ aria-label = "Next page"
115+ >
116+ Next
117+ </ button >
118+ </ div >
119+ ) ;
120+ } ;
121+
122+ export default Pagination ;
0 commit comments