Created
October 17, 2019 07:22
-
-
Save RomiC/3601b19b23c5517c29e3c312dfd436d9 to your computer and use it in GitHub Desktop.
Pafination React component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import range from 'degiro-frontend-core/lib/utils/range'; | |
import React, {PureComponent} from 'react'; | |
import {NavLink} from 'react-router-dom'; | |
import {paginationLink, paginationLinkActive, paginationList, paginationListItem} from './pagination.css'; | |
interface PaginationProps { | |
/** | |
* Total amount of items | |
*/ | |
itemsTotal: number; | |
/** | |
* Current page | |
* @default 0 | |
*/ | |
currentPage?: number; | |
/** | |
* Items per page | |
* @default 10 | |
*/ | |
itemsPerPage?: number; | |
/** | |
* Amount of visible items (odd value recommended): | |
* [<- prev] 1 2 ... 32 33 34 ... 99 100 [next ->] | |
* | | |
* current page | |
* |______| | |
* visibleItems | |
* @default 3 | |
*/ | |
itemsVisible?: number; | |
/** | |
* Total amount of pages | |
* (will be calculated automatically if not provided) | |
*/ | |
pagesTotal?: number; | |
/** | |
* If true, show "<- prev" and "next ->" buttons | |
*/ | |
showPrevNextButtons?: boolean; | |
/** | |
* Function to generate links | |
* @param page Page number (starting from 1) | |
* @returns Link for the page | |
*/ | |
getPageLink?: (page: number) => string; | |
} | |
const itemsPerPageDefault: number = 10; | |
const itemsVisibleDefault: number = 3; | |
export default class Pagination extends PureComponent<PaginationProps> { | |
/** | |
* Return total amount of pages | |
* @returns {number} Amount of pages | |
*/ | |
private get pagesTotal(): number { | |
const {itemsPerPage = itemsPerPageDefault, itemsTotal, pagesTotal} = this.props; | |
const hasMoreThanOnePage = itemsPerPage >= 1 && itemsTotal; | |
return pagesTotal ? pagesTotal : hasMoreThanOnePage ? Math.ceil(itemsTotal / itemsPerPage) : 1; | |
} | |
/** | |
* Generate page ranges | |
* @returns {number[]} Page numbers range | |
*/ | |
private getPageRages(): number[] { | |
const lastPageIndex = this.pagesTotal - 1; | |
const {currentPage = 0} = this.props; | |
const itemsVisibleHalf = Math.floor((this.props.itemsVisible || itemsVisibleDefault) / 2); | |
const result: number[] = []; | |
const mergeThreshold = 2; | |
const startBlock = range(0, Math.min(itemsVisibleHalf, lastPageIndex)); | |
const middleBlock = range( | |
Math.max(0, currentPage - itemsVisibleHalf), | |
Math.min(lastPageIndex, currentPage + itemsVisibleHalf) | |
); | |
const endBlock = range(Math.max(0, lastPageIndex - itemsVisibleHalf), lastPageIndex); | |
for (const val of [ | |
...startBlock, | |
...(middleBlock[0] < endBlock[0] ? [...middleBlock, ...endBlock] : endBlock) | |
]) { | |
const lastElem = typeof result[result.length - 1] !== 'undefined' ? result[result.length - 1] : val - 1; | |
if (val > lastElem) { | |
const diff = val - lastElem; | |
if (diff === 1) { | |
result.push(val); | |
} else if (diff <= mergeThreshold) { | |
result.push(...range(lastElem + 1, val)); | |
} else { | |
result.push(Infinity, val); | |
} | |
} | |
} | |
return result; | |
} | |
/** | |
* Function to generate links for pages | |
* @param {number} page Page number (starting from 0) | |
* @returns {string} Link to page | |
*/ | |
private getPageLink(page: number) { | |
const {getPageLink} = this.props; | |
if (typeof getPageLink === 'function') { | |
return getPageLink(page); | |
} | |
return `?page=${page}`; | |
} | |
render() { | |
const {showPrevNextButtons = false, currentPage = 0} = this.props; | |
return ( | |
<nav> | |
<ul className={paginationList}> | |
{showPrevNextButtons && currentPage > 0 && ( | |
<li className={paginationListItem}> | |
<NavLink to={this.getPageLink(currentPage)} className={paginationLink}> | |
← | |
</NavLink> | |
</li> | |
)} | |
{this.getPageRages().map((page) => { | |
if (page === Infinity) { | |
return <li className={paginationListItem}>…</li>; | |
} | |
return ( | |
<li className={paginationListItem}> | |
{page !== currentPage ? ( | |
<NavLink to={this.getPageLink(page)} className={paginationLink}> | |
{page + 1} | |
</NavLink> | |
) : ( | |
<span className={paginationLinkActive}>{page + 1}</span> | |
)} | |
</li> | |
); | |
})} | |
{showPrevNextButtons && currentPage < this.pagesTotal - 1 && ( | |
<li className={paginationListItem}> | |
<NavLink to={this.getPageLink(currentPage + 1)} className={paginationLink}> | |
→ | |
</NavLink> | |
</li> | |
)} | |
</ul> | |
</nav> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment