@tanstack/table-core
Version:
Headless UI for building powerful tables & datagrids for TS/JS.
213 lines (211 loc) • 6.38 kB
JavaScript
import { callMemoOrStaticFn, cloneState } from "../../utils.js";
import { row_getIsAllParentsExpanded } from "../row-expanding/rowExpandingFeature.utils.js";
//#region src/features/row-pinning/rowPinningFeature.utils.ts
/**
* Creates the default row pinning state.
*
* Both pinning regions start empty. Reset APIs use this value when
* `defaultState` is `true`.
*
* @example
* ```ts
* const pinning = getDefaultRowPinningState()
* ```
*/
function getDefaultRowPinningState() {
return {
top: [],
bottom: []
};
}
/**
* Routes a row pinning updater through the table's row-pinning change handler.
*
* The updater may be a next `{ top, bottom }` state or a function of the
* previous state, matching the instance `table.setRowPinning` behavior.
*
* @example
* ```ts
* table_setRowPinning(table, (old) => ({ ...old, top: [rowId] }))
* ```
*/
function table_setRowPinning(table, updater) {
table.options.onRowPinningChange?.(updater);
}
/**
* Resets `rowPinning` to the configured initial state or feature default.
*
* With no argument, the reset clones `table.initialState.rowPinning` when it
* exists. Passing `true` ignores initial state and resets to empty top/bottom
* arrays.
*
* @example
* ```ts
* table_resetRowPinning(table)
* table_resetRowPinning(table, true)
* ```
*/
function table_resetRowPinning(table, defaultState) {
table_setRowPinning(table, defaultState ? getDefaultRowPinningState() : cloneState(table.initialState.rowPinning ?? getDefaultRowPinningState()));
}
/**
* Checks whether any rows are pinned.
*
* Omit `position` to check both regions, or pass `'top'`/`'bottom'` to inspect
* one region.
*
* @example
* ```ts
* const hasPinnedRows = table_getIsSomeRowsPinned(table)
* ```
*/
function table_getIsSomeRowsPinned(table, position) {
const rowPinning = table.atoms.rowPinning?.get();
if (!position) return Boolean(rowPinning?.top.length || rowPinning?.bottom.length);
return Boolean(rowPinning?.[position].length);
}
function table_getPinnedRows(table, position) {
const visibleRows = table.getRowModel().rows;
const pinnedRowIds = table.atoms.rowPinning?.get()?.[position] ?? [];
const keepPinnedRows = table.options.keepPinnedRows ?? true;
const result = [];
for (let i = 0; i < pinnedRowIds.length; i++) {
const rowId = pinnedRowIds[i];
let row;
if (keepPinnedRows) {
const fullRow = table.getPrePaginatedRowModel().rowsById[rowId] ?? table.getCoreRowModel().rowsById[rowId];
if (fullRow && row_getIsAllParentsExpanded(fullRow)) row = fullRow;
} else row = visibleRows.find((r) => r.id === rowId);
if (!row) continue;
row.position = position;
result.push(row);
}
return result;
}
/**
* Resolves the visible rows pinned to the top region.
*
* The result follows `state.rowPinning.top` order and marks each row with
* `position = 'top'`.
*
* @example
* ```ts
* const rows = table_getTopRows(table)
* ```
*/
function table_getTopRows(table) {
return table_getPinnedRows(table, "top");
}
/**
* Resolves the visible rows pinned to the bottom region.
*
* The result follows `state.rowPinning.bottom` order and marks each row with
* `position = 'bottom'`.
*
* @example
* ```ts
* const rows = table_getBottomRows(table)
* ```
*/
function table_getBottomRows(table) {
return table_getPinnedRows(table, "bottom");
}
/**
* Resolves rows that are not pinned to top or bottom.
*
* The current row model is filtered by `state.rowPinning.top` and
* `state.rowPinning.bottom`.
*
* @example
* ```ts
* const rows = table_getCenterRows(table)
* ```
*/
function table_getCenterRows(table) {
const { top, bottom } = table.atoms.rowPinning?.get() ?? getDefaultRowPinningState();
const allRows = table.getRowModel().rows;
const topAndBottom = /* @__PURE__ */ new Set([...top, ...bottom]);
return allRows.filter((d) => !topAndBottom.has(d.id));
}
/**
* Checks whether this row can be pinned.
*
* `options.enableRowPinning` may be a boolean or a row predicate; it defaults
* to `true`.
*
* @example
* ```ts
* const canPin = row_getCanPin(row)
* ```
*/
function row_getCanPin(row) {
const { enableRowPinning } = row.table.options;
if (typeof enableRowPinning === "function") return enableRowPinning(row);
return enableRowPinning ?? true;
}
/**
* Reads this row's current pinning region.
*
* Rows listed in `state.rowPinning.top` return `'top'`, rows listed in
* `bottom` return `'bottom'`, and unpinned rows return `false`.
*
* @example
* ```ts
* const position = row_getIsPinned(row)
* ```
*/
function row_getIsPinned(row) {
const { top, bottom } = row.table.atoms.rowPinning?.get() ?? getDefaultRowPinningState();
return top.includes(row.id) ? "top" : bottom.includes(row.id) ? "bottom" : false;
}
/**
* Finds this row's visible index within its pinned region.
*
* Unpinned rows return `-1`.
*
* @example
* ```ts
* const index = row_getPinnedIndex(row)
* ```
*/
function row_getPinnedIndex(row) {
const position = row_getIsPinned(row);
if (!position) return -1;
return (position === "top" ? callMemoOrStaticFn(row.table, "getTopRows", table_getTopRows) : callMemoOrStaticFn(row.table, "getBottomRows", table_getBottomRows)).map(({ id }) => id).indexOf(row.id);
}
/**
* Pins or unpins a row.
*
* Optional flags let callers include parent rows or leaf rows when updating
* the row pinning state.
*
* @example
* ```ts
* row_pin(row, 'top')
* ```
*/
function row_pin(row, position, includeLeafRows, includeParentRows) {
const leafRowIds = includeLeafRows ? row.getLeafRows().map(({ id }) => id) : [];
const parentRowIds = includeParentRows ? row.getParentRows().map(({ id }) => id) : [];
const rowIds = /* @__PURE__ */ new Set([
...parentRowIds,
row.id,
...leafRowIds
]);
table_setRowPinning(row.table, (old) => {
if (position === "bottom") return {
top: old.top.filter((d) => !rowIds.has(d)),
bottom: [...old.bottom.filter((d) => !rowIds.has(d)), ...Array.from(rowIds)]
};
if (position === "top") return {
top: [...old.top.filter((d) => !rowIds.has(d)), ...Array.from(rowIds)],
bottom: old.bottom.filter((d) => !rowIds.has(d))
};
return {
top: old.top.filter((d) => !rowIds.has(d)),
bottom: old.bottom.filter((d) => !rowIds.has(d))
};
});
}
//#endregion
export { getDefaultRowPinningState, row_getCanPin, row_getIsPinned, row_getPinnedIndex, row_pin, table_getBottomRows, table_getCenterRows, table_getIsSomeRowsPinned, table_getTopRows, table_resetRowPinning, table_setRowPinning };