feat(ui): support disabling pagination in useTable complete mode

Add `CompletePaginationOptions` type extending `PaginationOptions`
with a `type` field supporting `'page'` (default) and `'none'`.
When using `mode: 'complete'` with `type: 'none'`, `useTable` skips
data slicing and produces `pagination: { type: 'none' }` in
`tableProps` directly.

Also sync `pageSize` state when `paginationOptions.pageSize` changes
dynamically, fixing cases where the initial value became stale.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2026-03-27 10:29:02 +01:00
parent 40c8ec9615
commit 3bc23a5587
8 changed files with 127 additions and 41 deletions
+18 -2
View File
@@ -906,6 +906,12 @@ export type Columns =
| '12'
| 'auto';
// @public (undocumented)
export interface CompletePaginationOptions extends PaginationOptions {
// (undocumented)
type?: 'page' | 'none';
}
// @public (undocumented)
export const Container: ForwardRefExoticComponent<
ContainerProps & RefAttributes<HTMLDivElement>
@@ -3162,7 +3168,17 @@ export const useBreakpoint: () => {
// @public (undocumented)
export function useTable<T extends TableItem, TFilter = unknown>(
options: UseTableOptions<T, TFilter>,
options: UseTableCompleteOptions<T, TFilter>,
): UseTableResult<T, TFilter>;
// @public (undocumented)
export function useTable<T extends TableItem, TFilter = unknown>(
options: UseTableOffsetOptions<T, TFilter>,
): UseTableResult<T, TFilter>;
// @public (undocumented)
export function useTable<T extends TableItem, TFilter = unknown>(
options: UseTableCursorOptions<T, TFilter>,
): UseTableResult<T, TFilter>;
// @public (undocumented)
@@ -3171,7 +3187,7 @@ export type UseTableCompleteOptions<
TFilter = unknown,
> = QueryOptions<TFilter> & {
mode: 'complete';
paginationOptions?: PaginationOptions;
paginationOptions?: CompletePaginationOptions;
sortFn?: (data: T[], sort: SortDescriptor) => T[];
filterFn?: (data: T[], filter: TFilter) => T[];
searchFn?: (data: T[], search: string) => T[];
@@ -62,6 +62,11 @@ export interface PaginationOptions
initialOffset?: number;
}
/** @public */
export interface CompletePaginationOptions extends PaginationOptions {
type?: 'page' | 'none';
}
/** @public */
export interface OffsetParams<TFilter> {
offset: number;
@@ -102,7 +107,7 @@ export type UseTableCompleteOptions<
TFilter = unknown,
> = QueryOptions<TFilter> & {
mode: 'complete';
paginationOptions?: PaginationOptions;
paginationOptions?: CompletePaginationOptions;
sortFn?: (data: T[], sort: SortDescriptor) => T[];
filterFn?: (data: T[], filter: TFilter) => T[];
searchFn?: (data: T[], search: string) => T[];
@@ -38,8 +38,11 @@ export function useCompletePagination<T extends TableItem, TFilter>(
searchFn,
} = options;
const hasGetData = 'getData' in options;
const noPagination = paginationOptions.type === 'none';
const { initialOffset = 0 } = paginationOptions;
const defaultPageSize = getEffectivePageSize(paginationOptions);
const defaultPageSize = noPagination
? Infinity
: getEffectivePageSize(paginationOptions);
const getData = useStableCallback(getDataProp);
const { sort, filter, search } = query;
@@ -52,6 +55,12 @@ export function useCompletePagination<T extends TableItem, TFilter>(
const [offset, setOffset] = useState(initialOffset);
const [pageSize, setPageSize] = useState(defaultPageSize);
// Sync pageSize when the caller changes paginationOptions.pageSize
useEffect(() => {
setPageSize(defaultPageSize);
setOffset(0);
}, [defaultPageSize]);
// Load data on mount and when loadCount changes (reload trigger)
useEffect(() => {
if (data) {
@@ -121,12 +130,15 @@ export function useCompletePagination<T extends TableItem, TFilter>(
// Paginate the processed data
const paginatedData = useMemo(
() => processedData?.slice(offset, offset + pageSize),
[processedData, offset, pageSize],
() =>
noPagination
? processedData
: processedData?.slice(offset, offset + pageSize),
[processedData, offset, pageSize, noPagination],
);
const hasNextPage = offset + pageSize < totalCount;
const hasPreviousPage = offset > 0;
const hasNextPage = !noPagination && offset + pageSize < totalCount;
const hasPreviousPage = !noPagination && offset > 0;
const onNextPage = useCallback(() => {
if (offset + pageSize < totalCount) {
@@ -16,8 +16,10 @@
import { useMemo, useRef } from 'react';
import type { SortState, TableItem, TableProps } from '../types';
import type {
PaginationOptions,
PaginationResult,
UseTableCompleteOptions,
UseTableCursorOptions,
UseTableOffsetOptions,
UseTableOptions,
UseTableResult,
} from './types';
@@ -29,7 +31,7 @@ import { useOffsetPagination } from './useOffsetPagination';
function useTableProps<T extends TableItem>(
paginationResult: PaginationResult<T>,
sortState: SortState,
paginationOptions: PaginationOptions = {},
paginationOptions: UseTableCompleteOptions<T>['paginationOptions'] = {},
): Omit<
TableProps<T>,
'columnConfig' | 'rowConfig' | 'selection' | 'emptyState'
@@ -52,8 +54,11 @@ function useTableProps<T extends TableItem>(
const displayData = paginationResult.data ?? previousDataRef.current;
const isStale = paginationResult.loading && displayData !== undefined;
const pagination = useMemo(
() => ({
const pagination = useMemo(() => {
if (paginationOptions.type === 'none') {
return { type: 'none' as const };
}
return {
type: 'page' as const,
pageSize: paginationResult.pageSize,
pageSizeOptions,
@@ -76,25 +81,25 @@ function useTableProps<T extends TableItem>(
showPageSizeOptions,
getLabel,
showPaginationLabel,
}),
[
paginationResult.pageSize,
pageSizeOptions,
paginationResult.offset,
paginationResult.totalCount,
paginationResult.hasNextPage,
paginationResult.hasPreviousPage,
paginationResult.onNextPage,
paginationResult.onPreviousPage,
paginationResult.onPageSizeChange,
onNextPageCallback,
onPreviousPageCallback,
onPageSizeChangeCallback,
showPageSizeOptions,
getLabel,
showPaginationLabel,
],
);
};
}, [
paginationOptions.type,
paginationResult.pageSize,
pageSizeOptions,
paginationResult.offset,
paginationResult.totalCount,
paginationResult.hasNextPage,
paginationResult.hasPreviousPage,
paginationResult.onNextPage,
paginationResult.onPreviousPage,
paginationResult.onPageSizeChange,
onNextPageCallback,
onPreviousPageCallback,
onPageSizeChangeCallback,
showPageSizeOptions,
getLabel,
showPaginationLabel,
]);
return useMemo(
() => ({
@@ -117,6 +122,17 @@ function useTableProps<T extends TableItem>(
}
/** @public */
export function useTable<T extends TableItem, TFilter = unknown>(
options: UseTableCompleteOptions<T, TFilter>,
): UseTableResult<T, TFilter>;
/** @public */
export function useTable<T extends TableItem, TFilter = unknown>(
options: UseTableOffsetOptions<T, TFilter>,
): UseTableResult<T, TFilter>;
/** @public */
export function useTable<T extends TableItem, TFilter = unknown>(
options: UseTableCursorOptions<T, TFilter>,
): UseTableResult<T, TFilter>;
export function useTable<T extends TableItem, TFilter = unknown>(
options: UseTableOptions<T, TFilter>,
): UseTableResult<T, TFilter> {
@@ -69,6 +69,7 @@ export type {
SearchState,
QueryOptions,
PaginationOptions,
CompletePaginationOptions,
} from './hooks/types';
export { TableDefinition } from './definition';