11'use client' ;
22
3+ import { useEffect , useState } from 'react' ;
34import { useTranslations } from 'next-intl' ;
45import { cn } from '@/lib/utils' ;
56
67interface PaginationProps {
78 currentPage : number ;
89 totalPages : number ;
910 onPageChange : ( page : number ) => void ;
11+ accentColor : string ;
12+ }
13+
14+ function hexToRgba ( hex : string , alpha : number ) : string {
15+ const normalized = hex . replace ( '#' , '' ) ;
16+ if ( normalized . length !== 6 ) return `rgba(0, 0, 0, ${ alpha } )` ;
17+ const r = parseInt ( normalized . slice ( 0 , 2 ) , 16 ) ;
18+ const g = parseInt ( normalized . slice ( 2 , 4 ) , 16 ) ;
19+ const b = parseInt ( normalized . slice ( 4 , 6 ) , 16 ) ;
20+ return `rgba(${ r } , ${ g } , ${ b } , ${ alpha } )` ;
1021}
1122
1223export function Pagination ( {
1324 currentPage,
1425 totalPages,
1526 onPageChange,
27+ accentColor,
1628} : PaginationProps ) {
1729 const t = useTranslations ( 'qa.pagination' ) ;
30+ const accentSoft = hexToRgba ( accentColor , 0.16 ) ;
31+ const accentGlow = hexToRgba ( accentColor , 0.22 ) ;
32+ const [ isMobile , setIsMobile ] = useState ( false ) ;
33+
34+ useEffect ( ( ) => {
35+ const media = window . matchMedia ( '(max-width: 640px)' ) ;
36+ const update = ( ) => setIsMobile ( media . matches ) ;
37+ update ( ) ;
38+ media . addEventListener ( 'change' , update ) ;
39+ return ( ) => media . removeEventListener ( 'change' , update ) ;
40+ } , [ ] ) ;
1841
1942 if ( totalPages <= 1 ) return null ;
2043
2144 const getPageNumbers = ( ) : ( number | 'ellipsis' ) [ ] => {
2245 const pages : ( number | 'ellipsis' ) [ ] = [ ] ;
23- const maxVisible = 5 ;
46+ const maxVisible = isMobile ? 3 : 5 ;
2447
2548 if ( totalPages <= maxVisible + 2 ) {
2649 for ( let i = 1 ; i <= totalPages ; i ++ ) {
@@ -59,25 +82,32 @@ export function Pagination({
5982
6083 return (
6184 < nav
62- className = "flex items-center justify-center gap-1 mt-8"
85+ className = "flex items-center justify-center gap-1 mt-8 sm:gap-2"
86+ style = {
87+ {
88+ '--qa-accent' : accentColor ,
89+ '--qa-accent-soft' : accentSoft ,
90+ '--qa-accent-glow' : accentGlow ,
91+ } as React . CSSProperties
92+ }
6393 aria-label = { t ( 'label' ) }
6494 >
6595 < button
6696 onClick = { ( ) => onPageChange ( currentPage - 1 ) }
6797 disabled = { currentPage === 1 }
6898 className = { cn (
69- 'px-3 py-2 text-sm font-medium rounded-lg transition-colors' ,
99+ 'px-2 py-2 text-sm font-medium rounded-lg transition-colors sm:px-3 ' ,
70100 'border border-gray-300 dark:border-gray-700' ,
71101 currentPage === 1
72102 ? 'text-gray-400 dark:text-gray-600 cursor-not-allowed'
73- : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 '
103+ : 'text-gray-700 dark:text-gray-300 hover:bg-[var(--qa-accent-soft)] '
74104 ) }
75105 aria-label = { t ( 'previousPage' ) }
76106 >
77- ← { t ( 'previous' ) }
107+ ← < span className = "hidden sm:inline" > { t ( 'previous' ) } </ span >
78108 </ button >
79109
80- < div className = "flex items-center gap-1 mx-2" >
110+ < div className = "flex items-center gap-1 mx-1 sm:mx- 2" >
81111 { pages . map ( ( page , index ) =>
82112 page === 'ellipsis' ? (
83113 < span
@@ -92,11 +122,20 @@ export function Pagination({
92122 onClick = { ( ) => onPageChange ( page ) }
93123 disabled = { page === currentPage }
94124 className = { cn (
95- 'min-w-[40px] px-3 py-2 text-sm font-medium rounded-lg transition-colors' ,
125+ 'min-w-[40px] px-3 py-2 text-sm font-medium rounded-lg transition-colors border border-transparent overflow-hidden ' ,
96126 page === currentPage
97- ? 'bg-blue-600 text-white '
98- : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 '
127+ ? 'shadow-sm text-gray-700 dark: text-gray-300 '
128+ : 'text-gray-700 dark:text-gray-300 hover:bg-[var(--qa-accent-soft)] '
99129 ) }
130+ style = {
131+ page === currentPage
132+ ? {
133+ backgroundColor : accentSoft ,
134+ borderColor : accentColor ,
135+ boxShadow : `inset 0 0 18px ${ accentGlow } ` ,
136+ }
137+ : undefined
138+ }
100139 aria-label = { t ( 'page' , { page } ) }
101140 aria-current = { page === currentPage ? 'page' : undefined }
102141 >
@@ -110,16 +149,16 @@ export function Pagination({
110149 onClick = { ( ) => onPageChange ( currentPage + 1 ) }
111150 disabled = { currentPage === totalPages }
112151 className = { cn (
113- 'px-3 py-2 text-sm font-medium rounded-lg transition-colors' ,
152+ 'px-2 py-2 text-sm font-medium rounded-lg transition-colors sm:px-3 ' ,
114153 'border border-gray-300 dark:border-gray-700' ,
115154 currentPage === totalPages
116155 ? 'text-gray-400 dark:text-gray-600 cursor-not-allowed'
117- : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 '
156+ : 'text-gray-700 dark:text-gray-300 hover:bg-[var(--qa-accent-soft)] '
118157 ) }
119158 aria-label = { t ( 'nextPage' ) }
120159 >
121- { t ( 'next' ) } →
160+ < span className = "hidden sm:inline" > { t ( 'next' ) } </ span > →
122161 </ button >
123162 </ nav >
124163 ) ;
125- }
164+ }
0 commit comments