Created
February 20, 2019 18:41
-
-
Save alobato/6779ccc4d6bbb74322266ffd4d1c2d2a to your computer and use it in GitHub Desktop.
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 React, { memo, useState, useEffect } from 'react' | |
import memoize from 'memoize-one' | |
import isEqual from 'lodash.isequal' | |
const equalFn = (newArg, lastArg) => isEqual(newArg, lastArg) | |
const range = (start, end) => [...Array(end - start).keys()].map(k => k + start) | |
const getPager = memoize((totalItems, currentPage, pageSize) => { | |
// calculate total pages | |
const totalPages = Math.ceil(totalItems / pageSize) | |
let startPage, endPage | |
if (totalPages <= 10) { | |
// less than 10 total pages so show all | |
startPage = 1 | |
endPage = totalPages | |
} else { | |
// more than 10 total pages so calculate start and end pages | |
if (currentPage <= 6) { | |
startPage = 1 | |
endPage = 10 | |
} else if (currentPage + 4 >= totalPages) { | |
startPage = totalPages - 9 | |
endPage = totalPages | |
} else { | |
startPage = currentPage - 5 | |
endPage = currentPage + 4 | |
} | |
} | |
// calculate start and end item indexes | |
const startIndex = (currentPage - 1) * pageSize | |
const endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1) | |
// create an array of pages | |
const pages = range(startPage, endPage + 1) | |
// return object with all pager properties required by the view | |
return { | |
totalItems: totalItems, | |
currentPage: currentPage, | |
pageSize: pageSize, | |
totalPages: totalPages, | |
startPage: startPage, | |
endPage: endPage, | |
startIndex: startIndex, | |
endIndex: endIndex, | |
pages: pages | |
} | |
}) | |
const getPageOfItems = memoize((items, pager) => items.slice(pager.startIndex, pager.endIndex + 1), equalFn) | |
const Pagination = memo(({ initialPage, pageSize, items, previousLabel, nextLabel, fistLabel, lastLabel, onChange, ...rest }) => { | |
const processedItems = () => items | |
const genPager = () => getPager(processedItems().length, initialPage, pageSize) | |
const [pager, setPager] = useState(genPager()) | |
useEffect(() => { | |
const newPager = genPager() | |
setPager(newPager) | |
const pageOfItems = getPageOfItems(processedItems(), newPager) | |
onChange(pageOfItems) | |
}, [JSON.stringify(items)] | |
) | |
const setPage = page => { | |
const newPager = getPager(processedItems().length, page, pageSize) | |
setPager(newPager) | |
const pageOfItems = getPageOfItems(processedItems(), newPager) | |
onChange(pageOfItems) | |
} | |
const isFirstPage = pager.currentPage === 1 | |
const isCurrentPage = page => pager.currentPage === page | |
return ( | |
<div {...rest}> | |
<ul> | |
<li className={isFirstPage ? 'disabled' : ''}> | |
{isFirstPage ? (<span>{fistLabel}</span>) : (<span tabIndex={0} onKeyPress={e => (e.key === 'Enter') && setPage(1)} onClick={() => setPage(1)}>{fistLabel}</span>)} | |
</li> | |
<li className={isFirstPage ? 'disabled' : ''}> | |
{isFirstPage ? (<span>{previousLabel}</span>) : (<span tabIndex={0} onKeyPress={e => (e.key === 'Enter') && setPage(pager.currentPage - 1)} onClick={() => setPage(pager.currentPage - 1)}>{previousLabel}</span>)} | |
</li> | |
{pager.pages.map((page, index) => | |
<li key={index} className={isCurrentPage(page) ? 'active' : ''}><span tabIndex={isCurrentPage(page) ? -1 : 0} onKeyPress={e => (e.key === 'Enter') && setPage(page)} onClick={() => setPage(page)}>{page}</span></li> | |
)} | |
<li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}> | |
{pager.currentPage === pager.totalPages ? (<span>{nextLabel}</span>) : (<span tabIndex={0} onKeyPress={e => (e.key === 'Enter') && setPage(pager.currentPage + 1)} onClick={() => setPage(pager.currentPage + 1)}>{nextLabel}</span>)} | |
</li> | |
<li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}> | |
{pager.currentPage === pager.totalPages ? (<span>{lastLabel}</span>) : (<span tabIndex={0} onKeyPress={e => (e.key === 'Enter') && setPage(pager.totalPages)} onClick={() => setPage(pager.totalPages)}>{lastLabel}</span>)} | |
</li> | |
</ul> | |
</div> | |
) | |
}, equalFn) | |
Pagination.defaultProps = { | |
initialPage: 1, | |
pageSize: 20, | |
items: [], | |
fistLabel: 'Primeira', | |
nextLabel: 'Próxima', | |
previousLabel: 'Anterior', | |
lastLabel: 'Última', | |
onChange: () => {} | |
} | |
export default Pagination |
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 styled from 'styled-components' | |
import Pagination from './Pagination' | |
const StyledPagination = styled(Pagination)` | |
display: flex; | |
justify-content: center; | |
& > ul { | |
display: inline-block; | |
padding-left: 0; | |
border-radius: 4px; | |
user-select: none; | |
} | |
& > ul > li { | |
display:inline; | |
} | |
& > ul > li > a { | |
cursor: pointer; | |
} | |
& > ul > li > a, & > ul > li > span { | |
cursor: pointer; | |
position: relative; | |
float: left; | |
padding: 6px 12px; | |
margin-left: -1px; | |
line-height: 1.42857143; | |
color: hsla(216, 40%, 55%, 1); | |
text-decoration: none; | |
background-color: white; | |
border: 1px solid rgb(231, 231, 231); | |
} | |
& > ul > li:first-child > a, & ul > li:first-child > span { | |
margin-left: 0; | |
border-top-left-radius: 4px; | |
border-bottom-left-radius: 4px; | |
} | |
& > ul > li:last-child > a, & > ul > li:last-child > span { | |
border-top-right-radius: 4px; | |
border-bottom-right-radius: 4px; | |
} | |
& > ul > li > a:focus, & > ul > li > a:hover, & > ul > li > span:focus, & > ul > li > span:hover { | |
z-index: 3; | |
color: rgb(66, 133, 244); | |
background-color: rgb(240, 240, 240); | |
border-color: rgb(231, 231, 231); | |
} | |
& > ul > .active > a, | |
& > ul > .active > a:focus, | |
& > ul > .active > a:hover, | |
& > ul > .active > span, | |
& > ul > .active > span:focus, | |
& > ul > .active > span:hover { | |
z-index: 2; | |
color: white; | |
cursor: default; | |
background-color: hsla(216, 40%, 55%, 1); | |
border-color: hsla(216, 40%, 55%, 1); | |
} | |
& > ul > .disabled > a, | |
& > ul > .disabled > a:focus, | |
& > ul > .disabled > a:hover, | |
& > ul > .disabled > span, | |
& > ul > .disabled > span:focus, | |
& > ul > .disabled > span:hover { | |
color: rgb(200, 200, 200); | |
cursor: not-allowed; | |
background-color: white; | |
border-color: rgb(231, 231, 231); | |
} | |
` | |
export default StyledPagination |
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 { useState } from 'react' | |
function usePagination(items) { | |
const [currentPageItems, setCurrentPageItems] = useState([]) | |
const paginationProps = { items: items, onChange: setCurrentPageItems } | |
return [paginationProps, currentPageItems] | |
} | |
export default usePagination |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment