diff --git a/packages/table-core/src/core/row.ts b/packages/table-core/src/core/row.ts index 0af28aa94c..14e966712a 100644 --- a/packages/table-core/src/core/row.ts +++ b/packages/table-core/src/core/row.ts @@ -6,6 +6,7 @@ export interface CoreRow { _getAllCellsByColumnId: () => Record> _uniqueValuesCache: Record _valuesCache: Record + clone: () => Row /** * The depth of the row (if nested or grouped) relative to the root row array. * @link [API Docs](https://tanstack.com/table/v8/docs/api/core/row#depth) @@ -92,26 +93,29 @@ export interface CoreRow { subRows: Row[] } -export const createRow = ( - table: Table, - id: string, - original: TData, - rowIndex: number, - depth: number, - subRows?: Row[], - parentId?: string -): Row => { - let row: CoreRow = { - id, - index: rowIndex, - original, - depth, - parentId, - _valuesCache: {}, - _uniqueValuesCache: {}, - getValue: columnId => { - if (row._valuesCache.hasOwnProperty(columnId)) { - return row._valuesCache[columnId] +const rowProtosByTable = new WeakMap, any>() + +/** + * Creates a table-specific row prototype object to hold shared row methods, including from all the + * features that have been registered on the table. + */ +export function getRowProto(table: Table) { + let rowProto = rowProtosByTable.get(table) + + if (!rowProto) { + const proto = {} as CoreRow + + proto.clone = function () { + return Object.assign(Object.create(Object.getPrototypeOf(this)), this) + } + + // Make the default fallback value available on the proto itself to avoid duplicating it on every row instance + // even if it's not used. This is safe as long as we don't mutate the value directly. + proto.subRows = [] as const + + proto.getValue = function (columnId: string) { + if (this._valuesCache.hasOwnProperty(columnId)) { + return this._valuesCache[columnId] } const column = table.getColumn(columnId) @@ -120,16 +124,22 @@ export const createRow = ( return undefined } - row._valuesCache[columnId] = column.accessorFn( - row.original as TData, - rowIndex + this._valuesCache[columnId] = column.accessorFn( + this.original as TData, + this.index ) - return row._valuesCache[columnId] as any - }, - getUniqueValues: columnId => { - if (row._uniqueValuesCache.hasOwnProperty(columnId)) { - return row._uniqueValuesCache[columnId] + return this._valuesCache[columnId] as any + } + + proto.getUniqueValues = function (columnId: string) { + if (!this.hasOwnProperty('_uniqueValuesCache')) { + // lazy-init cache on the instance + this._uniqueValuesCache = {} + } + + if (this._uniqueValuesCache.hasOwnProperty(columnId)) { + return this._uniqueValuesCache[columnId] } const column = table.getColumn(columnId) @@ -139,26 +149,33 @@ export const createRow = ( } if (!column.columnDef.getUniqueValues) { - row._uniqueValuesCache[columnId] = [row.getValue(columnId)] - return row._uniqueValuesCache[columnId] + this._uniqueValuesCache[columnId] = [this.getValue(columnId)] + return this._uniqueValuesCache[columnId] } - row._uniqueValuesCache[columnId] = column.columnDef.getUniqueValues( - row.original as TData, - rowIndex + this._uniqueValuesCache[columnId] = column.columnDef.getUniqueValues( + this.original as TData, + this.index ) - return row._uniqueValuesCache[columnId] as any - }, - renderValue: columnId => - row.getValue(columnId) ?? table.options.renderFallbackValue, - subRows: subRows ?? [], - getLeafRows: () => flattenBy(row.subRows, d => d.subRows), - getParentRow: () => - row.parentId ? table.getRow(row.parentId, true) : undefined, - getParentRows: () => { + return this._uniqueValuesCache[columnId] as any + } + + proto.renderValue = function (columnId: string) { + return this.getValue(columnId) ?? table.options.renderFallbackValue + } + + proto.getLeafRows = function () { + return flattenBy(this.subRows, d => d.subRows) + } + + proto.getParentRow = function () { + return this.parentId ? table.getRow(this.parentId, true) : undefined + } + + proto.getParentRows = function () { let parentRows: Row[] = [] - let currentRow = row + let currentRow = this while (true) { const parentRow = currentRow.getParentRow() if (!parentRow) break @@ -166,19 +183,24 @@ export const createRow = ( currentRow = parentRow } return parentRows.reverse() - }, - getAllCells: memo( - () => [table.getAllLeafColumns()], - leafColumns => { + } + + proto.getAllCells = memo( + function (this: Row) { + return [this, table.getAllLeafColumns()] + }, + (row, leafColumns) => { return leafColumns.map(column => { - return createCell(table, row as Row, column, column.id) + return createCell(table, row, column, column.id) }) }, getMemoOptions(table.options, 'debugRows', 'getAllCells') - ), + ) - _getAllCellsByColumnId: memo( - () => [row.getAllCells()], + proto._getAllCellsByColumnId = memo( + function (this: Row) { + return [this.getAllCells()] + }, allCells => { return allCells.reduce( (acc, cell) => { @@ -189,7 +211,36 @@ export const createRow = ( ) }, getMemoOptions(table.options, 'debugRows', 'getAllCellsByColumnId') - ), + ) + + rowProtosByTable.set(table, proto) + rowProto = proto + } + + return rowProto as CoreRow +} + +export const createRow = ( + table: Table, + id: string, + original: TData, + rowIndex: number, + depth: number, + subRows?: Row[], + parentId?: string +): Row => { + const row: CoreRow = Object.create(getRowProto(table)) + Object.assign(row, { + id, + index: rowIndex, + original, + depth, + parentId, + _valuesCache: {}, + }) + + if (subRows) { + row.subRows = subRows } for (let i = 0; i < table._features.length; i++) { diff --git a/packages/table-core/src/features/ColumnFiltering.ts b/packages/table-core/src/features/ColumnFiltering.ts index 24ebaedaf6..18e558f004 100644 --- a/packages/table-core/src/features/ColumnFiltering.ts +++ b/packages/table-core/src/features/ColumnFiltering.ts @@ -1,4 +1,4 @@ -import { RowModel } from '..' +import { getRowProto, RowModel } from '..' import { BuiltInFilterFn, filterFns } from '../filterFns' import { Column, @@ -362,14 +362,6 @@ export const ColumnFiltering: TableFeature = { } }, - createRow: ( - row: Row, - _table: Table - ): void => { - row.columnFilters = {} - row.columnFiltersMeta = {} - }, - createTable: (table: Table): void => { table.setColumnFilters = (updater: Updater) => { const leafColumns = table.getAllLeafColumns() @@ -411,6 +403,28 @@ export const ColumnFiltering: TableFeature = { return table._getFilteredRowModel() } + + // Lazy-init the backing caches on the instance so we don't take up memory for rows that don't need it + Object.defineProperties(getRowProto(table), { + columnFilters: { + get() { + return (this._columnFilters ??= {}) + }, + set(value) { + this._columnFilters = value + }, + enumerable: true, + }, + columnFiltersMeta: { + get() { + return (this._columnFiltersMeta ??= {}) + }, + set(value) { + this._columnFiltersMeta = value + }, + enumerable: true, + }, + }) }, } diff --git a/packages/table-core/src/features/ColumnGrouping.ts b/packages/table-core/src/features/ColumnGrouping.ts index 21e8781cc2..0240dfe25d 100644 --- a/packages/table-core/src/features/ColumnGrouping.ts +++ b/packages/table-core/src/features/ColumnGrouping.ts @@ -1,4 +1,4 @@ -import { RowModel } from '..' +import { getRowProto, RowModel } from '..' import { BuiltInAggregationFn, aggregationFns } from '../aggregationFns' import { AggregationFns, @@ -353,31 +353,37 @@ export const ColumnGrouping: TableFeature = { return table._getGroupedRowModel() } - }, - createRow: ( - row: Row, - table: Table - ): void => { - row.getIsGrouped = () => !!row.groupingColumnId - row.getGroupingValue = columnId => { - if (row._groupingValuesCache.hasOwnProperty(columnId)) { - return row._groupingValuesCache[columnId] - } + Object.defineProperty(getRowProto(table), '_groupingValuesCache', { + get() { + // Lazy-init the backing cache on the instance so we don't take up memory for rows that don't need it + return (this.__groupingValuesCache ??= {}) + }, + enumerable: true, + }) + + Object.assign(getRowProto(table), { + getIsGrouped() { + return !!this.groupingColumnId + }, + getGroupingValue(columnId) { + if (this._groupingValuesCache.hasOwnProperty(columnId)) { + return this._groupingValuesCache[columnId] + } - const column = table.getColumn(columnId) + const column = table.getColumn(columnId) - if (!column?.columnDef.getGroupingValue) { - return row.getValue(columnId) - } + if (!column?.columnDef.getGroupingValue) { + return this.getValue(columnId) + } - row._groupingValuesCache[columnId] = column.columnDef.getGroupingValue( - row.original - ) + this._groupingValuesCache[columnId] = column.columnDef.getGroupingValue( + this.original + ) - return row._groupingValuesCache[columnId] - } - row._groupingValuesCache = {} + return this._groupingValuesCache[columnId] + }, + } as GroupingRow & Row) }, createCell: ( diff --git a/packages/table-core/src/features/ColumnPinning.ts b/packages/table-core/src/features/ColumnPinning.ts index 1c0ef70799..d3487409c6 100644 --- a/packages/table-core/src/features/ColumnPinning.ts +++ b/packages/table-core/src/features/ColumnPinning.ts @@ -1,3 +1,4 @@ +import { getRowProto } from '..' import { OnChangeFn, Updater, @@ -9,6 +10,7 @@ import { TableFeature, } from '../types' import { getMemoOptions, makeStateUpdater, memo } from '../utils' +import { RowPinningRow } from './RowPinning' export type ColumnPinningPosition = false | 'left' | 'right' @@ -236,49 +238,6 @@ export const ColumnPinning: TableFeature = { } }, - createRow: ( - row: Row, - table: Table - ): void => { - row.getCenterVisibleCells = memo( - () => [ - row._getAllVisibleCells(), - table.getState().columnPinning.left, - table.getState().columnPinning.right, - ], - (allCells, left, right) => { - const leftAndRight: string[] = [...(left ?? []), ...(right ?? [])] - - return allCells.filter(d => !leftAndRight.includes(d.column.id)) - }, - getMemoOptions(table.options, 'debugRows', 'getCenterVisibleCells') - ) - row.getLeftVisibleCells = memo( - () => [row._getAllVisibleCells(), table.getState().columnPinning.left], - (allCells, left) => { - const cells = (left ?? []) - .map(columnId => allCells.find(cell => cell.column.id === columnId)!) - .filter(Boolean) - .map(d => ({ ...d, position: 'left' }) as Cell) - - return cells - }, - getMemoOptions(table.options, 'debugRows', 'getLeftVisibleCells') - ) - row.getRightVisibleCells = memo( - () => [row._getAllVisibleCells(), table.getState().columnPinning.right], - (allCells, right) => { - const cells = (right ?? []) - .map(columnId => allCells.find(cell => cell.column.id === columnId)!) - .filter(Boolean) - .map(d => ({ ...d, position: 'right' }) as Cell) - - return cells - }, - getMemoOptions(table.options, 'debugRows', 'getRightVisibleCells') - ) - }, - createTable: (table: Table): void => { table.setColumnPinning = updater => table.options.onColumnPinningChange?.(updater) @@ -332,5 +291,63 @@ export const ColumnPinning: TableFeature = { }, getMemoOptions(table.options, 'debugColumns', 'getCenterLeafColumns') ) + + Object.assign(getRowProto(table), { + getCenterVisibleCells: memo( + function (this: Row) { + return [ + this._getAllVisibleCells(), + table.getState().columnPinning.left, + table.getState().columnPinning.right, + ] + }, + (allCells, left, right) => { + const leftAndRight: string[] = [...(left ?? []), ...(right ?? [])] + + return allCells.filter(d => !leftAndRight.includes(d.column.id)) + }, + getMemoOptions(table.options, 'debugRows', 'getCenterVisibleCells') + ), + + getLeftVisibleCells: memo( + function (this: Row) { + return [ + this._getAllVisibleCells(), + table.getState().columnPinning.left, + ] + }, + (allCells, left) => { + const cells = (left ?? []) + .map( + columnId => allCells.find(cell => cell.column.id === columnId)! + ) + .filter(Boolean) + .map(d => ({ ...d, position: 'left' }) as Cell) + + return cells + }, + getMemoOptions(table.options, 'debugRows', 'getLeftVisibleCells') + ), + + getRightVisibleCells: memo( + function (this: Row) { + return [ + this._getAllVisibleCells(), + table.getState().columnPinning.right, + ] + }, + (allCells, right) => { + const cells = (right ?? []) + .map( + columnId => allCells.find(cell => cell.column.id === columnId)! + ) + .filter(Boolean) + .map(d => ({ ...d, position: 'right' }) as Cell) + + return cells + }, + getMemoOptions(table.options, 'debugRows', 'getRightVisibleCells') + ), + } as RowPinningRow & Row) }, } diff --git a/packages/table-core/src/features/ColumnVisibility.ts b/packages/table-core/src/features/ColumnVisibility.ts index f37e57be8e..ed8f5e900a 100644 --- a/packages/table-core/src/features/ColumnVisibility.ts +++ b/packages/table-core/src/features/ColumnVisibility.ts @@ -1,4 +1,4 @@ -import { ColumnPinningPosition } from '..' +import { ColumnPinningPosition, getRowProto } from '..' import { Cell, Column, @@ -201,28 +201,6 @@ export const ColumnVisibility: TableFeature = { } }, - createRow: ( - row: Row, - table: Table - ): void => { - row._getAllVisibleCells = memo( - () => [row.getAllCells(), table.getState().columnVisibility], - cells => { - return cells.filter(cell => cell.column.getIsVisible()) - }, - getMemoOptions(table.options, 'debugRows', '_getAllVisibleCells') - ) - row.getVisibleCells = memo( - () => [ - row.getLeftVisibleCells(), - row.getCenterVisibleCells(), - row.getRightVisibleCells(), - ], - (left, center, right) => [...left, ...center, ...right], - getMemoOptions(table.options, 'debugRows', 'getVisibleCells') - ) - }, - createTable: (table: Table): void => { const makeVisibleColumnsMethod = ( key: string, @@ -300,6 +278,30 @@ export const ColumnVisibility: TableFeature = { ) } } + + Object.assign(getRowProto(table), { + _getAllVisibleCells: memo( + function (this: Row) { + return [this.getAllCells(), table.getState().columnVisibility] + }, + cells => { + return cells.filter(cell => cell.column.getIsVisible()) + }, + getMemoOptions(table.options, 'debugRows', '_getAllVisibleCells') + ), + + getVisibleCells: memo( + function (this: Row) { + return [ + this.getLeftVisibleCells(), + this.getCenterVisibleCells(), + this.getRightVisibleCells(), + ] + }, + (left, center, right) => [...left, ...center, ...right], + getMemoOptions(table.options, 'debugRows', 'getVisibleCells') + ), + }) }, } diff --git a/packages/table-core/src/features/RowExpanding.ts b/packages/table-core/src/features/RowExpanding.ts index 15da45e0ea..7614ea81e7 100644 --- a/packages/table-core/src/features/RowExpanding.ts +++ b/packages/table-core/src/features/RowExpanding.ts @@ -1,4 +1,4 @@ -import { RowModel } from '..' +import { getRowProto, RowModel } from '..' import { OnChangeFn, Table, @@ -281,75 +281,76 @@ export const RowExpanding: TableFeature = { return table._getExpandedRowModel() } - }, - createRow: ( - row: Row, - table: Table - ): void => { - row.toggleExpanded = expanded => { - table.setExpanded(old => { - const exists = old === true ? true : !!old?.[row.id] - - let oldExpanded: ExpandedStateList = {} - - if (old === true) { - Object.keys(table.getRowModel().rowsById).forEach(rowId => { - oldExpanded[rowId] = true - }) - } else { - oldExpanded = old - } + Object.assign(getRowProto(table), { + toggleExpanded(expanded) { + table.setExpanded(old => { + const exists = old === true ? true : !!old?.[this.id] - expanded = expanded ?? !exists + let oldExpanded: ExpandedStateList = {} - if (!exists && expanded) { - return { - ...oldExpanded, - [row.id]: true, + if (old === true) { + Object.keys(table.getRowModel().rowsById).forEach(rowId => { + oldExpanded[rowId] = true + }) + } else { + oldExpanded = old } - } - if (exists && !expanded) { - const { [row.id]: _, ...rest } = oldExpanded - return rest - } + expanded = expanded ?? !exists - return old - }) - } - row.getIsExpanded = () => { - const expanded = table.getState().expanded + if (!exists && expanded) { + return { + ...oldExpanded, + [this.id]: true, + } + } - return !!( - table.options.getIsRowExpanded?.(row) ?? - (expanded === true || expanded?.[row.id]) - ) - } - row.getCanExpand = () => { - return ( - table.options.getRowCanExpand?.(row) ?? - ((table.options.enableExpanding ?? true) && !!row.subRows?.length) - ) - } - row.getIsAllParentsExpanded = () => { - let isFullyExpanded = true - let currentRow = row + if (exists && !expanded) { + const { [this.id]: _, ...rest } = oldExpanded + return rest + } - while (isFullyExpanded && currentRow.parentId) { - currentRow = table.getRow(currentRow.parentId, true) - isFullyExpanded = currentRow.getIsExpanded() - } + return old + }) + }, - return isFullyExpanded - } - row.getToggleExpandedHandler = () => { - const canExpand = row.getCanExpand() + getIsExpanded() { + const expanded = table.getState().expanded - return () => { - if (!canExpand) return - row.toggleExpanded() - } - } + return !!( + table.options.getIsRowExpanded?.(this) ?? + (expanded === true || expanded?.[this.id]) + ) + }, + + getCanExpand() { + return ( + table.options.getRowCanExpand?.(this) ?? + ((table.options.enableExpanding ?? true) && !!this.subRows?.length) + ) + }, + + getIsAllParentsExpanded() { + let isFullyExpanded = true + let currentRow = this + + while (isFullyExpanded && currentRow.parentId) { + currentRow = table.getRow(currentRow.parentId, true) + isFullyExpanded = currentRow.getIsExpanded() + } + + return isFullyExpanded + }, + + getToggleExpandedHandler() { + const canExpand = this.getCanExpand() + + return () => { + if (!canExpand) return + this.toggleExpanded() + } + }, + } as ExpandedRow & Row) }, } diff --git a/packages/table-core/src/features/RowPinning.ts b/packages/table-core/src/features/RowPinning.ts index 02288ab8d1..123dd8ddfd 100644 --- a/packages/table-core/src/features/RowPinning.ts +++ b/packages/table-core/src/features/RowPinning.ts @@ -1,3 +1,4 @@ +import { getRowProto } from '..' import { OnChangeFn, Updater, @@ -142,75 +143,6 @@ export const RowPinning: TableFeature = { } }, - createRow: ( - row: Row, - table: Table - ): void => { - row.pin = (position, includeLeafRows, includeParentRows) => { - const leafRowIds = includeLeafRows - ? row.getLeafRows().map(({ id }) => id) - : [] - const parentRowIds = includeParentRows - ? row.getParentRows().map(({ id }) => id) - : [] - const rowIds = new Set([...parentRowIds, row.id, ...leafRowIds]) - - table.setRowPinning(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)), - } - }) - } - row.getCanPin = () => { - const { enableRowPinning, enablePinning } = table.options - if (typeof enableRowPinning === 'function') { - return enableRowPinning(row) - } - return enableRowPinning ?? enablePinning ?? true - } - row.getIsPinned = () => { - const rowIds = [row.id] - - const { top, bottom } = table.getState().rowPinning - - const isTop = rowIds.some(d => top?.includes(d)) - const isBottom = rowIds.some(d => bottom?.includes(d)) - - return isTop ? 'top' : isBottom ? 'bottom' : false - } - row.getPinnedIndex = () => { - const position = row.getIsPinned() - if (!position) return -1 - - const visiblePinnedRowIds = ( - position === 'top' ? table.getTopRows() : table.getBottomRows() - )?.map(({ id }) => id) - - return visiblePinnedRowIds?.indexOf(row.id) ?? -1 - } - }, - createTable: (table: Table): void => { table.setRowPinning = updater => table.options.onRowPinningChange?.(updater) @@ -273,5 +205,74 @@ export const RowPinning: TableFeature = { }, getMemoOptions(table.options, 'debugRows', 'getCenterRows') ) + + Object.assign(getRowProto(table), { + pin(position, includeLeafRows, includeParentRows) { + const leafRowIds = includeLeafRows + ? this.getLeafRows().map(({ id }) => id) + : [] + const parentRowIds = includeParentRows + ? this.getParentRows().map(({ id }) => id) + : [] + const rowIds = new Set([...parentRowIds, this.id, ...leafRowIds]) + + table.setRowPinning(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)), + } + }) + }, + + getCanPin() { + const { enableRowPinning, enablePinning } = table.options + if (typeof enableRowPinning === 'function') { + return enableRowPinning(this) + } + return enableRowPinning ?? enablePinning ?? true + }, + + getIsPinned() { + const rowIds = [this.id] + + const { top, bottom } = table.getState().rowPinning + + const isTop = rowIds.some(d => top?.includes(d)) + const isBottom = rowIds.some(d => bottom?.includes(d)) + + return isTop ? 'top' : isBottom ? 'bottom' : false + }, + + getPinnedIndex() { + const position = this.getIsPinned() + if (!position) return -1 + + const visiblePinnedRowIds = ( + position === 'top' ? table.getTopRows() : table.getBottomRows() + )?.map(({ id }) => id) + + return visiblePinnedRowIds?.indexOf(this.id) ?? -1 + }, + } as RowPinningRow & Row) }, } diff --git a/packages/table-core/src/features/RowSelection.ts b/packages/table-core/src/features/RowSelection.ts index 90166823aa..8284c789eb 100644 --- a/packages/table-core/src/features/RowSelection.ts +++ b/packages/table-core/src/features/RowSelection.ts @@ -1,3 +1,4 @@ +import { getRowProto } from '..' import { OnChangeFn, Table, @@ -464,83 +465,82 @@ export const RowSelection: TableFeature = { ) } } - }, - createRow: ( - row: Row, - table: Table - ): void => { - row.toggleSelected = (value, opts) => { - const isSelected = row.getIsSelected() + Object.assign(getRowProto(table), { + toggleSelected(value, opts) { + const isSelected = this.getIsSelected() - table.setRowSelection(old => { - value = typeof value !== 'undefined' ? value : !isSelected + table.setRowSelection(old => { + value = typeof value !== 'undefined' ? value : !isSelected - if (row.getCanSelect() && isSelected === value) { - return old - } + if (this.getCanSelect() && isSelected === value) { + return old + } - const selectedRowIds = { ...old } + const selectedRowIds = { ...old } - mutateRowIsSelected( - selectedRowIds, - row.id, - value, - opts?.selectChildren ?? true, - table - ) + mutateRowIsSelected( + selectedRowIds, + this.id, + value, + opts?.selectChildren ?? true, + table + ) - return selectedRowIds - }) - } - row.getIsSelected = () => { - const { rowSelection } = table.getState() - return isRowSelected(row, rowSelection) - } + return selectedRowIds + }) + }, - row.getIsSomeSelected = () => { - const { rowSelection } = table.getState() - return isSubRowSelected(row, rowSelection, table) === 'some' - } + getIsSelected() { + const { rowSelection } = table.getState() + return isRowSelected(this, rowSelection) + }, - row.getIsAllSubRowsSelected = () => { - const { rowSelection } = table.getState() - return isSubRowSelected(row, rowSelection, table) === 'all' - } + getIsSomeSelected() { + const { rowSelection } = table.getState() + return isSubRowSelected(this, rowSelection, table) === 'some' + }, - row.getCanSelect = () => { - if (typeof table.options.enableRowSelection === 'function') { - return table.options.enableRowSelection(row) - } + getIsAllSubRowsSelected() { + const { rowSelection } = table.getState() + return isSubRowSelected(this, rowSelection, table) === 'all' + }, - return table.options.enableRowSelection ?? true - } + getCanSelect() { + if (typeof table.options.enableRowSelection === 'function') { + return table.options.enableRowSelection(this) + } - row.getCanSelectSubRows = () => { - if (typeof table.options.enableSubRowSelection === 'function') { - return table.options.enableSubRowSelection(row) - } + return table.options.enableRowSelection ?? true + }, - return table.options.enableSubRowSelection ?? true - } + getCanSelectSubRows() { + if (typeof table.options.enableSubRowSelection === 'function') { + return table.options.enableSubRowSelection(this) + } - row.getCanMultiSelect = () => { - if (typeof table.options.enableMultiRowSelection === 'function') { - return table.options.enableMultiRowSelection(row) - } + return table.options.enableSubRowSelection ?? true + }, - return table.options.enableMultiRowSelection ?? true - } - row.getToggleSelectedHandler = () => { - const canSelect = row.getCanSelect() + getCanMultiSelect() { + if (typeof table.options.enableMultiRowSelection === 'function') { + return table.options.enableMultiRowSelection(this) + } - return (e: unknown) => { - if (!canSelect) return - row.toggleSelected( - ((e as MouseEvent).target as HTMLInputElement)?.checked - ) - } - } + return table.options.enableMultiRowSelection ?? true + }, + + getToggleSelectedHandler() { + const canSelect = this.getCanSelect() + + return (e: unknown) => { + if (!canSelect) return + this.toggleSelected( + ((e as MouseEvent).target as HTMLInputElement)?.checked + ) + } + }, + } as RowSelectionRow & Row) }, } @@ -599,10 +599,8 @@ export function selectRowsFn( } if (row.subRows?.length) { - row = { - ...row, - subRows: recurseRows(row.subRows, depth + 1), - } + row = row.clone() + row.subRows = recurseRows(row.subRows, depth + 1) } if (isSelected) { diff --git a/packages/table-core/src/utils.ts b/packages/table-core/src/utils.ts index 77be57d750..98dad4d4fd 100755 --- a/packages/table-core/src/utils.ts +++ b/packages/table-core/src/utils.ts @@ -145,11 +145,11 @@ export function memo( let deps: any[] = [] let result: TResult | undefined - return depArgs => { + return function (this: any, depArgs) { let depTime: number if (opts.key && opts.debug) depTime = Date.now() - const newDeps = getDeps(depArgs) + const newDeps = getDeps.call(this, depArgs) const depsChanged = newDeps.length !== deps.length || @@ -164,7 +164,7 @@ export function memo( let resultTime: number if (opts.key && opts.debug) resultTime = Date.now() - result = fn(...newDeps) + result = fn.apply(this, newDeps) opts?.onChange?.(result) if (opts.key && opts.debug) { diff --git a/packages/table-core/src/utils/getSortedRowModel.ts b/packages/table-core/src/utils/getSortedRowModel.ts index 928c26448f..88c7549bb7 100644 --- a/packages/table-core/src/utils/getSortedRowModel.ts +++ b/packages/table-core/src/utils/getSortedRowModel.ts @@ -45,7 +45,7 @@ export function getSortedRowModel(): ( const sortData = (rows: Row[]) => { // This will also perform a stable sorting using the row index // if needed. - const sortedData = rows.map(row => ({ ...row })) + const sortedData = rows.map(row => row.clone()) sortedData.sort((rowA, rowB) => { for (let i = 0; i < availableSorting.length; i += 1) {