@@ -13,7 +13,18 @@ const Wrapper = styled.div`
13
13
gap : ${ spacingsPx . xxxs } ;
14
14
` ;
15
15
16
- const PageItem = styled . div < { $isActive ?: boolean } > `
16
+ const Ellipsis = styled . div `
17
+ display : flex;
18
+ align-items : center;
19
+ justify-content : center;
20
+ width : ${ spacingsPx . xxl } ;
21
+ height : ${ spacingsPx . xxl } ;
22
+ padding : ${ spacingsPx . xxs } ${ spacingsPx . xs } ;
23
+ text-align : center;
24
+ ${ typography . hint } ;
25
+ ` ;
26
+
27
+ const PageItem = styled . div < { $isActive ?: boolean ; $isDisabled ?: boolean } > `
17
28
display: flex;
18
29
align-items: center;
19
30
justify-content: center;
@@ -31,14 +42,22 @@ const PageItem = styled.div<{ $isActive?: boolean }>`
31
42
${ typography . hint } ;
32
43
cursor: pointer;
33
44
34
- ${ ( { $isActive, theme } ) =>
45
+ ${ ( { $isActive, $isDisabled , theme } ) =>
35
46
! $isActive &&
47
+ ! $isDisabled &&
36
48
css `
37
49
& : hover {
38
50
background : ${ theme . backgroundTertiaryDefaultOnElevation0 } ;
39
51
color : ${ theme . textOnTertiary } ;
40
52
}
41
53
` } ;
54
+
55
+ ${ ( { $isDisabled } ) =>
56
+ $isDisabled &&
57
+ css `
58
+ color : ${ ( { theme } ) => theme . textDisabled } ;
59
+ cursor : not-allowed;
60
+ ` } ;
42
61
` ;
43
62
44
63
const Actions = styled . div < { $isActive : boolean } > `
@@ -56,27 +75,90 @@ interface PaginationProps {
56
75
onPageSelected : ( page : number ) => void ;
57
76
}
58
77
78
+ const SLIDING_WINDOW_SIZE = 7 ; // should be an even number, so that the active page can be centered
79
+ const calculatePages = ( {
80
+ currentPage,
81
+ totalPages,
82
+ } : {
83
+ currentPage : number ;
84
+ totalPages : number ;
85
+ } ) => {
86
+ const calculatedPages : ( number | null ) [ ] = [ ] ;
87
+
88
+ // center the current page
89
+ let windowBeginning = currentPage - Math . floor ( ( SLIDING_WINDOW_SIZE - 1 ) / 2 ) ;
90
+
91
+ // prevent window overflow on the right
92
+ if ( windowBeginning + SLIDING_WINDOW_SIZE > totalPages )
93
+ windowBeginning = totalPages - ( SLIDING_WINDOW_SIZE - 1 ) ;
94
+
95
+ // prevent window overflow on the left
96
+ if ( windowBeginning < 1 ) windowBeginning = 1 ;
97
+
98
+ for (
99
+ let page = windowBeginning ;
100
+ page < windowBeginning + SLIDING_WINDOW_SIZE && page <= totalPages ;
101
+ page ++
102
+ ) {
103
+ const indexInWindow = calculatedPages . length ;
104
+
105
+ // first button override
106
+ if ( indexInWindow === 0 ) {
107
+ calculatedPages . push ( 1 ) ;
108
+ continue ;
109
+ }
110
+
111
+ // second button override
112
+ if ( indexInWindow === 1 && page !== 2 ) {
113
+ calculatedPages . push ( null ) ;
114
+ continue ;
115
+ }
116
+
117
+ // second to last button override
118
+ if (
119
+ indexInWindow === SLIDING_WINDOW_SIZE - 2 &&
120
+ page !== totalPages - 1 &&
121
+ totalPages > SLIDING_WINDOW_SIZE
122
+ ) {
123
+ calculatedPages . push ( null ) ;
124
+ continue ;
125
+ }
126
+
127
+ // last button override
128
+ if ( indexInWindow === SLIDING_WINDOW_SIZE - 1 && page !== totalPages ) {
129
+ calculatedPages . push ( totalPages ) ;
130
+ continue ;
131
+ }
132
+
133
+ calculatedPages . push ( page ) ;
134
+ }
135
+
136
+ return calculatedPages ;
137
+ } ;
138
+
59
139
export const Pagination = ( {
60
140
currentPage,
61
141
onPageSelected,
62
142
hasPages = true ,
63
- isLastPage,
143
+ isLastPage : _isLastPage ,
64
144
perPage,
65
145
totalItems,
66
146
...rest
67
147
} : PaginationProps ) => {
68
148
const totalPages = Math . ceil ( totalItems / perPage ) ;
69
- const showPrevious = currentPage > 1 ;
70
- // array of int used for creating all page buttons
149
+ const isFirstPage = currentPage === 1 ;
150
+ const isLastPage = hasPages ? currentPage === totalPages : _isLastPage ;
151
+
152
+ // array of pages to be rendered as buttons
71
153
const calculatedPages = useMemo (
72
- ( ) => [ ... Array ( totalPages ) ] . map ( ( _p , i ) => i + 1 ) ,
73
- [ totalPages ] ,
154
+ ( ) => calculatePages ( { currentPage , totalPages } ) ,
155
+ [ currentPage , totalPages ] ,
74
156
) ;
75
157
76
158
if ( ! hasPages ) {
77
159
return (
78
160
< Wrapper { ...rest } >
79
- < Actions $isActive = { showPrevious } >
161
+ < Actions $isActive = { ! isFirstPage } >
80
162
< PageItem onClick = { ( ) => onPageSelected ( currentPage - 1 ) } >
81
163
‹ < Translation id = "TR_PAGINATION_NEWER" />
82
164
</ PageItem >
@@ -92,23 +174,39 @@ export const Pagination = ({
92
174
93
175
return (
94
176
< Wrapper { ...rest } >
95
- < Actions $isActive = { showPrevious } >
96
- { currentPage > 2 && < PageItem onClick = { ( ) => onPageSelected ( 1 ) } > «</ PageItem > }
97
- < PageItem onClick = { ( ) => onPageSelected ( currentPage - 1 ) } > ‹</ PageItem >
177
+ < Actions $isActive = { true } >
178
+ < PageItem
179
+ $isDisabled = { isFirstPage }
180
+ onClick = { ! isFirstPage ? ( ) => onPageSelected ( 1 ) : undefined }
181
+ >
182
+ «
183
+ </ PageItem >
184
+ < PageItem
185
+ $isDisabled = { isFirstPage }
186
+ onClick = { ! isFirstPage ? ( ) => onPageSelected ( currentPage - 1 ) : undefined }
187
+ >
188
+ ‹
189
+ </ PageItem >
98
190
</ Actions >
99
191
100
192
{ totalPages ? (
101
- calculatedPages . map ( i => (
102
- < PageItem
103
- key = { i }
104
- data-testid = { `@wallet/accounts/pagination/${ i } ` }
105
- data-test-activated = { i === currentPage }
106
- onClick = { ( ) => onPageSelected ( i ) }
107
- $isActive = { i === currentPage }
108
- >
109
- { i }
110
- </ PageItem >
111
- ) )
193
+ < >
194
+ { calculatedPages . map ( ( page , i ) =>
195
+ page === null ? (
196
+ < Ellipsis key = { i } > …</ Ellipsis >
197
+ ) : (
198
+ < PageItem
199
+ key = { i }
200
+ data-testid = { `@wallet/accounts/pagination/${ page } ` }
201
+ data-test-activated = { page === currentPage }
202
+ onClick = { ( ) => onPageSelected ( page ) }
203
+ $isActive = { page === currentPage }
204
+ >
205
+ { page }
206
+ </ PageItem >
207
+ ) ,
208
+ ) }
209
+ </ >
112
210
) : (
113
211
< >
114
212
{ [ ...Array ( currentPage - 1 ) ] . map ( ( _p , i ) => (
@@ -129,10 +227,20 @@ export const Pagination = ({
129
227
</ >
130
228
) }
131
229
132
- < Actions $isActive = { currentPage < ( totalPages || 1 ) } >
133
- < PageItem onClick = { ( ) => onPageSelected ( currentPage + 1 ) } > ›</ PageItem >
134
- { totalPages && totalPages > 2 && (
135
- < PageItem onClick = { ( ) => onPageSelected ( totalPages ) } > »</ PageItem >
230
+ < Actions $isActive = { true } >
231
+ < PageItem
232
+ $isDisabled = { isLastPage }
233
+ onClick = { ! isLastPage ? ( ) => onPageSelected ( currentPage + 1 ) : undefined }
234
+ >
235
+ ›
236
+ </ PageItem >
237
+ { totalPages > 0 && (
238
+ < PageItem
239
+ $isDisabled = { isLastPage }
240
+ onClick = { ! isLastPage ? ( ) => onPageSelected ( totalPages ) : undefined }
241
+ >
242
+ »
243
+ </ PageItem >
136
244
) }
137
245
</ Actions >
138
246
</ Wrapper >
0 commit comments