104 lines
2.8 KiB
TypeScript
104 lines
2.8 KiB
TypeScript
import { h, Fragment, type FunctionComponent } from 'preact'
|
|
import type { ForwardedRef } from 'preact/compat'
|
|
import cn from 'classnames'
|
|
|
|
import s from './pagination.module.scss'
|
|
|
|
function getPages({
|
|
pathname,
|
|
search,
|
|
totalCount,
|
|
limit,
|
|
offset,
|
|
}: {
|
|
pathname: string
|
|
search: string
|
|
totalCount: number
|
|
limit: number
|
|
offset: number
|
|
}) {
|
|
const pages = []
|
|
const searchParams = new URLSearchParams(search)
|
|
const last = Math.floor(totalCount / limit)
|
|
const current = offset / limit
|
|
|
|
for (let i = 0; i <= last; i++) {
|
|
if (i === 0) searchParams.delete('offset')
|
|
else searchParams.set('offset', (limit * i) as unknown as string)
|
|
|
|
const query = searchParams.toString()
|
|
|
|
// TODO should all pages have a rel attribute?
|
|
pages.push({
|
|
path: pathname + (query ? '?' + query : ''),
|
|
index: i,
|
|
current: i === current,
|
|
})
|
|
}
|
|
|
|
return {
|
|
current,
|
|
firstItemCount: totalCount ? offset + 1 : 0,
|
|
last,
|
|
lastItemCount: Math.min(offset + limit, totalCount),
|
|
next: pages[current + 1],
|
|
pages,
|
|
prev: pages[current - 1],
|
|
totalCount,
|
|
}
|
|
}
|
|
|
|
type PaginationProps = {
|
|
className?: string
|
|
forwardRef: ForwardedRef<HTMLDivElement>
|
|
pathname: string
|
|
search: string
|
|
totalCount: number
|
|
limit: number
|
|
offset: number
|
|
}
|
|
|
|
const Pagination: FunctionComponent<PaginationProps> = (props) => {
|
|
const { current, last, firstItemCount, lastItemCount, totalCount, pages, prev, next } = getPages(props)
|
|
|
|
const start = current + 3 > last ? Math.max(last - 6, 0) : Math.max(current - 3, 0)
|
|
const end = start + 7
|
|
|
|
return (
|
|
<div className={cn(s.pagination, props.className)} ref={props.forwardRef}>
|
|
<div className={s.results}>
|
|
Showing <span className={cn(s.first, s.number)}>{firstItemCount}</span> to{' '}
|
|
<span className={cn(s.last, s.number)}>{lastItemCount || 0}</span> of{' '}
|
|
<span className={cn(s.total, s.number)}>{totalCount || 0}</span> entries
|
|
</div>
|
|
<ul className={s.pages}>
|
|
{totalCount > 0 && (
|
|
<Fragment>
|
|
<li className={s.prev}>
|
|
<a href={prev?.path || 'javascript:;'} rel={prev && 'prev'} className={s.nav} title='Previous Page'>
|
|
Previous
|
|
</a>
|
|
</li>
|
|
|
|
{pages.slice(start, end).map((page) => (
|
|
<li key={page.index} className={cn(s.page, page.current && s.currentPage)}>
|
|
<a href={page.path} className={s.nav} title={`Sida ${page.index + 1}`}>
|
|
{page.index + 1}
|
|
</a>
|
|
</li>
|
|
))}
|
|
|
|
<li className={s.next}>
|
|
<a href={next?.path || 'javascript:;'} rel={next && 'next'} className={s.nav} title='Next Page'>
|
|
Next
|
|
</a>
|
|
</li>
|
|
</Fragment>
|
|
)}
|
|
</ul>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Pagination
|