Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 89 additions & 33 deletions docs/guide/custom-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,53 @@ TanStack Table's source code is arguably somewhat simple (at least we think so).
All of the functionality of a feature object can be described with the `TableFeature` type that is exported from TanStack Table. This type is a TypeScript interface that describes the shape of a feature object needed to create a feature.

```ts
export interface TableFeature<TConstructors extends FeatureConstructors> {
assignCellPrototype?: AssignCellPrototype<TConstructors>
assignColumnPrototype?: AssignColumnPrototype<TConstructors>
assignHeaderPrototype?: AssignHeaderPrototype<TConstructors>
assignRowPrototype?: AssignRowPrototype<TConstructors>
constructTableAPIs?: ConstructTableAPIs<TConstructors>
getDefaultColumnDef?: GetDefaultColumnDef<TConstructors>
getDefaultTableOptions?: GetDefaultTableOptions<TConstructors>
getInitialState?: GetInitialState<TConstructors>
initRowInstanceData?: InitRowInstanceData<TConstructors>
export interface TableFeature {
assignCellPrototype?: <
TFeatures extends TableFeatures,
TData extends RowData,
>(
prototype: Record<string, any>,
table: Table_Internal<TFeatures, TData>,
) => void
assignColumnPrototype?: <
TFeatures extends TableFeatures,
TData extends RowData,
>(
prototype: Record<string, any>,
table: Table_Internal<TFeatures, TData>,
) => void
assignHeaderPrototype?: <
TFeatures extends TableFeatures,
TData extends RowData,
>(
prototype: Record<string, any>,
table: Table_Internal<TFeatures, TData>,
) => void
assignRowPrototype?: <TFeatures extends TableFeatures, TData extends RowData>(
prototype: Record<string, any>,
table: Table_Internal<TFeatures, TData>,
) => void
constructTableAPIs?: <TFeatures extends TableFeatures, TData extends RowData>(
table: Table_Internal<TFeatures, TData>,
) => void
getDefaultColumnDef?: <
TFeatures extends TableFeatures,
TData extends RowData,
TValue extends CellData = CellData,
>() => ColumnDefBase_All<TFeatures, TData, TValue>
getDefaultTableOptions?: <
TFeatures extends TableFeatures,
TData extends RowData,
>(
table: Table_Internal<TFeatures, TData>,
) => Partial<TableOptions_All<TFeatures, TData>>
getInitialState?: (initialState: Partial<TableState_All>) => TableState_All
initRowInstanceData?: <
TFeatures extends TableFeatures,
TData extends RowData,
>(
row: Row<TFeatures, TData>,
) => void
}
```

Expand Down Expand Up @@ -140,22 +177,34 @@ export interface Table_Density {
setDensity: (updater: Updater<DensityState>) => void
toggleDensity: (value?: DensityState) => void
}

interface DensityPluginConstructors {
Table: Table_Density
TableOptions: TableOptions_Density
TableState: TableState_Density
}
```

#### Step 2: Add the Feature to the TableFeatures Interface
#### Step 2: Add the Feature to TanStack Table's Feature Maps

TanStack Table uses the keys passed to `tableFeatures({ ... })` to infer which feature state, options, and APIs exist on a table. To make a custom feature key type-safe, add it to the exported `Plugins` interface with declaration merging.
TanStack Table uses the keys passed to `tableFeatures({ ... })` to infer which feature state, options, and APIs exist on a table. To make a custom feature key type-safe, add it to the exported `Plugins`, `TableState_FeatureMap`, `TableOptions_FeatureMap`, and `Table_FeatureMap` interfaces with declaration merging.

```ts
declare module '@tanstack/react-table' {
interface Plugins {
densityPlugin?: TableFeature<DensityPluginConstructors>
densityPlugin: TableFeature
}

interface TableState_FeatureMap {
densityPlugin: TableState_Density
}

interface TableOptions_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityPlugin: TableOptions_Density
}

interface Table_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityPlugin: Table_Density
}
}
```
Expand All @@ -169,7 +218,7 @@ With all of that TypeScript setup out of the way, we can now create the feature
Use the `TableFeature` type to ensure that you are creating the feature object correctly. If the TypeScript types are set up correctly, you should have no TypeScript errors when you create the feature object with the new state, options, and instance APIs.

```ts
export const densityPlugin: TableFeature<DensityPluginConstructors> = {
export const densityPlugin: TableFeature = {
// define the new feature's initial state
getInitialState: (initialState) => {
return {
Expand All @@ -190,19 +239,26 @@ export const densityPlugin: TableFeature<DensityPluginConstructors> = {

// define the new feature's table instance methods
constructTableAPIs: (table) => {
table.setDensity = (updater) => {
const safeUpdater: Updater<DensityState> = (old) => {
const newState = functionalUpdate(updater, old)
return newState
}
return table.options.onDensityChange?.(safeUpdater)
}
table.toggleDensity = (value) => {
table.setDensity?.((old) => {
if (value) return value
return old === 'lg' ? 'md' : old === 'md' ? 'sm' : 'lg'
})
}
assignTableAPIs('densityPlugin', table, {
table_setDensity: {
fn: (updater: Updater<DensityState>) => {
const safeUpdater: Updater<DensityState> = (old) => {
const newState = functionalUpdate(updater, old)
return newState
}
return table.options.onDensityChange?.(safeUpdater)
},
},
table_toggleDensity: {
fn: (value?: DensityState) => {
const safeUpdater: Updater<DensityState> = (old) => {
if (value) return value
return old === 'lg' ? 'md' : old === 'md' ? 'sm' : 'lg'
}
return table.options.onDensityChange?.(safeUpdater)
},
},
})
},

// if you need to add row instance APIs...
Expand Down
82 changes: 49 additions & 33 deletions examples/angular/custom-plugin/src/app/density/density-feature.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { functionalUpdate, makeStateUpdater } from '@tanstack/angular-table'
import {
assignTableAPIs,
functionalUpdate,
makeStateUpdater,
} from '@tanstack/angular-table'
import type {
OnChangeFn,
RowData,
TableFeature,
TableFeatures,
Updater,
Expand All @@ -26,16 +31,32 @@ export interface Table_Density {
toggleDensity: (value?: DensityState) => void
}

interface DensityPluginConstructors<TFeatures extends TableFeatures, TData> {
Table: Table_Density
TableOptions: TableOptions_Density
TableState: TableState_Density
declare module '@tanstack/angular-table' {
interface Plugins {
densityPlugin: TableFeature
}

interface TableState_FeatureMap {
densityPlugin: TableState_Density
}

interface TableOptions_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityPlugin: TableOptions_Density
}

interface Table_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityPlugin: Table_Density
}
}

// Here is all of the actual javascript code for our new feature
export const densityPlugin: TableFeature<
DensityPluginConstructors<TableFeatures, Table_Density>
> = {
export const densityPlugin: TableFeature = {
// define the new feature's initial state
getInitialState: (initialState) => {
return {
Expand All @@ -56,31 +77,26 @@ export const densityPlugin: TableFeature<

// define the new feature's table instance methods
constructTableAPIs: (table) => {
table.setDensity = (updater) => {
const safeUpdater: Updater<DensityState> = (old) => {
const newState = functionalUpdate(updater, old)
return newState
}
return table.options.onDensityChange?.(safeUpdater)
}
table.toggleDensity = (value) => {
table.setDensity?.((old) => {
if (value) return value
return old === 'lg' ? 'md' : old === 'md' ? 'sm' : 'lg' // cycle through the 3 options
})
}
assignTableAPIs('densityPlugin', table, {
table_setDensity: {
fn: (updater: Updater<DensityState>) => {
const safeUpdater: Updater<DensityState> = (old) => {
const newState = functionalUpdate(updater, old)
return newState
}
return table.options.onDensityChange?.(safeUpdater)
},
},
table_toggleDensity: {
fn: (value?: DensityState) => {
const safeUpdater: Updater<DensityState> = (old) => {
if (value) return value
return old === 'lg' ? 'md' : old === 'md' ? 'sm' : 'lg' // cycle through the 3 options
}
return table.options.onDensityChange?.(safeUpdater)
},
},
})
},

// if you need to add row instance APIs...
// constructRowAPIs: (row) => {},

// if you need to add cell instance APIs...
// constructCellAPIs: (cell) => {},

// if you need to add column instance APIs...
// constructColumnAPIs: (column) => {},

// if you need to add header instance APIs...
// constructHeaderAPIs: (header) => {},
}
// end of custom feature code
76 changes: 46 additions & 30 deletions examples/preact/custom-plugin/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useMemo, useState } from 'preact/hooks'
import { render } from 'preact'
import './index.css'
import {
assignTableAPIs,
columnFilteringFeature,
createColumnHelper,
createFilteredRowModel,
Expand All @@ -21,7 +22,9 @@ import type {
Column,
OnChangeFn,
PreactTable,
RowData,
TableFeature,
TableFeatures,
Updater,
} from '@tanstack/preact-table'
import type { Person } from './makeData'
Expand All @@ -46,14 +49,32 @@ export interface Table_Density {
toggleDensity: (value?: DensityState) => void
}

interface DensityPluginConstructors {
Table: Table_Density
TableOptions: TableOptions_Density
TableState: TableState_Density
declare module '@tanstack/preact-table' {
interface Plugins {
densityPlugin: TableFeature
}

interface TableState_FeatureMap {
densityPlugin: TableState_Density
}

interface TableOptions_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityPlugin: TableOptions_Density
}

interface Table_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityPlugin: Table_Density
}
}

// Here is all of the actual javascript code for our new feature
export const densityPlugin: TableFeature<DensityPluginConstructors> = {
export const densityPlugin: TableFeature = {
// define the new feature's initial state
getInitialState: (initialState) => {
return {
Expand All @@ -74,32 +95,27 @@ export const densityPlugin: TableFeature<DensityPluginConstructors> = {

// define the new feature's table instance methods
constructTableAPIs: (table) => {
table.setDensity = (updater) => {
const safeUpdater: Updater<DensityState> = (old) => {
const newState = functionalUpdate(updater, old)
return newState
}
return table.options.onDensityChange?.(safeUpdater)
}
table.toggleDensity = (value) => {
table.setDensity?.((old) => {
if (value) return value
return old === 'lg' ? 'md' : old === 'md' ? 'sm' : 'lg' // cycle through the 3 options
})
}
assignTableAPIs('densityPlugin', table, {
table_setDensity: {
fn: (updater: Updater<DensityState>) => {
const safeUpdater: Updater<DensityState> = (old) => {
const newState = functionalUpdate(updater, old)
return newState
}
return table.options.onDensityChange?.(safeUpdater)
},
},
table_toggleDensity: {
fn: (value?: DensityState) => {
const safeUpdater: Updater<DensityState> = (old) => {
if (value) return value
return old === 'lg' ? 'md' : old === 'md' ? 'sm' : 'lg' // cycle through the 3 options
}
return table.options.onDensityChange?.(safeUpdater)
},
},
})
},

// if you need to add row instance APIs...
// constructRowAPIs: (row) => {},

// if you need to add cell instance APIs...
// constructCellAPIs: (cell) => {},

// if you need to add column instance APIs...
// constructColumnAPIs: (column) => {},

// if you need to add header instance APIs...
// constructHeaderAPIs: (header) => {},
}
// end of custom feature code

Expand Down
Loading
Loading