brf/client/admin/components/pagination.tsx
2025-12-18 07:31:37 +01:00

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