@tanstack/lit-table
Version:
Headless UI for building powerful tables & datagrids for Lit.
273 lines • 15.3 kB
TypeScript
import { FlexRender, LitRenderable } from "./flexRender.js";
import { LitTable } from "./TableController.js";
import { AccessorFn, AccessorFnColumnDef, AccessorKeyColumnDef, Cell, CellContext, CellData, Column, ColumnDef, DeepKeys, DeepValue, DisplayColumnDef, GroupColumnDef, Header, IdentifiedColumnDef, NoInfer, Row, RowData, Table, TableFeatures, TableOptions, TableState } from "@tanstack/table-core";
import { ReactiveControllerHost } from "lit";
import { Context, ContextConsumer } from "@lit/context";
//#region src/createTableHook.d.ts
type ComponentType<T extends Record<string, any>> = (props: T) => any;
type BoundComponents<TComponents extends Record<string, ComponentType<any>>> = { [TKey in keyof TComponents]: () => ReturnType<TComponents[TKey]>; };
/**
* Enhanced CellContext with pre-bound cell components.
* The `cell` property includes the registered cellComponents.
*/
type AppCellContext<TFeatures extends TableFeatures, TData extends RowData, TValue extends CellData, TCellComponents extends Record<string, ComponentType<any>>> = {
cell: Cell<TFeatures, TData, TValue> & BoundComponents<TCellComponents> & {
FlexRender: () => LitRenderable;
};
column: Column<TFeatures, TData, TValue>;
getValue: CellContext<TFeatures, TData, TValue>['getValue'];
renderValue: CellContext<TFeatures, TData, TValue>['renderValue'];
row: Row<TFeatures, TData>;
table: Table<TFeatures, TData>;
};
/**
* Enhanced HeaderContext with pre-bound header components.
* The `header` property includes the registered headerComponents.
*/
type AppHeaderContext<TFeatures extends TableFeatures, TData extends RowData, TValue extends CellData, THeaderComponents extends Record<string, ComponentType<any>>> = {
column: Column<TFeatures, TData, TValue>;
header: Header<TFeatures, TData, TValue> & BoundComponents<THeaderComponents> & {
FlexRender: () => LitRenderable;
};
table: Table<TFeatures, TData>;
};
/**
* Template type for column definitions that can be a string or a function.
*/
type AppColumnDefTemplate<TProps extends object> = string | ((props: TProps) => any);
/**
* Enhanced column definition base with pre-bound components in cell/header/footer contexts.
*/
type AppColumnDefBase<TFeatures extends TableFeatures, TData extends RowData, TValue extends CellData, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> = Omit<IdentifiedColumnDef<TFeatures, TData, TValue>, 'cell' | 'header' | 'footer'> & {
cell?: AppColumnDefTemplate<AppCellContext<TFeatures, TData, TValue, TCellComponents>>;
header?: AppColumnDefTemplate<AppHeaderContext<TFeatures, TData, TValue, THeaderComponents>>;
footer?: AppColumnDefTemplate<AppHeaderContext<TFeatures, TData, TValue, THeaderComponents>>;
};
/**
* Enhanced display column definition with pre-bound components.
*/
type AppDisplayColumnDef<TFeatures extends TableFeatures, TData extends RowData, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> = Omit<DisplayColumnDef<TFeatures, TData, unknown>, 'cell' | 'header' | 'footer'> & {
cell?: AppColumnDefTemplate<AppCellContext<TFeatures, TData, unknown, TCellComponents>>;
header?: AppColumnDefTemplate<AppHeaderContext<TFeatures, TData, unknown, THeaderComponents>>;
footer?: AppColumnDefTemplate<AppHeaderContext<TFeatures, TData, unknown, THeaderComponents>>;
};
/**
* Enhanced group column definition with pre-bound components.
*/
type AppGroupColumnDef<TFeatures extends TableFeatures, TData extends RowData, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> = Omit<GroupColumnDef<TFeatures, TData, unknown>, 'cell' | 'header' | 'footer' | 'columns'> & {
cell?: AppColumnDefTemplate<AppCellContext<TFeatures, TData, unknown, TCellComponents>>;
header?: AppColumnDefTemplate<AppHeaderContext<TFeatures, TData, unknown, THeaderComponents>>;
footer?: AppColumnDefTemplate<AppHeaderContext<TFeatures, TData, unknown, THeaderComponents>>;
columns?: ReadonlyArray<ColumnDef<TFeatures, TData, unknown>>;
};
/**
* Enhanced column helper with pre-bound components in cell/header/footer contexts.
* This enables TypeScript to know about the registered components when defining columns.
*/
type AppColumnHelper<TFeatures extends TableFeatures, TData extends RowData, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> = {
/**
* Creates a data column definition with an accessor key or function.
* The cell, header, and footer contexts include pre-bound components.
*/
accessor: <TAccessor extends AccessorFn<TData> | DeepKeys<TData>, TValue extends (TAccessor extends AccessorFn<TData, infer TReturn> ? TReturn : TAccessor extends DeepKeys<TData> ? DeepValue<TData, TAccessor> : never)>(accessor: TAccessor, column: TAccessor extends AccessorFn<TData> ? AppColumnDefBase<TFeatures, TData, TValue, TCellComponents, THeaderComponents> & {
id: string;
} : AppColumnDefBase<TFeatures, TData, TValue, TCellComponents, THeaderComponents>) => TAccessor extends AccessorFn<TData> ? AccessorFnColumnDef<TFeatures, TData, TValue> : AccessorKeyColumnDef<TFeatures, TData, TValue>;
/**
* Wraps an array of column definitions to preserve each column's individual TValue type.
*/
columns: <TColumns extends ReadonlyArray<ColumnDef<TFeatures, TData, any>>>(columns: [...TColumns]) => Array<ColumnDef<TFeatures, TData, any>> & [...TColumns];
/**
* Creates a display column definition for non-data columns.
* The cell, header, and footer contexts include pre-bound components.
*/
display: (column: AppDisplayColumnDef<TFeatures, TData, TCellComponents, THeaderComponents>) => DisplayColumnDef<TFeatures, TData, unknown>;
/**
* Creates a group column definition with nested child columns.
* The cell, header, and footer contexts include pre-bound components.
*/
group: (column: AppGroupColumnDef<TFeatures, TData, TCellComponents, THeaderComponents>) => GroupColumnDef<TFeatures, TData, unknown>;
};
/**
* Options for creating a table hook with pre-bound components and default table options.
* Extends all TableOptions except 'columns' | 'data' | 'store' | 'state' | 'initialState'.
*/
type CreateTableHookOptions<TFeatures extends TableFeatures, TTableComponents extends Record<string, ComponentType<any>>, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> = Omit<TableOptions<TFeatures, any>, 'columns' | 'data' | 'store' | 'state' | 'initialState'> & {
/**
* Table-level components that need access to the table instance.
* These are available directly on the table object returned by useAppTable.
* Use `useTableContext()` inside these components.
* @example { PaginationControls, GlobalFilter, RowCount }
*/
tableComponents?: TTableComponents;
/**
* Cell-level components that need access to the cell instance.
* These are available on the cell object passed to AppCell's children.
* Use `useCellContext()` inside these components.
* @example { TextCell, NumberCell, DateCell, CurrencyCell }
*/
cellComponents?: TCellComponents;
/**
* Header-level components that need access to the header instance.
* These are available on the header object passed to AppHeader/AppFooter's children.
* Use `useHeaderContext()` inside these components.
* @example { SortIndicator, ColumnFilter, ResizeHandle }
*/
headerComponents?: THeaderComponents;
};
/**
* Extended table API returned by useAppTable with all App wrapper functions
*/
type AppLitTable<TFeatures extends TableFeatures, TData extends RowData, TSelected, TTableComponents extends Record<string, ComponentType<any>>, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> = LitTable<TFeatures, TData, TSelected> & NoInfer<TTableComponents> & {
/**
* Wraps a cell and provides cell context with pre-bound cellComponents.
* @example
* ```ts
* ${table.AppCell(cell, (c) => html`<td>${c.FlexRender()}</td>`)}
* ```
*/
AppCell: <TValue extends CellData = CellData>(cell: Cell<TFeatures, TData, TValue>, renderFn: (cell: Cell<TFeatures, TData, TValue> & BoundComponents<TCellComponents> & {
FlexRender: () => LitRenderable;
}) => LitRenderable) => LitRenderable;
/**
* Wraps a header and provides header context with pre-bound headerComponents.
* @example
* ```ts
* ${table.AppHeader(header, (h) => html`<th>${h.FlexRender()}</th>`)}
* ```
*/
AppHeader: <TValue extends CellData = CellData>(header: Header<TFeatures, TData, TValue>, renderFn: (header: Header<TFeatures, TData, TValue> & BoundComponents<THeaderComponents> & {
FlexRender: () => LitRenderable;
}) => LitRenderable) => LitRenderable;
/**
* Wraps a footer and provides header context with pre-bound headerComponents.
* @example
* ```ts
* ${table.AppFooter(footer, (f) => html`<td>${f.FlexRender()}</td>`)}
* ```
*/
AppFooter: <TValue extends CellData = CellData>(header: Header<TFeatures, TData, TValue>, renderFn: (header: Header<TFeatures, TData, TValue> & BoundComponents<THeaderComponents> & {
FlexRender: () => LitRenderable;
}) => LitRenderable) => LitRenderable;
/**
* Convenience FlexRender function attached to the table instance.
* Renders cell, header, or footer content from column definitions.
* @example
* ```ts
* ${table.FlexRender({ header })}
* ${table.FlexRender({ cell })}
* ${table.FlexRender({ footer: header })}
* ```
*/
FlexRender: typeof FlexRender;
};
interface CreateTableHookResult<TFeatures extends TableFeatures, TTableComponents extends Record<string, ComponentType<any>>, TCellComponents extends Record<string, ComponentType<any>>, THeaderComponents extends Record<string, ComponentType<any>>> {
/** The features object that was passed to `createTableHook`. */
appFeatures: TFeatures;
/**
* A column helper pre-bound to `TFeatures` and the registered components, so
* the cell/header/footer render props expose the bound components.
*/
createAppColumnHelper: <TData extends RowData>() => AppColumnHelper<TFeatures, TData, TCellComponents, THeaderComponents>;
/**
* Creates a controller-like object whose `table()` method returns a table with
* the `App*` wrapper functions, a bound `FlexRender`, and the registered
* `tableComponents` attached. `TData` is inferred from the `data` option.
*/
useAppTable: <TData extends RowData, TSelected = TableState<TFeatures>>(host: ReactiveControllerHost & HTMLElement, tableOptions: Omit<TableOptions<TFeatures, TData>, 'features'>, selector?: (state: TableState<TFeatures>) => TSelected) => {
table: () => AppLitTable<TFeatures, TData, TSelected, TTableComponents, TCellComponents, THeaderComponents>;
};
/**
* Reads the table provided by the nearest ancestor that called `useAppTable`,
* via a `@lit/context` `ContextConsumer`. This is the BARE `LitTable` written
* to the provider, not the extended `AppLitTable`.
*/
useTableContext: <TData extends RowData = RowData>(host: ReactiveControllerHost & HTMLElement) => ContextConsumer<Context<symbol, LitTable<TFeatures, TData, any>>, ReactiveControllerHost & HTMLElement>;
/**
* Reads the cell instance from a `@lit/context` `ContextConsumer`. lit never
* provides an extended cell through context, so this is a BARE `Cell`.
*/
useCellContext: <TValue extends CellData = CellData>(host: ReactiveControllerHost & HTMLElement) => ContextConsumer<Context<symbol, Cell<TFeatures, any, TValue>>, ReactiveControllerHost & HTMLElement>;
/**
* Reads the header instance from a `@lit/context` `ContextConsumer`. lit never
* provides an extended header through context, so this is a BARE `Header`.
*/
useHeaderContext: <TValue extends CellData = CellData>(host: ReactiveControllerHost & HTMLElement) => ContextConsumer<Context<symbol, Header<TFeatures, any, TValue>>, ReactiveControllerHost & HTMLElement>;
}
/**
* Creates a custom table hook with pre-bound components for composition.
*
* This is the table equivalent of TanStack Form's `createFormHook`. It allows you to:
* - Define features, row models, and default options once, shared across all tables
* - Register reusable table, cell, and header components
* - Access table/cell/header instances via `@lit/context` in those components
* - Get a `useAppTable` hook that returns an extended table with App wrapper functions
* - Get a `createAppColumnHelper` function pre-bound to your features
*
* @example
* ```ts
* // hooks/table.ts
* export const {
* createAppColumnHelper,
* useAppTable,
* useTableContext,
* useCellContext,
* useHeaderContext,
* } = createTableHook({
* features: tableFeatures({
* rowPaginationFeature,
* rowSortingFeature,
* columnFilteringFeature,
* paginatedRowModel: createPaginatedRowModel(),
* sortedRowModel: createSortedRowModel(),
* filteredRowModel: createFilteredRowModel(),
* sortFns,
* filterFns,
* }),
* tableComponents: { PaginationControls, RowCount },
* cellComponents: { TextCell, NumberCell },
* headerComponents: { SortIndicator, ColumnFilter },
* })
*
* // Create column helper with TFeatures already bound
* const columnHelper = createAppColumnHelper<Person>()
*
* // my-table.ts
* @customElement('my-table')
* class MyTable extends LitElement {
* private appTable = useAppTable(this, {
* columns,
* data: this.data,
* })
*
* protected render() {
* const table = this.appTable.table()
*
* return html`
* <table>
* <thead>
* ${repeat(table.getHeaderGroups(), (hg) => hg.id, (hg) => html`
* <tr>
* ${hg.headers.map((h) => table.AppHeader(h, (header) => html`
* <th>${header.FlexRender()}</th>
* `))}
* </tr>
* `)}
* </thead>
* <tbody>
* ${table.getRowModel().rows.map((row) => html`
* <tr>
* ${row.getAllCells().map((c) => table.AppCell(c, (cell) => html`
* <td>${cell.FlexRender()}</td>
* `))}
* </tr>
* `)}
* </tbody>
* </table>
* `
* }
* }
* ```
*/
declare function createTableHook<TFeatures extends TableFeatures, const TTableComponents extends Record<string, ComponentType<any>>, const TCellComponents extends Record<string, ComponentType<any>>, const THeaderComponents extends Record<string, ComponentType<any>>>({ tableComponents, cellComponents, headerComponents, ...defaultTableOptions }: CreateTableHookOptions<TFeatures, TTableComponents, TCellComponents, THeaderComponents>): CreateTableHookResult<TFeatures, TTableComponents, TCellComponents, THeaderComponents>;
//#endregion
export { AppCellContext, AppColumnDefBase, AppColumnDefTemplate, AppColumnHelper, AppDisplayColumnDef, AppGroupColumnDef, AppHeaderContext, AppLitTable, BoundComponents, ComponentType, CreateTableHookOptions, CreateTableHookResult, createTableHook };