Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mleibman-db committed Feb 20, 2025
1 parent db745af commit 2d6e916
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 180 deletions.
19 changes: 16 additions & 3 deletions examples/vanilla/basic/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Person = {
status: string
progress: number
}

/*
const data: Person[] = [
{
firstName: 'tanner',
Expand All @@ -38,7 +38,10 @@ const data: Person[] = [
status: 'Complicated',
progress: 10,
},
]
];
*/

const data = new Array(50000).fill({});

const columnHelper = createColumnHelper<Person>()

Expand Down Expand Up @@ -96,8 +99,15 @@ const renderTable = () => {
theadElement.appendChild(trElement)
})


table.getRowModel();


/*
// Render table rows
table.getRowModel().rows.forEach(row => {
table.getRowModel().rows.forEach((row, idx) => {
if (idx > 10) return;
const trElement = document.createElement('tr')
row.getVisibleCells().forEach(cell => {
const tdElement = document.createElement('td')
Expand All @@ -110,6 +120,7 @@ const renderTable = () => {
tbodyElement.appendChild(trElement)
})
/*
// Render table footers
table.getFooterGroups().forEach(footerGroup => {
const trElement = document.createElement('tr')
Expand All @@ -123,6 +134,8 @@ const renderTable = () => {
tfootElement.appendChild(trElement)
})
*/

// Clear previous content and append new content
const wrapperElement = document.getElementById('wrapper') as HTMLDivElement
wrapperElement.innerHTML = ''
Expand Down
218 changes: 135 additions & 83 deletions packages/table-core/src/core/row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,130 @@ export interface CoreRow<TData extends RowData> {
subRows: Row<TData>[]
}

const rowProtosByTable = new WeakMap<Table<any>, any>()

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
)

return this._valuesCache[columnId] as any
},

getUniqueValues(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)

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

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

this._uniqueValuesCache[columnId] = column.columnDef.getUniqueValues(
this.original as TData,
this.index
)

return this._uniqueValuesCache[columnId] as any
},

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

getLeafRows() {
return flattenBy(this.subRows, d => d.subRows)
},

getParentRow() {
return this.parentId ? table.getRow(this.parentId, true) : undefined
},

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()
},
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')
),
}

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

return rowProto as CoreRow<TData>
}

export const createRow = <TData extends RowData>(
table: Table<TData>,
id: string,
Expand All @@ -101,97 +225,25 @@ export const createRow = <TData extends RowData>(
subRows?: Row<TData>[],
parentId?: string
): Row<TData> => {
let row: CoreRow<TData> = {
const row: CoreRow<TData> = Object.create(getRowProto(table))
Object.assign(row, {
id,
index: rowIndex,
original,
depth,
parentId,
_valuesCache: {},
_uniqueValuesCache: {},
getValue: columnId => {
if (row._valuesCache.hasOwnProperty(columnId)) {
return row._valuesCache[columnId]
}

const column = table.getColumn(columnId)

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

row._valuesCache[columnId] = column.accessorFn(
row.original as TData,
rowIndex
)

return row._valuesCache[columnId] as any
},
getUniqueValues: columnId => {
if (row._uniqueValuesCache.hasOwnProperty(columnId)) {
return row._uniqueValuesCache[columnId]
}

const column = table.getColumn(columnId)

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

if (!column.columnDef.getUniqueValues) {
row._uniqueValuesCache[columnId] = [row.getValue(columnId)]
return row._uniqueValuesCache[columnId]
}

row._uniqueValuesCache[columnId] = column.columnDef.getUniqueValues(
row.original as TData,
rowIndex
)

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: () => {
let parentRows: Row<TData>[] = []
let currentRow = row
while (true) {
const parentRow = currentRow.getParentRow()
if (!parentRow) break
parentRows.push(parentRow)
currentRow = parentRow
}
return parentRows.reverse()
},
getAllCells: memo(
() => [table.getAllLeafColumns()],
leafColumns => {
return leafColumns.map(column => {
return createCell(table, row as Row<TData>, column, column.id)
})
},
getMemoOptions(table.options, 'debugRows', 'getAllCells')
),

_getAllCellsByColumnId: memo(
() => [row.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')
),
})

if (subRows) {
row.subRows = subRows
}

// TODO: We could avoid looping over all built-in table features for every row here to make it a lot faster since
// they can just be applied to the row proto once. That would be a breaking change however for any custom features
// users may have, so keep this for now. We can optimize this by making built-in features only apply the update to
// the proto once however.

for (let i = 0; i < table._features.length; i++) {
const feature = table._features[i]
feature?.createRow?.(row as Row<TData>, table)
Expand Down
8 changes: 5 additions & 3 deletions packages/table-core/src/features/ColumnFiltering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,13 @@ export const ColumnFiltering: TableFeature = {
},

createRow: <TData extends RowData>(
row: Row<TData>,
rowInstance: Row<TData>,
_table: Table<TData>
): void => {
row.columnFilters = {}
row.columnFiltersMeta = {}
const proto = Object.getPrototypeOf(rowInstance) as Row<TData>;

proto.columnFilters = {}
proto.columnFiltersMeta = {}
},

createTable: <TData extends RowData>(table: Table<TData>): void => {
Expand Down
26 changes: 16 additions & 10 deletions packages/table-core/src/features/ColumnGrouping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,28 +356,34 @@ export const ColumnGrouping: TableFeature = {
},

createRow: <TData extends RowData>(
row: Row<TData>,
rowInstance: Row<TData>,
table: Table<TData>
): void => {
row.getIsGrouped = () => !!row.groupingColumnId
row.getGroupingValue = columnId => {
if (row._groupingValuesCache.hasOwnProperty(columnId)) {
return row._groupingValuesCache[columnId]
const proto = Object.getPrototypeOf(rowInstance) as Row<TData>

proto.getIsGrouped = function () {
return !!this.groupingColumnId
}

proto.getGroupingValue = function (columnId) {
if (this._groupingValuesCache.hasOwnProperty(columnId)) {
return this._groupingValuesCache[columnId]
}

const column = table.getColumn(columnId)

if (!column?.columnDef.getGroupingValue) {
return row.getValue(columnId)
return this.getValue(columnId)
}

row._groupingValuesCache[columnId] = column.columnDef.getGroupingValue(
row.original
this._groupingValuesCache[columnId] = column.columnDef.getGroupingValue(
this.original
)

return row._groupingValuesCache[columnId]
return this._groupingValuesCache[columnId]
}
row._groupingValuesCache = {}

proto._groupingValuesCache = {}
},

createCell: <TData extends RowData, TValue>(
Expand Down
Loading

0 comments on commit 2d6e916

Please sign in to comment.