Uecko_ERP/packages/rdx-ui/src/components/datatable/data-table-pagination.tsx

162 lines
5.4 KiB
TypeScript
Raw Normal View History

2025-10-18 19:57:52 +00:00
import { Table } from "@tanstack/react-table";
2025-10-16 11:18:55 +00:00
import {
2025-10-18 19:57:52 +00:00
ChevronLeftIcon,
ChevronRightIcon,
ChevronsLeftIcon,
ChevronsRightIcon
} from "lucide-react";
2025-10-16 11:18:55 +00:00
import {
2025-10-18 19:57:52 +00:00
Pagination, PaginationContent,
PaginationItem, PaginationLink,
Select,
2025-10-16 11:18:55 +00:00
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
2025-10-18 19:57:52 +00:00
} from '@repo/shadcn-ui/components';
import { cn } from '@repo/shadcn-ui/lib/utils';
import { useTranslation } from '../../locales/i18n.ts';
import { DataTableMeta } from './data-table.tsx';
2025-10-16 11:18:55 +00:00
interface DataTablePaginationProps<TData> {
2025-10-18 19:57:52 +00:00
table: Table<TData>;
className?: string;
2025-10-16 11:18:55 +00:00
}
2025-10-18 19:57:52 +00:00
export function DataTablePagination<TData>({ table, className }: DataTablePaginationProps<TData>) {
const { t } = useTranslation();
2025-10-18 20:33:01 +00:00
const { pageIndex: rawIndex, pageSize: rawSize } = table.getState().pagination;
2025-10-18 19:57:52 +00:00
const totalRows = (table.options.meta as DataTableMeta<TData>)?.totalItems ?? table.getFilteredRowModel().rows.length;
2025-10-18 20:33:01 +00:00
// Normalización segura
const pageIndex = Number.isFinite(rawIndex) && rawIndex >= 0 ? rawIndex : 0;
const pageSize = Number.isFinite(rawSize) && rawSize > 0 ? rawSize : 10;
const pageCount = table.getPageCount() || Math.max(1, Math.ceil((totalRows || 0) / pageSize));
2025-10-18 19:57:52 +00:00
const hasSelected = table.getFilteredSelectedRowModel().rows.length > 0;
2025-10-18 20:33:01 +00:00
// Rango visible (1-based en UI)
const start = totalRows > 0 ? pageIndex * pageSize + 1 : 0;
const end = totalRows > 0 ? Math.min(start + pageSize - 1, totalRows) : 0;
2025-10-18 19:57:52 +00:00
2025-10-16 11:18:55 +00:00
return (
2025-10-18 19:57:52 +00:00
<div className={cn(
"flex items-center justify-between",
className
)}>
{/* Información izquierda */}
<div className="flex flex-col sm:flex-row items-center gap-4 flex-1 text-sm text-muted-foreground">
{/* Rango visible */}
<span aria-live="polite">
{t("components.datatable.pagination.showing_range", {
start,
end,
total: totalRows,
})}
</span>
{/* Selección de filas */}
{hasSelected && (
<span aria-live="polite">
{t("components.datatable.pagination.rows_selected", {
count: table.getFilteredSelectedRowModel().rows.length,
total: table.getFilteredRowModel().rows.length,
})}
</span>
)}
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground text-nowrap">
{t("components.datatable.pagination.rows_per_page")}
</span>
2025-10-16 11:18:55 +00:00
<Select
2025-10-18 19:57:52 +00:00
value={String(pageSize)}
onValueChange={(value) => table.setPageSize(Number(value))}
2025-10-16 11:18:55 +00:00
>
2025-10-18 19:57:52 +00:00
<SelectTrigger className="w-20 h-8 bg-white border-gray-200">
<SelectValue placeholder={String(pageSize)} />
2025-10-16 11:18:55 +00:00
</SelectTrigger>
2025-10-18 19:57:52 +00:00
<SelectContent>
{[10, 20, 25, 30, 40, 50].map((size) => (
<SelectItem key={size} value={String(size)}>
{size}
2025-10-16 11:18:55 +00:00
</SelectItem>
))}
</SelectContent>
</Select>
</div>
2025-10-18 19:57:52 +00:00
</div>
{/* Controles derecha */}
<div className="flex items-center gap-2">
<Pagination>
<PaginationContent>
{/* Primera página */}
<PaginationItem>
<PaginationLink
aria-label={t("components.datatable.pagination.goto_first_page")}
onClick={() => table.setPageIndex(0)}
isActive={!table.getCanPreviousPage()}
size="sm"
className="px-2.5"
>
<ChevronsLeftIcon className="size-4" />
</PaginationLink>
</PaginationItem>
{/* Anterior */}
<PaginationItem>
<PaginationLink
aria-label={t("components.datatable.pagination.goto_previous_page")}
onClick={() => table.previousPage()}
isActive={!table.getCanPreviousPage()}
size="sm"
className="px-2.5"
>
<ChevronLeftIcon className="size-4" />
</PaginationLink>
</PaginationItem>
<span
className="text-sm text-muted-foreground px-2"
aria-live="polite"
>
{t("components.datatable.pagination.page_of", {
page: pageIndex + 1,
of: pageCount || 1,
})}
</span>
{/* Siguiente */}
<PaginationItem>
<PaginationLink
aria-label={t("components.datatable.pagination.goto_next_page")}
onClick={() => table.nextPage()}
isActive={!table.getCanNextPage()}
size="sm"
className="px-2.5"
>
<ChevronRightIcon className="size-4" />
</PaginationLink>
</PaginationItem>
{/* Última página */}
<PaginationItem>
<PaginationLink
aria-label={t("components.datatable.pagination.goto_last_page")}
onClick={() => table.setPageIndex(pageCount - 1)}
isActive={!table.getCanNextPage()}
size="sm"
className="px-2.5"
>
<ChevronsRightIcon className="size-4" />
</PaginationLink>
</PaginationItem>
</PaginationContent>
</Pagination>
2025-10-16 11:18:55 +00:00
</div>
</div>
2025-10-18 19:57:52 +00:00
);
}