|
1 | | -import { cn } from '@/lib/utils.ts'; |
2 | 1 | import { |
3 | 2 | Pagination, |
4 | 3 | PaginationContent, |
5 | | - PaginationEllipsis, |
6 | 4 | PaginationItem, |
7 | 5 | PaginationLink, |
8 | | - PaginationNext, |
9 | 6 | PaginationPrevious, |
10 | | -} from '../components/ui/pagination.tsx'; |
| 7 | + PaginationNext, |
| 8 | + PaginationEllipsis, |
| 9 | +} from '@/components/ui/pagination'; |
11 | 10 |
|
12 | | -interface PaginatedNavigationProps { |
13 | | - className?: string; |
14 | | - totalPages: number; |
| 11 | +type PaginationControlsProps = { |
15 | 12 | currentPage: number; |
| 13 | + totalPages: number; |
16 | 14 | onPageChange: (page: number) => void; |
17 | | -} |
18 | | - |
19 | | -const PaginationItemLink = ({ |
20 | | - page, |
21 | | - isActive, |
22 | | - onClick, |
23 | | -}: { |
24 | | - page: number; |
25 | | - isActive: boolean; |
26 | | - onClick: () => void; |
27 | | -}) => ( |
28 | | - <PaginationItem key={page}> |
29 | | - <PaginationLink |
30 | | - className="rounded-lg" |
31 | | - isActive={isActive} |
32 | | - onClick={onClick} |
33 | | - > |
34 | | - {page + 1} |
35 | | - </PaginationLink> |
36 | | - </PaginationItem> |
37 | | -); |
38 | | - |
39 | | -const generatePaginationItems = ( |
40 | | - totalPages: number, |
41 | | - currentPage: number, |
42 | | - onPageChange: (page: number) => void |
43 | | -) => { |
44 | | - const items: JSX.Element[] = []; |
45 | | - |
46 | | - const maxVisiblePages = 3; |
47 | | - const boundaryThreshold = 1; |
48 | | - const minimumPagesForEllipsis = 3; |
49 | | - |
50 | | - const firstPage = 0; |
51 | | - const lastPage = totalPages - 1; |
| 15 | +}; |
52 | 16 |
|
53 | | - const showStartEllipsis = currentPage > boundaryThreshold; |
54 | | - const showEndEllipsis = currentPage < totalPages - boundaryThreshold - 1; |
| 17 | +export const PaginatedNavigation = ({ |
| 18 | + currentPage, |
| 19 | + totalPages, |
| 20 | + onPageChange, |
| 21 | +}: PaginationControlsProps) => { |
| 22 | + const generatePages = () => { |
| 23 | + const pages: (number | 'ellipsis')[] = []; |
55 | 24 |
|
56 | | - if (!showStartEllipsis) { |
57 | | - const isFirstPage = currentPage === firstPage; |
58 | | - let endPage = Math.min(maxVisiblePages, totalPages); |
59 | | - if (isFirstPage) { |
60 | | - endPage++; |
61 | | - } |
62 | | - for (let i = 0; i < endPage; i++) { |
63 | | - items.push( |
64 | | - <PaginationItemLink |
65 | | - key={i} |
66 | | - page={i} |
67 | | - isActive={currentPage === i} |
68 | | - onClick={() => onPageChange(i)} |
69 | | - /> |
70 | | - ); |
71 | | - } |
| 25 | + if (totalPages <= 5) { |
| 26 | + // Affiche tout si peu de pages |
| 27 | + for (let i = 1; i <= totalPages; i++) { |
| 28 | + pages.push(i); |
| 29 | + } |
| 30 | + } else if (currentPage <= 3) { |
| 31 | + for (let i = 1; i <= Math.min(currentPage + 2, totalPages); i++) { |
| 32 | + pages.push(i); |
| 33 | + } |
| 34 | + } else { |
| 35 | + pages.push(1); |
| 36 | + pages.push('ellipsis'); |
72 | 37 |
|
73 | | - if (totalPages > maxVisiblePages) { |
74 | | - items.push( |
75 | | - <PaginationItem key="ellipsis-end"> |
76 | | - <PaginationEllipsis /> |
77 | | - </PaginationItem> |
78 | | - ); |
79 | | - items.push( |
80 | | - <PaginationItemLink |
81 | | - key={lastPage} |
82 | | - page={lastPage} |
83 | | - isActive={false} |
84 | | - onClick={() => onPageChange(lastPage)} |
85 | | - /> |
86 | | - ); |
| 38 | + const maxPage = Math.min(currentPage + 2, totalPages); |
| 39 | + for (let i = currentPage; i <= maxPage; i++) { |
| 40 | + pages.push(i); |
| 41 | + } |
87 | 42 | } |
88 | | - } else if (!showEndEllipsis) { |
89 | | - if (totalPages >= minimumPagesForEllipsis && currentPage !== 2) { |
90 | | - items.push( |
91 | | - <PaginationItemLink |
92 | | - key={firstPage} |
93 | | - page={firstPage} |
94 | | - isActive={false} |
95 | | - onClick={() => onPageChange(firstPage)} |
96 | | - /> |
97 | | - ); |
98 | | - items.push( |
99 | | - <PaginationItem key="ellipsis-start"> |
100 | | - <PaginationEllipsis /> |
101 | | - </PaginationItem> |
102 | | - ); |
103 | | - } |
104 | | - for (let i = totalPages - maxVisiblePages; i < totalPages; i++) { |
105 | | - items.push( |
106 | | - <PaginationItemLink |
107 | | - key={i} |
108 | | - page={i} |
109 | | - isActive={currentPage === i} |
110 | | - onClick={() => onPageChange(i)} |
111 | | - /> |
112 | | - ); |
113 | | - } |
114 | | - } else { |
115 | | - const middlePages = [currentPage - 1, currentPage, currentPage + 1].filter( |
116 | | - (p) => p >= 0 && p < totalPages |
117 | | - ); |
118 | 43 |
|
119 | | - items.push( |
120 | | - <PaginationItemLink |
121 | | - key={firstPage} |
122 | | - page={firstPage} |
123 | | - isActive={false} |
124 | | - onClick={() => onPageChange(firstPage)} |
125 | | - /> |
126 | | - ); |
127 | | - items.push( |
128 | | - <PaginationItem key="ellipsis-start"> |
129 | | - <PaginationEllipsis /> |
130 | | - </PaginationItem> |
131 | | - ); |
| 44 | + return pages; |
| 45 | + }; |
132 | 46 |
|
133 | | - middlePages.forEach((page) => { |
134 | | - items.push( |
135 | | - <PaginationItemLink |
136 | | - key={page} |
137 | | - page={page} |
138 | | - isActive={currentPage === page} |
139 | | - onClick={() => onPageChange(page)} |
140 | | - /> |
141 | | - ); |
142 | | - }); |
| 47 | + const pages = generatePages(); |
143 | 48 |
|
144 | | - items.push( |
145 | | - <PaginationItem key="ellipsis-end"> |
146 | | - <PaginationEllipsis /> |
147 | | - </PaginationItem> |
148 | | - ); |
149 | | - items.push( |
150 | | - <PaginationItemLink |
151 | | - key={lastPage} |
152 | | - page={lastPage} |
153 | | - isActive={false} |
154 | | - onClick={() => onPageChange(lastPage)} |
155 | | - /> |
156 | | - ); |
157 | | - } |
| 49 | + const handlePrevious = () => { |
| 50 | + if (currentPage > 1) onPageChange(currentPage - 1); |
| 51 | + }; |
158 | 52 |
|
159 | | - return items; |
160 | | -}; |
| 53 | + const handleNext = () => { |
| 54 | + if (currentPage < totalPages) onPageChange(currentPage + 1); |
| 55 | + }; |
161 | 56 |
|
162 | | -export function PaginatedNavigation({ |
163 | | - className, |
164 | | - totalPages, |
165 | | - currentPage, |
166 | | - onPageChange, |
167 | | -}: PaginatedNavigationProps) { |
168 | 57 | return ( |
169 | | - <Pagination className={cn(className, 'py-4')}> |
| 58 | + <Pagination> |
170 | 59 | <PaginationContent> |
171 | 60 | <PaginationItem> |
172 | 61 | <PaginationPrevious |
173 | | - className="rounded-lg" |
174 | | - disabled={currentPage <= 0} |
175 | | - onClick={() => currentPage > 0 && onPageChange(currentPage - 1)} |
| 62 | + onClick={handlePrevious} |
| 63 | + className={ |
| 64 | + currentPage === 1 ? 'pointer-events-none opacity-50' : '' |
| 65 | + } |
176 | 66 | /> |
177 | 67 | </PaginationItem> |
178 | | - {generatePaginationItems(totalPages, currentPage, onPageChange)} |
| 68 | + |
| 69 | + {pages.map((page, index) => |
| 70 | + page === 'ellipsis' ? ( |
| 71 | + <PaginationItem key={`ellipsis-${index}`}> |
| 72 | + <PaginationEllipsis /> |
| 73 | + </PaginationItem> |
| 74 | + ) : ( |
| 75 | + <PaginationItem key={page}> |
| 76 | + <PaginationLink |
| 77 | + isActive={page === currentPage} |
| 78 | + onClick={() => onPageChange(page)} |
| 79 | + > |
| 80 | + {page} |
| 81 | + </PaginationLink> |
| 82 | + </PaginationItem> |
| 83 | + ) |
| 84 | + )} |
| 85 | + |
179 | 86 | <PaginationItem> |
180 | 87 | <PaginationNext |
181 | | - className="rounded-lg" |
182 | | - disabled={currentPage >= totalPages - 1} |
183 | | - onClick={() => |
184 | | - currentPage < totalPages - 1 && onPageChange(currentPage + 1) |
| 88 | + onClick={handleNext} |
| 89 | + className={ |
| 90 | + currentPage === totalPages ? 'pointer-events-none opacity-50' : '' |
185 | 91 | } |
186 | 92 | /> |
187 | 93 | </PaginationItem> |
188 | 94 | </PaginationContent> |
189 | 95 | </Pagination> |
190 | 96 | ); |
191 | | -} |
| 97 | +}; |
0 commit comments