Add vendors listing with search, category filter, grid/list, pagination
This commit is contained in:
81
frontend/src/components/pagination.tsx
Normal file
81
frontend/src/components/pagination.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
type Props = {
|
||||
page: number
|
||||
itemsPerPage: number
|
||||
totalItems: number
|
||||
limit?: number
|
||||
buildSearch: (nextPage: number) => Record<string, unknown>
|
||||
}
|
||||
|
||||
export function Pagination({ page, itemsPerPage, totalItems, limit = 3, buildSearch }: Props) {
|
||||
const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage))
|
||||
if (totalPages === 1) return null
|
||||
|
||||
const pages: number[] = []
|
||||
for (let i = 0; i < totalPages; i++) {
|
||||
if (i < limit || i >= totalPages - limit || (i >= page - limit && i < page + limit - 1)) {
|
||||
pages.push(i + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const prev = Math.max(1, page - 1)
|
||||
const next = Math.min(totalPages, page + 1)
|
||||
|
||||
return (
|
||||
<div className="my-6 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<Link
|
||||
to="."
|
||||
search={buildSearch(prev)}
|
||||
className="inline-flex h-10 items-center justify-center rounded-md border px-3 text-sm hover:bg-accent hover:text-accent-foreground"
|
||||
>
|
||||
← Previous
|
||||
</Link>
|
||||
|
||||
<div className="hidden gap-1 md:flex">
|
||||
{pages.map((p, idx) => (
|
||||
<div key={p} className="flex items-center">
|
||||
{idx > 0 && p - 1 !== pages[idx - 1] && <span className="mx-2 leading-10">…</span>}
|
||||
<Link
|
||||
to="."
|
||||
search={buildSearch(p)}
|
||||
className={cn(
|
||||
'inline-flex h-10 min-w-10 items-center justify-center rounded-md border px-3 text-sm hover:bg-accent hover:text-accent-foreground',
|
||||
p === page && 'bg-accent font-semibold',
|
||||
)}
|
||||
>
|
||||
{p}
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<select
|
||||
className="h-10 rounded-md border bg-transparent px-3 text-sm md:hidden"
|
||||
value={page}
|
||||
onChange={(e) => {
|
||||
const target = Number(e.target.value)
|
||||
const hash = Object.entries(buildSearch(target))
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join('&')
|
||||
window.location.search = `?${hash}`
|
||||
}}
|
||||
>
|
||||
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
|
||||
<option key={p} value={p}>
|
||||
{p}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<Link
|
||||
to="."
|
||||
search={buildSearch(next)}
|
||||
className="inline-flex h-10 items-center justify-center rounded-md border px-3 text-sm hover:bg-accent hover:text-accent-foreground"
|
||||
>
|
||||
Next →
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user