@@ -55,9 +55,9 @@ export const props = {
5555 type : [ Number , String ] ,
5656 default : null ,
5757 validator ( value ) /* istanbul ignore next */ {
58- const num = toInteger ( value )
59- if ( ! isNull ( value ) && ( isNaN ( num ) || num < 1 ) ) {
60- warn ( 'pagination: v-model value must be a number greater than 0 ' )
58+ const number = toInteger ( value )
59+ if ( ! isNull ( value ) && ( isNaN ( number ) || number < 1 ) ) {
60+ warn ( '" v-model" value must be a number greater than "0"' , 'BPagination ')
6161 return false
6262 }
6363 return true
@@ -67,9 +67,9 @@ export const props = {
6767 type : [ Number , String ] ,
6868 default : DEFAULT_LIMIT ,
6969 validator ( value ) /* istanbul ignore next */ {
70- const num = toInteger ( value )
71- if ( isNaN ( num ) || num < 1 ) {
72- warn ( 'pagination: prop "limit" must be a number greater than 0 ' )
70+ const number = toInteger ( value )
71+ if ( isNaN ( number ) || number < 1 ) {
72+ warn ( 'Prop "limit" must be a number greater than "0"' , 'BPagination ')
7373 return false
7474 }
7575 return true
@@ -99,6 +99,14 @@ export const props = {
9999 type : String ,
100100 default : '\u00AB' // '«'
101101 } ,
102+ firstNumber : {
103+ type : Boolean ,
104+ default : false
105+ } ,
106+ firstClass : {
107+ type : [ String , Array , Object ] ,
108+ default : null
109+ } ,
102110 labelPrevPage : {
103111 type : String ,
104112 default : 'Go to previous page'
@@ -107,6 +115,10 @@ export const props = {
107115 type : String ,
108116 default : '\u2039' // '‹'
109117 } ,
118+ prevClass : {
119+ type : [ String , Array , Object ] ,
120+ default : null
121+ } ,
110122 labelNextPage : {
111123 type : String ,
112124 default : 'Go to next page'
@@ -115,6 +127,10 @@ export const props = {
115127 type : String ,
116128 default : '\u203A' // '›'
117129 } ,
130+ nextClass : {
131+ type : [ String , Array , Object ] ,
132+ default : null
133+ } ,
118134 labelLastPage : {
119135 type : String ,
120136 default : 'Go to last page'
@@ -123,17 +139,33 @@ export const props = {
123139 type : String ,
124140 default : '\u00BB' // '»'
125141 } ,
142+ lastNumber : {
143+ type : Boolean ,
144+ default : false
145+ } ,
146+ lastClass : {
147+ type : [ String , Array , Object ] ,
148+ default : null
149+ } ,
126150 labelPage : {
127151 type : [ String , Function ] ,
128152 default : 'Go to page'
129153 } ,
154+ pageClass : {
155+ type : [ String , Array , Object ] ,
156+ default : null
157+ } ,
130158 hideEllipsis : {
131159 type : Boolean ,
132160 default : false
133161 } ,
134162 ellipsisText : {
135163 type : String ,
136164 default : '\u2026' // '…'
165+ } ,
166+ ellipsisClass : {
167+ type : [ String , Array , Object ] ,
168+ default : null
137169 }
138170}
139171
@@ -165,8 +197,8 @@ export default {
165197 } else if ( align === 'end' || align === 'right' ) {
166198 return 'justify-content-end'
167199 } else if ( align === 'fill' ) {
168- // The page-items will also have 'flex-fill' added.
169- // We ad text centering to make the button appearance better in fill mode.
200+ // The page-items will also have 'flex-fill' added
201+ // We add text centering to make the button appearance better in fill mode
170202 return 'text-center'
171203 }
172204 return ''
@@ -289,14 +321,13 @@ export default {
289321 } ,
290322 methods : {
291323 handleKeyNav ( evt ) {
292- const keyCode = evt . keyCode
293- const shift = evt . shiftKey
324+ const { keyCode, shiftKey } = evt
294325 if ( keyCode === KeyCodes . LEFT || keyCode === KeyCodes . UP ) {
295326 evt . preventDefault ( )
296- shift ? this . focusFirst ( ) : this . focusPrev ( )
327+ shiftKey ? this . focusFirst ( ) : this . focusPrev ( )
297328 } else if ( keyCode === KeyCodes . RIGHT || keyCode === KeyCodes . DOWN ) {
298329 evt . preventDefault ( )
299- shift ? this . focusLast ( ) : this . focusNext ( )
330+ shiftKey ? this . focusLast ( ) : this . focusNext ( )
300331 }
301332 } ,
302333 getButtons ( ) {
@@ -307,7 +338,7 @@ export default {
307338 btn . focus ( )
308339 } ,
309340 focusCurrent ( ) {
310- // We do this in next tick to ensure buttons have finished rendering
341+ // We do this in `$nextTick()` to ensure buttons have finished rendering
311342 this . $nextTick ( ( ) => {
312343 const btn = this . getButtons ( ) . find (
313344 el => toInteger ( getAttr ( el , 'aria-posinset' ) ) === this . computedCurrentPage
@@ -321,7 +352,7 @@ export default {
321352 } )
322353 } ,
323354 focusFirst ( ) {
324- // We do this in next tick to ensure buttons have finished rendering
355+ // We do this in `$nextTick()` to ensure buttons have finished rendering
325356 this . $nextTick ( ( ) => {
326357 const btn = this . getButtons ( ) . find ( el => ! isDisabled ( el ) )
327358 if ( btn && btn . focus && btn !== document . activeElement ) {
@@ -330,7 +361,7 @@ export default {
330361 } )
331362 } ,
332363 focusLast ( ) {
333- // We do this in next tick to ensure buttons have finished rendering
364+ // We do this in `$nextTick()` to ensure buttons have finished rendering
334365 this . $nextTick ( ( ) => {
335366 const btn = this . getButtons ( )
336367 . reverse ( )
@@ -341,7 +372,7 @@ export default {
341372 } )
342373 } ,
343374 focusPrev ( ) {
344- // We do this in next tick to ensure buttons have finished rendering
375+ // We do this in `$nextTick()` to ensure buttons have finished rendering
345376 this . $nextTick ( ( ) => {
346377 const buttons = this . getButtons ( )
347378 const idx = buttons . indexOf ( document . activeElement )
@@ -351,7 +382,7 @@ export default {
351382 } )
352383 } ,
353384 focusNext ( ) {
354- // We do this in next tick to ensure buttons have finished rendering
385+ // We do this in `$nextTick()` to ensure buttons have finished rendering
355386 this . $nextTick ( ( ) => {
356387 const buttons = this . getButtons ( )
357388 const idx = buttons . indexOf ( document . activeElement )
@@ -365,19 +396,20 @@ export default {
365396 render ( h ) {
366397 const buttons = [ ]
367398 const numberOfPages = this . localNumberOfPages
399+ const pageNumbers = this . pageList . map ( p => p . number )
368400 const disabled = this . disabled
369401 const { showFirstDots, showLastDots } = this . paginationParams
370402 const currentPage = this . computedCurrentPage
371403 const fill = this . align === 'fill'
372404
373405 // Helper function and flag
374406 const isActivePage = pageNum => pageNum === currentPage
375- const noCurrPage = this . currentPage < 1
407+ const noCurrentPage = this . currentPage < 1
376408
377409 // Factory function for prev/next/first/last buttons
378- const makeEndBtn = ( linkTo , ariaLabel , btnSlot , btnText , pageTest , key ) => {
410+ const makeEndBtn = ( linkTo , ariaLabel , btnSlot , btnText , btnClass , pageTest , key ) => {
379411 const isDisabled =
380- disabled || isActivePage ( pageTest ) || noCurrPage || linkTo < 1 || linkTo > numberOfPages
412+ disabled || isActivePage ( pageTest ) || noCurrentPage || linkTo < 1 || linkTo > numberOfPages
381413 const pageNum = linkTo < 1 ? 1 : linkTo > numberOfPages ? numberOfPages : linkTo
382414 const scope = { disabled : isDisabled , page : pageNum , index : pageNum - 1 }
383415 const btnContent = this . normalizeSlot ( btnSlot , scope ) || toString ( btnText ) || h ( )
@@ -409,7 +441,7 @@ export default {
409441 {
410442 key,
411443 staticClass : 'page-item' ,
412- class : { disabled : isDisabled , 'flex-fill' : fill } ,
444+ class : [ { disabled : isDisabled , 'flex-fill' : fill } , btnClass ] ,
413445 attrs : {
414446 role : 'presentation' ,
415447 'aria-hidden' : isDisabled ? 'true' : null
@@ -426,7 +458,7 @@ export default {
426458 {
427459 key : `ellipsis-${ isLast ? 'last' : 'first' } ` ,
428460 staticClass : 'page-item' ,
429- class : [ 'disabled' , 'bv-d-xs-down-none' , fill ? 'flex-fill' : '' ] ,
461+ class : [ 'disabled' , 'bv-d-xs-down-none' , fill ? 'flex-fill' : '' , this . ellipsisClass ] ,
430462 attrs : { role : 'separator' }
431463 } ,
432464 [
@@ -437,33 +469,47 @@ export default {
437469 )
438470 }
439471
440- // Goto First Page button bookend
441- buttons . push (
442- this . hideGotoEndButtons
472+ // Goto first page button bookend
473+ // Don't render button when `hideGotoEndButtons` is set or when
474+ // `firstNumber` is enabled and the first page is in the page list
475+ const $firstPageBtn =
476+ this . hideGotoEndButtons || ( this . firstNumber && pageNumbers . indexOf ( 1 ) !== - 1 )
443477 ? h ( )
444- : makeEndBtn ( 1 , this . labelFirstPage , 'first-text' , this . firstText , 1 , 'bookend-goto-first' )
478+ : makeEndBtn (
479+ 1 ,
480+ this . labelFirstPage ,
481+ 'first-text' ,
482+ this . firstNumber ? '1' : this . firstText ,
483+ this . firstClass ,
484+ 1 ,
485+ 'bookend-goto-first'
486+ )
487+
488+ // Goto previous page button bookend
489+ const $prevPageBtn = makeEndBtn (
490+ currentPage - 1 ,
491+ this . labelPrevPage ,
492+ 'prev-text' ,
493+ this . prevText ,
494+ this . prevClass ,
495+ 1 ,
496+ 'bookend-goto-prev'
445497 )
446498
447- // Goto Previous page button bookend
499+ // When `firstNumber` prop is set we move the previous page button
500+ // before the first page button
448501 buttons . push (
449- makeEndBtn (
450- currentPage - 1 ,
451- this . labelPrevPage ,
452- 'prev-text' ,
453- this . prevText ,
454- 1 ,
455- 'bookend-goto-prev'
456- )
502+ ...( this . firstNumber ? [ $prevPageBtn , $firstPageBtn ] : [ $firstPageBtn , $prevPageBtn ] )
457503 )
458504
459505 // First Ellipsis Bookend
460506 buttons . push ( showFirstDots ? makeEllipsis ( false ) : h ( ) )
461507
462- // Individual Page links
508+ // Individual page links
463509 this . pageList . forEach ( ( page , idx ) => {
464- const active = isActivePage ( page . number ) && ! noCurrPage
510+ const active = isActivePage ( page . number ) && ! noCurrentPage
465511 // Active page will have tabindex of 0, or if no current page and first page button
466- const tabIndex = disabled ? null : active || ( noCurrPage && idx === 0 ) ? '0' : '-1'
512+ const tabIndex = disabled ? null : active || ( noCurrentPage && idx === 0 ) ? '0' : '-1'
467513 const attrs = {
468514 role : 'menuitemradio' ,
469515 'aria-disabled' : disabled ? 'true' : null ,
@@ -508,42 +554,47 @@ export default {
508554 {
509555 key : `page-${ page . number } ` ,
510556 staticClass : 'page-item' ,
511- class : [ { disabled, active, 'flex-fill' : fill } , page . classes ] ,
557+ class : [ { disabled, active, 'flex-fill' : fill } , page . classes , this . pageClass ] ,
512558 attrs : { role : 'presentation' }
513559 } ,
514560 [ inner ]
515561 )
516562 )
517563 } )
518564
519- // Last Ellipsis Bookend
565+ // Last ellipsis bookend
520566 buttons . push ( showLastDots ? makeEllipsis ( true ) : h ( ) )
521567
522- // Goto Next page button bookend
523- buttons . push (
524- makeEndBtn (
525- currentPage + 1 ,
526- this . labelNextPage ,
527- 'next-text' ,
528- this . nextText ,
529- numberOfPages ,
530- 'bookend-goto-next'
531- )
568+ // Goto next page button bookend
569+ const $nextPageBtn = makeEndBtn (
570+ currentPage + 1 ,
571+ this . labelNextPage ,
572+ 'next-text' ,
573+ this . nextText ,
574+ this . nextClass ,
575+ numberOfPages ,
576+ 'bookend-goto-next'
532577 )
533578
534- // Goto Last Page button bookend
535- buttons . push (
536- this . hideGotoEndButtons
579+ // Goto last page button bookend
580+ // Don't render button when `hideGotoEndButtons` is set or when
581+ // `lastNumber` is enabled and the last page is in the page list
582+ const $lastPageBtn =
583+ this . hideGotoEndButtons || ( this . lastNumber && pageNumbers . indexOf ( numberOfPages ) !== - 1 )
537584 ? h ( )
538585 : makeEndBtn (
539586 numberOfPages ,
540587 this . labelLastPage ,
541588 'last-text' ,
542- this . lastText ,
589+ this . lastNumber ? toString ( numberOfPages ) : this . lastText ,
590+ this . lastClass ,
543591 numberOfPages ,
544592 'bookend-goto-last'
545593 )
546- )
594+
595+ // When `lastNumber` prop is set we move the next page button
596+ // after the last page button
597+ buttons . push ( ...( this . lastNumber ? [ $lastPageBtn , $nextPageBtn ] : [ $nextPageBtn , $lastPageBtn ] ) )
547598
548599 // Assemble the pagination buttons
549600 const pagination = h (
0 commit comments