@tanstack/table-core
Version:
Headless UI for building powerful tables & datagrids for TS/JS.
289 lines (287 loc) • 8.63 kB
JavaScript
import { cloneState, functionalUpdate } from "../../utils.js";
//#region src/features/row-pagination/rowPaginationFeature.utils.ts
const defaultPageIndex = 0;
const defaultPageSize = 10;
/**
* Creates the default pagination state used by the pagination feature.
*
* The feature default starts at the first page with a page size of 10. Reset
* APIs use this value when `defaultState` is `true`.
*
* @example
* ```ts
* const pagination = getDefaultPaginationState()
* ```
*/
function getDefaultPaginationState() {
return {
pageIndex: defaultPageIndex,
pageSize: defaultPageSize
};
}
/**
* Resets the page index when a page-altering change should return to page 0.
*
* The reset runs when `autoResetAll`, `autoResetPageIndex`, or the default
* client-side pagination behavior allows it. Manual pagination opts out unless
* the reset options explicitly opt back in.
*
* @example
* ```ts
* table_autoResetPageIndex(table)
* ```
*/
function table_autoResetPageIndex(table) {
if (table.options.autoResetAll ?? table.options.autoResetPageIndex ?? !table.options.manualPagination) table_resetPageIndex(table, true);
}
/**
* Routes a pagination updater through the table's pagination change handler.
*
* The updater may be a next state object or a function of the previous
* `PaginationState`; controlled state and external atoms observe the same
* updater path as the instance API.
*
* @example
* ```ts
* table_setPagination(table, (old) => old)
* ```
*/
function table_setPagination(table, updater) {
const safeUpdater = (old) => {
return functionalUpdate(updater, old);
};
return table.options.onPaginationChange?.(safeUpdater);
}
/**
* Resets `pagination` to the configured initial state or feature default.
*
* With no argument, the reset clones `table.initialState.pagination` when it
* exists. Passing `true` ignores initial state and resets to
* `{ pageIndex: 0, pageSize: 10 }`.
*
* @example
* ```ts
* table_resetPagination(table)
* table_resetPagination(table, true)
* ```
*/
function table_resetPagination(table, defaultState) {
table_setPagination(table, defaultState ? getDefaultPaginationState() : cloneState(table.initialState.pagination ?? getDefaultPaginationState()));
}
/**
* Updates `pagination.pageIndex` and clamps it to the known page range.
*
* Unknown page counts (`undefined` or `-1`) allow any non-negative page index.
* Known page counts clamp the index between `0` and `pageCount - 1`.
*
* @example
* ```ts
* table_setPageIndex(table, (old) => old)
* ```
*/
function table_setPageIndex(table, updater) {
table_setPagination(table, (old) => {
let pageIndex = functionalUpdate(updater, old.pageIndex);
const maxPageIndex = typeof table.options.pageCount === "undefined" || table.options.pageCount === -1 ? Number.MAX_SAFE_INTEGER : table.options.pageCount - 1;
pageIndex = Math.max(0, Math.min(pageIndex, maxPageIndex));
return {
...old,
pageIndex
};
});
}
/**
* Resets only `pagination.pageIndex`.
*
* With no argument, the reset uses `table.initialState.pagination?.pageIndex`
* or `0`. Passing `true` always resets the page index to `0`.
*
* @example
* ```ts
* table_resetPageIndex(table)
* table_resetPageIndex(table, true)
* ```
*/
function table_resetPageIndex(table, defaultState) {
const currentPageIndex = table.atoms.pagination?.get()?.pageIndex ?? defaultPageIndex;
const newPageIndex = defaultState ? defaultPageIndex : table.initialState.pagination?.pageIndex ?? defaultPageIndex;
if (newPageIndex === currentPageIndex) return;
table_setPageIndex(table, newPageIndex);
}
/**
* Resets only `pagination.pageSize`.
*
* With no argument, the reset uses `table.initialState.pagination?.pageSize`
* or `10`. Passing `true` always resets the page size to `10`.
*
* @example
* ```ts
* table_resetPageSize(table)
* table_resetPageSize(table, true)
* ```
*/
function table_resetPageSize(table, defaultState) {
const currentPageSize = table.atoms.pagination?.get()?.pageSize ?? defaultPageSize;
const newPageSize = defaultState ? defaultPageSize : table.initialState.pagination?.pageSize ?? defaultPageSize;
if (newPageSize === currentPageSize) return;
table_setPageSize(table, newPageSize);
}
/**
* Updates `pagination.pageSize` while preserving the current top row.
*
* The new size is clamped to at least `1`, and `pageIndex` is recalculated so
* the row that was previously at the top of the page remains in view.
*
* @example
* ```ts
* table_setPageSize(table, (old) => old)
* ```
*/
function table_setPageSize(table, updater) {
table_setPagination(table, (old) => {
const pageSize = Math.max(1, functionalUpdate(updater, old.pageSize));
const topRowIndex = old.pageSize * old.pageIndex;
const pageIndex = Math.floor(topRowIndex / pageSize);
return {
...old,
pageIndex,
pageSize
};
});
}
/**
* Builds the zero-based page indexes available for the current page count.
*
* Unknown or empty page counts return an empty array; otherwise the result is
* `[0, 1, ...pageCount - 1]`.
*
* @example
* ```ts
* const pageIndexes = table_getPageOptions(table)
* ```
*/
function table_getPageOptions(table) {
const pageCount = table_getPageCount(table);
let pageOptions = [];
if (pageCount && pageCount > 0) pageOptions = [...new Array(pageCount)].fill(null).map((_, i) => i);
return pageOptions;
}
/**
* Checks whether the current page index can move backward.
*
* The first page is page index `0`, so only positive page indexes can navigate
* to a previous page.
*
* @example
* ```ts
* const canGoBack = table_getCanPreviousPage(table)
* ```
*/
function table_getCanPreviousPage(table) {
return (table.atoms.pagination?.get()?.pageIndex ?? 0) > 0;
}
/**
* Checks whether the current page index can move forward.
*
* A `pageCount` of `-1` means the caller does not know the total page count, so
* this returns `true`. A page count of `0` returns `false`.
*
* @example
* ```ts
* const canGoForward = table_getCanNextPage(table)
* ```
*/
function table_getCanNextPage(table) {
const pageIndex = table.atoms.pagination?.get()?.pageIndex ?? defaultPageIndex;
const pageCount = table_getPageCount(table);
if (pageCount === -1) return true;
if (pageCount === 0) return false;
return pageIndex < pageCount - 1;
}
/**
* Moves the table to the previous page.
*
* This delegates to `table_setPageIndex` so pagination state ownership and
* updater semantics remain consistent.
*
* @example
* ```ts
* table_previousPage(table)
* ```
*/
function table_previousPage(table) {
return table_setPageIndex(table, (old) => old - 1);
}
/**
* Moves the table to the next page.
*
* This delegates to `table_setPageIndex` so pagination state ownership and
* updater semantics remain consistent.
*
* @example
* ```ts
* table_nextPage(table)
* ```
*/
function table_nextPage(table) {
return table_setPageIndex(table, (old) => {
return old + 1;
});
}
/**
* Moves the table to the first page.
*
* This is a convenience wrapper around `table_setPageIndex(table, 0)`.
*
* @example
* ```ts
* table_firstPage(table)
* ```
*/
function table_firstPage(table) {
return table_setPageIndex(table, 0);
}
/**
* Moves the table to the last known page.
*
* The target page is derived from `table_getPageCount(table) - 1`.
*
* @example
* ```ts
* table_lastPage(table)
* ```
*/
function table_lastPage(table) {
return table_setPageIndex(table, table_getPageCount(table) - 1);
}
/**
* Resolves the number of pages for the current pagination state.
*
* `options.pageCount` wins for manual pagination. Otherwise the value is
* calculated from `table_getRowCount(table)` and the current `pageSize`.
*
* @example
* ```ts
* const pages = table_getPageCount(table)
* ```
*/
function table_getPageCount(table) {
return table.options.pageCount ?? Math.ceil(table_getRowCount(table) / (table.atoms.pagination?.get()?.pageSize ?? defaultPageSize));
}
/**
* Resolves the total row count used for pagination math.
*
* `options.rowCount` wins for manual pagination. Otherwise the count comes
* from the pre-paginated row model so filtering, grouping, sorting, and
* expansion are reflected before the page slice is applied.
*
* @example
* ```ts
* const rows = table_getRowCount(table)
* ```
*/
function table_getRowCount(table) {
return table.options.rowCount ?? table.getPrePaginatedRowModel().rows.length;
}
//#endregion
export { getDefaultPaginationState, table_autoResetPageIndex, table_firstPage, table_getCanNextPage, table_getCanPreviousPage, table_getPageCount, table_getPageOptions, table_getRowCount, table_lastPage, table_nextPage, table_previousPage, table_resetPageIndex, table_resetPageSize, table_resetPagination, table_setPageIndex, table_setPageSize, table_setPagination };