Skip to content

Commit

Permalink
make typing more explicit and make 'subRows' safer
Browse files Browse the repository at this point in the history
  • Loading branch information
mleibman-db committed Feb 22, 2025
1 parent 24710f8 commit 0769660
Showing 1 changed file with 93 additions and 98 deletions.
191 changes: 93 additions & 98 deletions packages/table-core/src/core/row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,119 +102,114 @@ export function getRowProto<TData extends RowData>(table: Table<TData>) {
let rowProto = rowProtosByTable.get(table)

if (!rowProto) {
const obj: CoreRow<TData> = {
// props are here only for typing; they are set on the instance at runtime
id: 'unused',
depth: 0,
index: -1,
original: undefined as TData,
subRows: [],
_valuesCache: {},
_uniqueValuesCache: {},

getValue(columnId: string) {
if (this._valuesCache.hasOwnProperty(columnId)) {
return this._valuesCache[columnId]
}

const column = table.getColumn(columnId)

if (!column?.accessorFn) {
return undefined
}

this._valuesCache[columnId] = column.accessorFn(
this.original as TData,
this.index
)
const proto = {} as CoreRow<TData>

return this._valuesCache[columnId] as any
},
// 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

getUniqueValues(columnId: string) {
if (!this.hasOwnProperty('_uniqueValuesCache')) {
// lazy-init cache on the instance
this._uniqueValuesCache = {}
}
proto.getValue = function (columnId: string) {
if (this._valuesCache.hasOwnProperty(columnId)) {
return this._valuesCache[columnId]
}

if (this._uniqueValuesCache.hasOwnProperty(columnId)) {
return this._uniqueValuesCache[columnId]
}
const column = table.getColumn(columnId)

const column = table.getColumn(columnId)
if (!column?.accessorFn) {
return undefined
}

if (!column?.accessorFn) {
return undefined
}
this._valuesCache[columnId] = column.accessorFn(
this.original as TData,
this.index
)

if (!column.columnDef.getUniqueValues) {
this._uniqueValuesCache[columnId] = [this.getValue(columnId)]
return this._uniqueValuesCache[columnId]
}
return this._valuesCache[columnId] as any
}

this._uniqueValuesCache[columnId] = column.columnDef.getUniqueValues(
this.original as TData,
this.index
)
proto.getUniqueValues = function (columnId: string) {
if (!this.hasOwnProperty('_uniqueValuesCache')) {
// lazy-init cache on the instance
this._uniqueValuesCache = {}
}

return this._uniqueValuesCache[columnId] as any
},
if (this._uniqueValuesCache.hasOwnProperty(columnId)) {
return this._uniqueValuesCache[columnId]
}

renderValue(columnId: string) {
return this.getValue(columnId) ?? table.options.renderFallbackValue
},
const column = table.getColumn(columnId)

getLeafRows() {
return flattenBy(this.subRows, d => d.subRows)
},
if (!column?.accessorFn) {
return undefined
}

getParentRow() {
return this.parentId ? table.getRow(this.parentId, true) : undefined
},
if (!column.columnDef.getUniqueValues) {
this._uniqueValuesCache[columnId] = [this.getValue(columnId)]
return this._uniqueValuesCache[columnId]
}

getParentRows() {
let parentRows: Row<TData>[] = []
let currentRow = this
while (true) {
const parentRow = currentRow.getParentRow()
if (!parentRow) break
parentRows.push(parentRow)
currentRow = parentRow
}
return parentRows.reverse()
},
this._uniqueValuesCache[columnId] = column.columnDef.getUniqueValues(
this.original as TData,
this.index
)

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)
}

getAllCells: memo(
function (this: Row<TData>) {
return [this, table.getAllLeafColumns()]
},
(row, leafColumns) => {
return leafColumns.map(column => {
return createCell(table, row, column, column.id)
})
},
getMemoOptions(table.options, 'debugRows', 'getAllCells')
),

_getAllCellsByColumnId: memo(
function (this: Row<TData>) {
return [this.getAllCells()]
},
allCells => {
return allCells.reduce(
(acc, cell) => {
acc[cell.column.id] = cell
return acc
},
{} as Record<string, Cell<TData, unknown>>
)
},
getMemoOptions(table.options, 'debugRows', 'getAllCellsByColumnId')
),
proto.getParentRow = function () {
return this.parentId ? table.getRow(this.parentId, true) : undefined
}

rowProtosByTable.set(table, obj)
rowProto = obj
proto.getParentRows = function () {
let parentRows: Row<TData>[] = []
let currentRow = this
while (true) {
const parentRow = currentRow.getParentRow()
if (!parentRow) break
parentRows.push(parentRow)
currentRow = parentRow
}
return parentRows.reverse()
}

proto.getAllCells = memo(
function (this: Row<TData>) {
return [this, table.getAllLeafColumns()]
},
(row, leafColumns) => {
return leafColumns.map(column => {
return createCell(table, row, column, column.id)
})
},
getMemoOptions(table.options, 'debugRows', 'getAllCells')
)

proto._getAllCellsByColumnId = memo(
function (this: Row<TData>) {
return [this.getAllCells()]
},
allCells => {
return allCells.reduce(
(acc, cell) => {
acc[cell.column.id] = cell
return acc
},
{} as Record<string, Cell<TData, unknown>>
)
},
getMemoOptions(table.options, 'debugRows', 'getAllCellsByColumnId')
)

rowProtosByTable.set(table, proto)
rowProto = proto
}

return rowProto as CoreRow<TData>
Expand Down

0 comments on commit 0769660

Please sign in to comment.