@tanstack/table-core
Version:
Headless UI for building powerful tables & datagrids for TS/JS.
170 lines (154 loc) • 5.81 kB
text/typescript
import {
Column,
Table,
AccessorFn,
ColumnDef,
RowData,
ColumnDefResolved,
} from '../types'
import { getMemoOptions, memo } from '../utils'
export interface CoreColumn<TData extends RowData, TValue> {
/**
* The resolved accessor function to use when extracting the value for the column from each row. Will only be defined if the column def has a valid accessor key or function defined.
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#accessorfn)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
accessorFn?: AccessorFn<TData, TValue>
/**
* The original column def used to create the column.
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#columndef)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
columnDef: ColumnDef<TData, TValue>
/**
* The child column (if the column is a group column). Will be an empty array if the column is not a group column.
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#columns)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
columns: Column<TData, TValue>[]
/**
* The depth of the column (if grouped) relative to the root column def array.
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#depth)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
depth: number
/**
* Returns the flattened array of this column and all child/grand-child columns for this column.
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#getflatcolumns)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
getFlatColumns: () => Column<TData, TValue>[]
/**
* Returns an array of all leaf-node columns for this column. If a column has no children, it is considered the only leaf-node column.
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#getleafcolumns)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
getLeafColumns: () => Column<TData, TValue>[]
/**
* The resolved unique identifier for the column resolved in this priority:
- A manual `id` property from the column def
- The accessor key from the column def
- The header string from the column def
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#id)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
id: string
/**
* The parent column for this column. Will be undefined if this is a root column.
* @link [API Docs](https://tanstack.com/table/v8/docs/api/core/column#parent)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/column-defs)
*/
parent?: Column<TData, TValue>
}
export function createColumn<TData extends RowData, TValue>(
table: Table<TData>,
columnDef: ColumnDef<TData, TValue>,
depth: number,
parent?: Column<TData, TValue>
): Column<TData, TValue> {
const defaultColumn = table._getDefaultColumnDef()
const resolvedColumnDef = {
...defaultColumn,
...columnDef,
} as ColumnDefResolved<TData>
const accessorKey = resolvedColumnDef.accessorKey
let id =
resolvedColumnDef.id ??
(accessorKey
? typeof String.prototype.replaceAll === 'function'
? accessorKey.replaceAll('.', '_')
: accessorKey.replace(/\./g, '_')
: undefined) ??
(typeof resolvedColumnDef.header === 'string'
? resolvedColumnDef.header
: undefined)
let accessorFn: AccessorFn<TData> | undefined
if (resolvedColumnDef.accessorFn) {
accessorFn = resolvedColumnDef.accessorFn
} else if (accessorKey) {
// Support deep accessor keys
if (accessorKey.includes('.')) {
accessorFn = (originalRow: TData) => {
let result = originalRow as Record<string, any>
for (const key of accessorKey.split('.')) {
result = result?.[key]
if (process.env.NODE_ENV !== 'production' && result === undefined) {
console.warn(
`"${key}" in deeply nested key "${accessorKey}" returned undefined.`
)
}
}
return result
}
} else {
accessorFn = (originalRow: TData) =>
(originalRow as any)[resolvedColumnDef.accessorKey]
}
}
if (!id) {
if (process.env.NODE_ENV !== 'production') {
throw new Error(
resolvedColumnDef.accessorFn
? `Columns require an id when using an accessorFn`
: `Columns require an id when using a non-string header`
)
}
throw new Error()
}
let column: CoreColumn<TData, any> = {
id: `${String(id)}`,
accessorFn,
parent: parent as any,
depth,
columnDef: resolvedColumnDef as ColumnDef<TData, any>,
columns: [],
getFlatColumns: memo(
() => [true],
() => {
return [
column as Column<TData, TValue>,
...column.columns?.flatMap(d => d.getFlatColumns()),
]
},
getMemoOptions(table.options, 'debugColumns', 'column.getFlatColumns')
),
getLeafColumns: memo(
() => [table._getOrderColumnsFn()],
orderColumns => {
if (column.columns?.length) {
let leafColumns = column.columns.flatMap(column =>
column.getLeafColumns()
)
return orderColumns(leafColumns)
}
return [column as Column<TData, TValue>]
},
getMemoOptions(table.options, 'debugColumns', 'column.getLeafColumns')
),
}
for (const feature of table._features) {
feature.createColumn?.(column as Column<TData, TValue>, table)
}
// Yes, we have to convert table to unknown, because we know more than the compiler here.
return column as Column<TData, TValue>
}