import React, {Fragment, useState} from 'react'; const SPACE_A = 'SPACE_A'; const SPACE_B = 'SPACE_B'; /** * Helper method for creating a range of numbers * range(1, 5) => [1, 2, 3, 4, 5] */ const range = (from, to, step = 1) => { let i = from; const range = []; while (i <= to) { range.push(i); i += step; } return range; } export const Paginate = (props) => { const pageLimit = typeof props.pageLimit === 'number' ? props.pageLimit : 30; const totalRecords = typeof props.totalRecords === 'number' ? props.totalRecords : 0; const pageNeighbours = typeof props.pageNeighbours === 'number' ? Math.max(0, Math.min(props.pageNeighbours, 2)) : 0; const totalPages = Math.ceil(totalRecords / pageLimit); const [currentPage, setCurrentPage] = useState(typeof props.currentPage === 'number' ? props.currentPage : 1); let hasLeft = false; let hasRight = false; const gotoPage = (page) => { const currentPage = Math.max(0, Math.min(page, totalPages)); props.onPageChanged(page) setCurrentPage(currentPage); } const handleClick = (page) => (evt) => { evt.preventDefault(); document.getElementById('root').scrollIntoView({behavior: 'smooth'}); if (currentPage !== page) gotoPage(page); } const handleMoveLeft = (evt) => { evt.preventDefault(); gotoPage(currentPage - 1); } const handleMoveRight = (evt) => { evt.preventDefault(); gotoPage(currentPage + 1); } const makePageLink = (page) => props.makeLink(page); /** * Let's say we have 10 pages and we set pageNeighbours to 2 * Given that the current page is 6 * The pagination control will look like the following: * * (1) < {4 5} [6] {7 8} > (10) * * (x) => terminal pages: first and last page(always visible) * [x] => represents current page * {...x} => represents page neighbours */ const fetchPageNumbers = () => { /** * totalNumbers: the total page numbers to show on the control * totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls */ const totalNumbers = (pageNeighbours * 2) + 3; const totalBlocks = totalNumbers + 2; if (totalPages > totalBlocks) { const startPage = Math.max(2, currentPage - pageNeighbours); const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours); let pages = range(startPage, endPage); /** * hasLeftSpill: has hidden pages to the left * hasRightSpill: has hidden pages to the right * spillOffset: number of hidden pages either to the left or to the right */ const hasLeftSpill = startPage > 2; const hasRightSpill = (totalPages - endPage) > 1; const spillOffset = totalNumbers - (pages.length + 1); // handle: (1) < {5 6} [7] {8 9} (10) if (hasLeftSpill && !hasRightSpill) { const extraPages = range(startPage - spillOffset, startPage - 1); hasLeft = true; pages = [1, SPACE_A, ...extraPages, ...pages]; // handle: (1) {2 3} [4] {5 6} > (10) } else if (!hasLeftSpill && hasRightSpill) { const extraPages = range(endPage + 1, endPage + spillOffset); hasRight = true; pages = [1, ...pages, ...extraPages, SPACE_B, totalPages]; // handle: (1) < {4 5} [6] {7 8} > (10) } else if (hasLeftSpill && hasRightSpill) { hasLeft = true; hasRight = true; pages = [1, SPACE_A, ...pages, SPACE_B, totalPages]; } return pages; } return range(1, totalPages); } if (!totalRecords || totalPages === 1) return null; const pages = fetchPageNumbers(); const blocks = pages.map((page, index) => { if (page === SPACE_A || page === SPACE_B) return ( ); return (
  • {page}
  • ); }) return ( ); }