1
1
import { ChevronLeftIcon , ChevronRightIcon } from '@primer/octicons-react'
2
2
import type React from 'react'
3
- import { useCallback , useState } from 'react'
3
+ import { useCallback , useMemo , useState } from 'react'
4
4
import styled from 'styled-components'
5
5
import { get } from '../constants'
6
6
import { Button } from '../internal/components/ButtonReset'
@@ -9,6 +9,7 @@ import {VisuallyHidden} from '../VisuallyHidden'
9
9
import { warning } from '../utils/warning'
10
10
import type { ResponsiveValue } from '../hooks/useResponsiveValue'
11
11
import { viewportRanges } from '../hooks/useResponsiveValue'
12
+ import { buildPaginationModel } from '../Pagination/model'
12
13
13
14
const StyledPagination = styled . nav `
14
15
display: flex;
@@ -166,12 +167,6 @@ export type PaginationProps = Omit<React.ComponentPropsWithoutRef<'nav'>, 'onCha
166
167
totalCount : number
167
168
}
168
169
169
- /**
170
- * Specifies the maximum number of items in between the first and last page,
171
- * including truncated steps
172
- */
173
- const MAX_TRUNCATED_STEP_COUNT = 7
174
-
175
170
export function Pagination ( {
176
171
'aria-label' : label ,
177
172
defaultPageIndex,
@@ -197,19 +192,7 @@ export function Pagination({
197
192
pageSize,
198
193
totalCount,
199
194
} )
200
- const truncatedPageCount = pageCount > 2 ? Math . min ( pageCount - 2 , MAX_TRUNCATED_STEP_COUNT ) : 0
201
- const defaultOffset = getDefaultOffsetStartIndex ( pageIndex , pageCount , truncatedPageCount )
202
- const [ defaultOffsetStartIndex , setDefaultOffsetStartIndex ] = useState ( defaultOffset )
203
- const [ offsetStartIndex , setOffsetStartIndex ] = useState ( defaultOffsetStartIndex )
204
-
205
- if ( defaultOffsetStartIndex !== defaultOffset ) {
206
- setOffsetStartIndex ( defaultOffset )
207
- setDefaultOffsetStartIndex ( defaultOffset )
208
- }
209
195
210
- const offsetEndIndex = offsetStartIndex + truncatedPageCount - 1
211
- const hasLeadingTruncation = offsetStartIndex >= 2
212
- const hasTrailingTruncation = pageCount - 1 - offsetEndIndex > 1
213
196
const getViewportRangesToHidePages = useCallback ( ( ) => {
214
197
if ( typeof showPages !== 'boolean' ) {
215
198
return Object . keys ( showPages ) . filter ( key => ! showPages [ key as keyof typeof viewportRanges ] ) as Array <
@@ -224,6 +207,10 @@ export function Pagination({
224
207
}
225
208
} , [ showPages ] )
226
209
210
+ const model = useMemo ( ( ) => {
211
+ return buildPaginationModel ( pageCount , pageIndex + 1 , ! ! showPages , 1 , 2 )
212
+ } , [ pageCount , pageIndex , showPages ] )
213
+
227
214
return (
228
215
< LiveRegion >
229
216
< LiveRegionOutlet />
@@ -240,77 +227,33 @@ export function Pagination({
240
227
if ( ! hasPreviousPage ) {
241
228
return
242
229
}
243
-
244
230
selectPreviousPage ( )
245
- if ( hasLeadingTruncation ) {
246
- if ( pageIndex - 1 < offsetStartIndex + 1 ) {
247
- setOffsetStartIndex ( offsetStartIndex - 1 )
248
- }
249
- }
250
231
} }
251
232
>
252
233
{ hasPreviousPage ? < ChevronLeftIcon /> : null }
253
234
< span className = "TablePaginationActionLabel" > Previous</ span >
254
235
< VisuallyHidden > page</ VisuallyHidden >
255
236
</ Button >
256
237
</ Step >
257
- { pageCount > 0 ? (
258
- < Step >
259
- < Page
260
- active = { pageIndex === 0 }
261
- onClick = { ( ) => {
262
- selectPage ( 0 )
263
- if ( pageCount > 1 ) {
264
- setOffsetStartIndex ( 1 )
265
- }
266
- } }
267
- >
268
- { 1 }
269
- { hasLeadingTruncation ? < VisuallyHidden > …</ VisuallyHidden > : null }
270
- </ Page >
271
- </ Step >
272
- ) : null }
273
- { pageCount > 2
274
- ? Array . from ( { length : truncatedPageCount } ) . map ( ( _ , i ) => {
275
- if ( i === 0 && hasLeadingTruncation ) {
276
- return < TruncationStep key = { `truncation-${ i } ` } />
277
- }
278
-
279
- if ( i === truncatedPageCount - 1 && hasTrailingTruncation ) {
280
- return < TruncationStep key = { `truncation-${ i } ` } />
281
- }
282
-
283
- const page = offsetStartIndex + i
284
- return (
285
- < Step key = { i } >
286
- < Page
287
- active = { pageIndex === page }
288
- onClick = { ( ) => {
289
- selectPage ( page )
290
- } }
291
- >
292
- { page + 1 }
293
- { i === truncatedPageCount - 2 && hasTrailingTruncation ? (
294
- < VisuallyHidden > …</ VisuallyHidden >
295
- ) : null }
296
- </ Page >
297
- </ Step >
298
- )
299
- } )
300
- : null }
301
- { pageCount > 1 ? (
302
- < Step >
303
- < Page
304
- active = { pageIndex === pageCount - 1 }
305
- onClick = { ( ) => {
306
- selectPage ( pageCount - 1 )
307
- setOffsetStartIndex ( pageCount - 1 - truncatedPageCount )
308
- } }
309
- >
310
- { pageCount }
311
- </ Page >
312
- </ Step >
313
- ) : null }
238
+ { model . map ( ( page , i ) => {
239
+ if ( page . type === 'BREAK' ) {
240
+ return < TruncationStep key = { `truncation-${ i } ` } />
241
+ } else if ( page . type === 'NUM' ) {
242
+ return (
243
+ < Step key = { i } >
244
+ < Page
245
+ active = { ! ! page . selected }
246
+ onClick = { ( ) => {
247
+ selectPage ( page . num )
248
+ } }
249
+ >
250
+ { page . num }
251
+ { page . precedesBreak ? < VisuallyHidden > …</ VisuallyHidden > : null }
252
+ </ Page >
253
+ </ Step >
254
+ )
255
+ }
256
+ } ) }
314
257
< Step >
315
258
< Button
316
259
className = "TablePaginationAction"
@@ -321,13 +264,7 @@ export function Pagination({
321
264
if ( ! hasNextPage ) {
322
265
return
323
266
}
324
-
325
267
selectNextPage ( )
326
- if ( hasTrailingTruncation ) {
327
- if ( pageIndex + 1 > offsetEndIndex - 1 ) {
328
- setOffsetStartIndex ( offsetStartIndex + 1 )
329
- }
330
- }
331
268
} }
332
269
>
333
270
< span className = "TablePaginationActionLabel" > Next</ span >
@@ -341,30 +278,6 @@ export function Pagination({
341
278
)
342
279
}
343
280
344
- function getDefaultOffsetStartIndex ( pageIndex : number , pageCount : number , truncatedPageCount : number ) : number {
345
- // When the current page is closer to the end of the list than the beginning
346
- if ( pageIndex > pageCount - 1 - pageIndex ) {
347
- if ( pageCount - 1 - pageIndex >= truncatedPageCount ) {
348
- return Math . max ( pageIndex - 3 , 1 )
349
- }
350
- return Math . max ( pageCount - 1 - truncatedPageCount , 1 )
351
- }
352
-
353
- // When the current page is closer to the beginning of the list than the end
354
- if ( pageIndex < pageCount - 1 - pageIndex ) {
355
- if ( pageIndex >= truncatedPageCount ) {
356
- return Math . max ( pageIndex - 3 , 1 )
357
- }
358
- return 1
359
- }
360
-
361
- // When the current page is the midpoint between the beginning and the end
362
- if ( pageIndex < truncatedPageCount ) {
363
- return pageIndex
364
- }
365
- return Math . max ( pageIndex - 3 , 1 )
366
- }
367
-
368
281
type RangeProps = {
369
282
pageStart : number
370
283
pageEnd : number
0 commit comments