mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-08 03:36:37 +00:00
fix client side pagination issue
This commit is contained in:
@@ -24,6 +24,8 @@ interface DataTablePaginationProps<TData> {
|
|||||||
isServerPagination?: boolean;
|
isServerPagination?: boolean;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
pageSize?: number;
|
||||||
|
pageIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DataTablePagination<TData>({
|
export function DataTablePagination<TData>({
|
||||||
@@ -33,10 +35,24 @@ export function DataTablePagination<TData>({
|
|||||||
totalCount,
|
totalCount,
|
||||||
isServerPagination = false,
|
isServerPagination = false,
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
disabled = false
|
disabled = false,
|
||||||
|
pageSize: controlledPageSize,
|
||||||
|
pageIndex: controlledPageIndex
|
||||||
}: DataTablePaginationProps<TData>) {
|
}: DataTablePaginationProps<TData>) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
|
// Use controlled values if provided, otherwise fall back to table state
|
||||||
|
const pageSize = controlledPageSize ?? table.getState().pagination.pageSize;
|
||||||
|
const pageIndex = controlledPageIndex ?? table.getState().pagination.pageIndex;
|
||||||
|
|
||||||
|
// Calculate page boundaries based on controlled state
|
||||||
|
// For server-side pagination, use totalCount if available for accurate page count
|
||||||
|
const pageCount = isServerPagination && totalCount !== undefined
|
||||||
|
? Math.ceil(totalCount / pageSize)
|
||||||
|
: table.getPageCount();
|
||||||
|
const canNextPage = pageIndex < pageCount - 1;
|
||||||
|
const canPreviousPage = pageIndex > 0;
|
||||||
|
|
||||||
const handlePageSizeChange = (value: string) => {
|
const handlePageSizeChange = (value: string) => {
|
||||||
const newPageSize = Number(value);
|
const newPageSize = Number(value);
|
||||||
table.setPageSize(newPageSize);
|
table.setPageSize(newPageSize);
|
||||||
@@ -51,7 +67,7 @@ export function DataTablePagination<TData>({
|
|||||||
action: "first" | "previous" | "next" | "last"
|
action: "first" | "previous" | "next" | "last"
|
||||||
) => {
|
) => {
|
||||||
if (isServerPagination && onPageChange) {
|
if (isServerPagination && onPageChange) {
|
||||||
const currentPage = table.getState().pagination.pageIndex;
|
const currentPage = pageIndex;
|
||||||
const pageCount = table.getPageCount();
|
const pageCount = table.getPageCount();
|
||||||
|
|
||||||
let newPage: number;
|
let newPage: number;
|
||||||
@@ -77,18 +93,24 @@ export function DataTablePagination<TData>({
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Use table's built-in navigation for client-side pagination
|
// Use table's built-in navigation for client-side pagination
|
||||||
|
// But add bounds checking to prevent going beyond page boundaries
|
||||||
|
const pageCount = table.getPageCount();
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "first":
|
case "first":
|
||||||
table.setPageIndex(0);
|
table.setPageIndex(0);
|
||||||
break;
|
break;
|
||||||
case "previous":
|
case "previous":
|
||||||
table.previousPage();
|
if (pageIndex > 0) {
|
||||||
|
table.previousPage();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "next":
|
case "next":
|
||||||
table.nextPage();
|
if (pageIndex < pageCount - 1) {
|
||||||
|
table.nextPage();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "last":
|
case "last":
|
||||||
table.setPageIndex(table.getPageCount() - 1);
|
table.setPageIndex(Math.max(0, pageCount - 1));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,13 +120,13 @@ export function DataTablePagination<TData>({
|
|||||||
<div className="flex items-center justify-between text-muted-foreground">
|
<div className="flex items-center justify-between text-muted-foreground">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Select
|
<Select
|
||||||
value={`${table.getState().pagination.pageSize}`}
|
value={`${pageSize}`}
|
||||||
onValueChange={handlePageSizeChange}
|
onValueChange={handlePageSizeChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="h-8 w-[73px]" disabled={disabled}>
|
<SelectTrigger className="h-8 w-[73px]" disabled={disabled}>
|
||||||
<SelectValue
|
<SelectValue
|
||||||
placeholder={table.getState().pagination.pageSize}
|
placeholder={pageSize}
|
||||||
/>
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent side="bottom">
|
<SelectContent side="bottom">
|
||||||
@@ -121,16 +143,11 @@ export function DataTablePagination<TData>({
|
|||||||
<div className="flex items-center justify-center text-sm font-medium">
|
<div className="flex items-center justify-center text-sm font-medium">
|
||||||
{isServerPagination && totalCount !== undefined
|
{isServerPagination && totalCount !== undefined
|
||||||
? t("paginator", {
|
? t("paginator", {
|
||||||
current:
|
current: pageIndex + 1,
|
||||||
table.getState().pagination.pageIndex + 1,
|
last: Math.ceil(totalCount / pageSize)
|
||||||
last: Math.ceil(
|
|
||||||
totalCount /
|
|
||||||
table.getState().pagination.pageSize
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
: t("paginator", {
|
: t("paginator", {
|
||||||
current:
|
current: pageIndex + 1,
|
||||||
table.getState().pagination.pageIndex + 1,
|
|
||||||
last: table.getPageCount()
|
last: table.getPageCount()
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -140,7 +157,7 @@ export function DataTablePagination<TData>({
|
|||||||
className="hidden h-8 w-8 p-0 lg:flex"
|
className="hidden h-8 w-8 p-0 lg:flex"
|
||||||
onClick={() => handlePageNavigation("first")}
|
onClick={() => handlePageNavigation("first")}
|
||||||
disabled={
|
disabled={
|
||||||
!table.getCanPreviousPage() || isLoading || disabled
|
!canPreviousPage || isLoading || disabled
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="sr-only">{t("paginatorToFirst")}</span>
|
<span className="sr-only">{t("paginatorToFirst")}</span>
|
||||||
@@ -151,7 +168,7 @@ export function DataTablePagination<TData>({
|
|||||||
className="h-8 w-8 p-0"
|
className="h-8 w-8 p-0"
|
||||||
onClick={() => handlePageNavigation("previous")}
|
onClick={() => handlePageNavigation("previous")}
|
||||||
disabled={
|
disabled={
|
||||||
!table.getCanPreviousPage() || isLoading || disabled
|
!canPreviousPage || isLoading || disabled
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="sr-only">
|
<span className="sr-only">
|
||||||
@@ -164,7 +181,7 @@ export function DataTablePagination<TData>({
|
|||||||
className="h-8 w-8 p-0"
|
className="h-8 w-8 p-0"
|
||||||
onClick={() => handlePageNavigation("next")}
|
onClick={() => handlePageNavigation("next")}
|
||||||
disabled={
|
disabled={
|
||||||
!table.getCanNextPage() || isLoading || disabled
|
!canNextPage || isLoading || disabled
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="sr-only">{t("paginatorToNext")}</span>
|
<span className="sr-only">{t("paginatorToNext")}</span>
|
||||||
@@ -175,7 +192,7 @@ export function DataTablePagination<TData>({
|
|||||||
className="hidden h-8 w-8 p-0 lg:flex"
|
className="hidden h-8 w-8 p-0 lg:flex"
|
||||||
onClick={() => handlePageNavigation("last")}
|
onClick={() => handlePageNavigation("last")}
|
||||||
disabled={
|
disabled={
|
||||||
!table.getCanNextPage() || isLoading || disabled
|
!canNextPage || isLoading || disabled
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="sr-only">{t("paginatorToLast")}</span>
|
<span className="sr-only">{t("paginatorToLast")}</span>
|
||||||
|
|||||||
@@ -542,6 +542,8 @@ export function LogDataTable<TData, TValue>({
|
|||||||
isServerPagination={isServerPagination}
|
isServerPagination={isServerPagination}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
pageSize={pageSize}
|
||||||
|
pageIndex={currentPage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
getFilteredRowModel,
|
getFilteredRowModel,
|
||||||
VisibilityState
|
VisibilityState,
|
||||||
|
PaginationState
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
|
|
||||||
// Extended ColumnDef type that includes optional friendlyName for column visibility dropdown
|
// Extended ColumnDef type that includes optional friendlyName for column visibility dropdown
|
||||||
@@ -227,6 +228,10 @@ export function DataTable<TData, TValue>({
|
|||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
|
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
|
||||||
initialColumnVisibility
|
initialColumnVisibility
|
||||||
);
|
);
|
||||||
|
const [pagination, setPagination] = useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: pageSize
|
||||||
|
});
|
||||||
const [activeTab, setActiveTab] = useState<string>(
|
const [activeTab, setActiveTab] = useState<string>(
|
||||||
defaultTab || tabs?.[0]?.id || ""
|
defaultTab || tabs?.[0]?.id || ""
|
||||||
);
|
);
|
||||||
@@ -256,6 +261,7 @@ export function DataTable<TData, TValue>({
|
|||||||
getFilteredRowModel: getFilteredRowModel(),
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
onGlobalFilterChange: setGlobalFilter,
|
onGlobalFilterChange: setGlobalFilter,
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
initialState: {
|
initialState: {
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize: pageSize,
|
pageSize: pageSize,
|
||||||
@@ -267,21 +273,18 @@ export function DataTable<TData, TValue>({
|
|||||||
sorting,
|
sorting,
|
||||||
columnFilters,
|
columnFilters,
|
||||||
globalFilter,
|
globalFilter,
|
||||||
columnVisibility
|
columnVisibility,
|
||||||
|
pagination
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Persist pageSize to localStorage when it changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentPageSize = table.getState().pagination.pageSize;
|
if (persistPageSize && pagination.pageSize !== pageSize) {
|
||||||
if (currentPageSize !== pageSize) {
|
setStoredPageSize(pagination.pageSize, tableId);
|
||||||
table.setPageSize(pageSize);
|
setPageSize(pagination.pageSize);
|
||||||
|
|
||||||
// Persist to localStorage if enabled
|
|
||||||
if (persistPageSize) {
|
|
||||||
setStoredPageSize(pageSize, tableId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [pageSize, table, persistPageSize, tableId]);
|
}, [pagination.pageSize, persistPageSize, tableId, pageSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Persist column visibility to localStorage when it changes
|
// Persist column visibility to localStorage when it changes
|
||||||
@@ -293,13 +296,13 @@ export function DataTable<TData, TValue>({
|
|||||||
const handleTabChange = (value: string) => {
|
const handleTabChange = (value: string) => {
|
||||||
setActiveTab(value);
|
setActiveTab(value);
|
||||||
// Reset to first page when changing tabs
|
// Reset to first page when changing tabs
|
||||||
table.setPageIndex(0);
|
setPagination(prev => ({ ...prev, pageIndex: 0 }));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enhanced pagination component that updates our local state
|
// Enhanced pagination component that updates our local state
|
||||||
const handlePageSizeChange = (newPageSize: number) => {
|
const handlePageSizeChange = (newPageSize: number) => {
|
||||||
|
setPagination(prev => ({ ...prev, pageSize: newPageSize, pageIndex: 0 }));
|
||||||
setPageSize(newPageSize);
|
setPageSize(newPageSize);
|
||||||
table.setPageSize(newPageSize);
|
|
||||||
|
|
||||||
// Persist immediately when changed
|
// Persist immediately when changed
|
||||||
if (persistPageSize) {
|
if (persistPageSize) {
|
||||||
@@ -614,6 +617,8 @@ export function DataTable<TData, TValue>({
|
|||||||
<DataTablePagination
|
<DataTablePagination
|
||||||
table={table}
|
table={table}
|
||||||
onPageSizeChange={handlePageSizeChange}
|
onPageSizeChange={handlePageSizeChange}
|
||||||
|
pageSize={pagination.pageSize}
|
||||||
|
pageIndex={pagination.pageIndex}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user