package.src.core.table.ts Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of table-core Show documentation
Show all versions of table-core Show documentation
Headless UI for building powerful tables & datagrids for TS/JS.
The newest version!
import { functionalUpdate, memo, RequiredKeys } from '../utils'
import {
Updater,
TableOptionsResolved,
TableState,
Table,
InitialTableState,
Row,
Column,
RowModel,
ColumnDef,
TableOptions,
RowData,
TableMeta,
ColumnDefResolved,
GroupColumnDef,
} from '../types'
//
import { createColumn } from './column'
import { Headers } from './headers'
//
import { ColumnSizing } from '../features/ColumnSizing'
import { Expanding } from '../features/Expanding'
import { Filters } from '../features/Filters'
import { Grouping } from '../features/Grouping'
import { Ordering } from '../features/Ordering'
import { Pagination } from '../features/Pagination'
import { Pinning } from '../features/Pinning'
import { RowSelection } from '../features/RowSelection'
import { Sorting } from '../features/Sorting'
import { Visibility } from '../features/Visibility'
export interface TableFeature {
getDefaultOptions?: (table: any) => any
getInitialState?: (initialState?: InitialTableState) => any
createTable?: (table: any) => any
getDefaultColumnDef?: () => any
createColumn?: (column: any, table: any) => any
createHeader?: (column: any, table: any) => any
createCell?: (cell: any, column: any, row: any, table: any) => any
createRow?: (row: any, table: any) => any
}
const features = [
Headers,
Visibility,
Ordering,
Pinning,
Filters,
Sorting,
Grouping,
Expanding,
Pagination,
RowSelection,
ColumnSizing,
] as const
//
export interface CoreTableState {}
export interface CoreOptions {
data: TData[]
state: Partial
onStateChange: (updater: Updater) => void
debugAll?: boolean
debugTable?: boolean
debugHeaders?: boolean
debugColumns?: boolean
debugRows?: boolean
initialState?: InitialTableState
autoResetAll?: boolean
mergeOptions?: (
defaultOptions: TableOptions,
options: Partial>
) => TableOptions
meta?: TableMeta
getCoreRowModel: (table: Table) => () => RowModel
getSubRows?: (originalRow: TData, index: number) => undefined | TData[]
getRowId?: (originalRow: TData, index: number, parent?: Row) => string
columns: ColumnDef[]
defaultColumn?: Partial>
renderFallbackValue: any
}
export interface CoreInstance {
initialState: TableState
reset: () => void
options: RequiredKeys, 'state'>
setOptions: (newOptions: Updater>) => void
getState: () => TableState
setState: (updater: Updater) => void
_features: readonly TableFeature[]
_queue: (cb: () => void) => void
_getRowId: (_: TData, index: number, parent?: Row) => string
getCoreRowModel: () => RowModel
_getCoreRowModel?: () => RowModel
getRowModel: () => RowModel
getRow: (id: string) => Row
_getDefaultColumnDef: () => Partial>
_getColumnDefs: () => ColumnDef[]
_getAllFlatColumnsById: () => Record>
getAllColumns: () => Column[]
getAllFlatColumns: () => Column[]
getAllLeafColumns: () => Column[]
getColumn: (columnId: string) => Column | undefined
}
export function createTable(
options: TableOptionsResolved
): Table {
if (options.debugAll || options.debugTable) {
console.info('Creating Table Instance...')
}
let table = { _features: features } as unknown as Table
const defaultOptions = table._features.reduce((obj, feature) => {
return Object.assign(obj, feature.getDefaultOptions?.(table))
}, {}) as TableOptionsResolved
const mergeOptions = (options: TableOptionsResolved) => {
if (table.options.mergeOptions) {
return table.options.mergeOptions(defaultOptions, options)
}
return {
...defaultOptions,
...options,
}
}
const coreInitialState: CoreTableState = {}
let initialState = {
...coreInitialState,
...(options.initialState ?? {}),
} as TableState
table._features.forEach(feature => {
initialState = feature.getInitialState?.(initialState) ?? initialState
})
const queued: (() => void)[] = []
let queuedTimeout = false
const coreInstance: CoreInstance = {
_features: features,
options: {
...defaultOptions,
...options,
},
initialState,
_queue: cb => {
queued.push(cb)
if (!queuedTimeout) {
queuedTimeout = true
// Schedule a microtask to run the queued callbacks after
// the current call stack (render, etc) has finished.
Promise.resolve()
.then(() => {
while (queued.length) {
queued.shift()!()
}
queuedTimeout = false
})
.catch(error =>
setTimeout(() => {
throw error
})
)
}
},
reset: () => {
table.setState(table.initialState)
},
setOptions: updater => {
const newOptions = functionalUpdate(updater, table.options)
table.options = mergeOptions(newOptions) as RequiredKeys<
TableOptionsResolved,
'state'
>
},
getState: () => {
return table.options.state as TableState
},
setState: (updater: Updater) => {
table.options.onStateChange?.(updater)
},
_getRowId: (row: TData, index: number, parent?: Row) =>
table.options.getRowId?.(row, index, parent) ??
`${parent ? [parent.id, index].join('.') : index}`,
getCoreRowModel: () => {
if (!table._getCoreRowModel) {
table._getCoreRowModel = table.options.getCoreRowModel(table)
}
return table._getCoreRowModel!()
},
// The final calls start at the bottom of the model,
// expanded rows, which then work their way up
getRowModel: () => {
return table.getPaginationRowModel()
},
getRow: (id: string) => {
const row = table.getRowModel().rowsById[id]
if (!row) {
if (process.env.NODE_ENV !== 'production') {
throw new Error(`getRow expected an ID, but got ${id}`)
}
throw new Error()
}
return row
},
_getDefaultColumnDef: memo(
() => [table.options.defaultColumn],
defaultColumn => {
defaultColumn = (defaultColumn ?? {}) as Partial<
ColumnDef
>
return {
header: props => {
const resolvedColumnDef = props.header.column
.columnDef as ColumnDefResolved
if (resolvedColumnDef.accessorKey) {
return resolvedColumnDef.accessorKey
}
if (resolvedColumnDef.accessorFn) {
return resolvedColumnDef.id
}
return null
},
// footer: props => props.header.column.id,
cell: props => props.renderValue()?.toString?.() ?? null,
...table._features.reduce((obj, feature) => {
return Object.assign(obj, feature.getDefaultColumnDef?.())
}, {}),
...defaultColumn,
} as Partial>
},
{
debug: () => table.options.debugAll ?? table.options.debugColumns,
key: process.env.NODE_ENV === 'development' && 'getDefaultColumnDef',
}
),
_getColumnDefs: () => table.options.columns,
getAllColumns: memo(
() => [table._getColumnDefs()],
columnDefs => {
const recurseColumns = (
columnDefs: ColumnDef[],
parent?: Column,
depth = 0
): Column[] => {
return columnDefs.map(columnDef => {
const column = createColumn(table, columnDef, depth, parent)
const groupingColumnDef = columnDef as GroupColumnDef<
TData,
unknown
>
column.columns = groupingColumnDef.columns
? recurseColumns(groupingColumnDef.columns, column, depth + 1)
: []
return column
})
}
return recurseColumns(columnDefs)
},
{
key: process.env.NODE_ENV === 'development' && 'getAllColumns',
debug: () => table.options.debugAll ?? table.options.debugColumns,
}
),
getAllFlatColumns: memo(
() => [table.getAllColumns()],
allColumns => {
return allColumns.flatMap(column => {
return column.getFlatColumns()
})
},
{
key: process.env.NODE_ENV === 'development' && 'getAllFlatColumns',
debug: () => table.options.debugAll ?? table.options.debugColumns,
}
),
_getAllFlatColumnsById: memo(
() => [table.getAllFlatColumns()],
flatColumns => {
return flatColumns.reduce((acc, column) => {
acc[column.id] = column
return acc
}, {} as Record>)
},
{
key: process.env.NODE_ENV === 'development' && 'getAllFlatColumnsById',
debug: () => table.options.debugAll ?? table.options.debugColumns,
}
),
getAllLeafColumns: memo(
() => [table.getAllColumns(), table._getOrderColumnsFn()],
(allColumns, orderColumns) => {
let leafColumns = allColumns.flatMap(column => column.getLeafColumns())
return orderColumns(leafColumns)
},
{
key: process.env.NODE_ENV === 'development' && 'getAllLeafColumns',
debug: () => table.options.debugAll ?? table.options.debugColumns,
}
),
getColumn: columnId => {
const column = table._getAllFlatColumnsById()[columnId]
if (process.env.NODE_ENV !== 'production' && !column) {
console.error(`[Table] Column with id '${columnId}' does not exist.`)
}
return column
},
}
Object.assign(table, coreInstance)
table._features.forEach(feature => {
return Object.assign(table, feature.createTable?.(table))
})
return table
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy