feat: standardize table pagination styling

This commit is contained in:
Esdras Renan 2025-10-20 21:05:50 -03:00
parent 50f6796ffa
commit 2e7f575682
3 changed files with 215 additions and 129 deletions

View file

@ -0,0 +1,177 @@
import { useMemo } from "react"
import type { Table } from "@tanstack/react-table"
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { cn } from "@/lib/utils"
type TablePaginationProps<TData> = {
table: Table<TData>
pageSizeOptions?: number[]
rowsPerPageLabel?: string
showSelectedRows?: boolean
selectionLabel?: (selected: number, total: number) => React.ReactNode
className?: string
}
function buildPaginationRange(currentPage: number, pageCount: number) {
if (pageCount <= 7) {
return Array.from({ length: pageCount }, (_, index) => index + 1)
}
const range: Array<number | "ellipsis-left" | "ellipsis-right"> = [1]
const left = Math.max(2, currentPage - 1)
const right = Math.min(pageCount - 1, currentPage + 1)
if (left > 2) {
range.push("ellipsis-left")
}
for (let page = left; page <= right; page += 1) {
range.push(page)
}
if (right < pageCount - 1) {
range.push("ellipsis-right")
}
range.push(pageCount)
return range
}
export function TablePagination<TData>({
table,
pageSizeOptions = [10, 20, 30, 50],
rowsPerPageLabel = "Itens por página",
showSelectedRows = false,
selectionLabel,
className,
}: TablePaginationProps<TData>) {
const pageIndex = table.getState().pagination.pageIndex
const pageSize = table.getState().pagination.pageSize
const pageCount = Math.max(table.getPageCount(), 1)
const currentPage = Math.min(pageIndex + 1, pageCount)
const totalRows = table.getFilteredRowModel().rows.length
const currentRows = table.getRowModel().rows.length
const rangeStart = totalRows === 0 ? 0 : pageIndex * pageSize + 1
const rangeEnd = totalRows === 0 ? 0 : rangeStart + currentRows - 1
const selectedCount = table.getFilteredSelectedRowModel().rows.length
const paginationRange = useMemo(
() => buildPaginationRange(currentPage, pageCount),
[currentPage, pageCount]
)
const handlePageChange = (pageNumber: number) => {
table.setPageIndex(pageNumber - 1)
}
return (
<div
className={cn(
"flex flex-col gap-4 border-t border-slate-200 px-4 pt-4 text-sm text-neutral-600 md:flex-row md:items-center md:justify-between",
className
)}
>
{showSelectedRows ? (
<div className="text-xs text-neutral-500 md:text-sm">
{selectionLabel
? selectionLabel(selectedCount, totalRows)
: `${selectedCount} de ${totalRows} selecionados`}
</div>
) : (
<div className="sr-only">Paginação de tabela</div>
)}
<div className="flex flex-col-reverse items-center gap-3 md:flex-row md:gap-4 md:justify-end md:flex-1">
{pageSizeOptions.length > 0 ? (
<div className="flex items-center gap-2 text-xs uppercase tracking-wide text-neutral-500 md:text-sm">
<span>{rowsPerPageLabel}</span>
<Select
value={`${pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
table.setPageIndex(0)
}}
>
<SelectTrigger className="h-8 w-20">
<SelectValue placeholder={pageSize} />
</SelectTrigger>
<SelectContent align="end">
{pageSizeOptions.map((option) => (
<SelectItem key={option} value={`${option}`}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
) : null}
<div className="flex flex-col items-center gap-2 md:flex-row md:gap-3">
<span className="text-xs font-medium text-neutral-500 md:text-sm">
{totalRows === 0
? "Nenhum registro"
: `Mostrando ${rangeStart}-${rangeEnd} de ${totalRows}`}
</span>
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
disabled={!table.getCanPreviousPage()}
onClick={() => table.previousPage()}
/>
</PaginationItem>
{paginationRange.map((item, index) => {
if (typeof item === "number") {
return (
<PaginationItem key={`page-${item}`}>
<PaginationLink
href="#"
isActive={item === currentPage}
onClick={(event) => {
event.preventDefault()
handlePageChange(item)
}}
>
{item}
</PaginationLink>
</PaginationItem>
)
}
return (
<PaginationItem key={`ellipsis-${item}-${index}`}>
<PaginationEllipsis />
</PaginationItem>
)
})}
<PaginationItem>
<PaginationNext
disabled={!table.getCanNextPage()}
onClick={() => table.nextPage()}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
</div>
</div>
)
}