diff --git a/docs/config.json b/docs/config.json index 9c9951a11f..1bdb61a68e 100644 --- a/docs/config.json +++ b/docs/config.json @@ -470,6 +470,9 @@ { "label": "LitTable", "to": "framework/lit/reference/type-aliases/LitTable" }, { "label": "AppLitTable", "to": "framework/lit/reference/type-aliases/AppLitTable" }, { "label": "CreateTableHookOptions", "to": "framework/lit/reference/type-aliases/CreateTableHookOptions" }, + { "label": "subscribe", "to": "framework/lit/reference/variables/subscribe" }, + { "label": "SubscribeDirective", "to": "framework/lit/reference/classes/SubscribeDirective" }, + { "label": "SelectionSource", "to": "framework/lit/reference/type-aliases/SelectionSource" }, { "label": "FlexRender", "to": "framework/lit/reference/functions/FlexRender-1" }, { "label": "flexRender", "to": "framework/lit/reference/functions/flexRender" } ] @@ -799,6 +802,7 @@ { "label": "Basic (useAppTable)", "to": "framework/lit/examples/basic-app-table" }, { "label": "Basic (External State)", "to": "framework/lit/examples/basic-external-state" }, { "label": "Basic (External Atoms)", "to": "framework/lit/examples/basic-external-atoms" }, + { "label": "Basic (Subscribe)", "to": "framework/lit/examples/basic-subscribe" }, { "label": "Header Groups", "to": "framework/lit/examples/column-groups" } ] }, diff --git a/docs/framework/lit/guide/migrating.md b/docs/framework/lit/guide/migrating.md index 3125e2ddc7..54b85be4f2 100644 --- a/docs/framework/lit/guide/migrating.md +++ b/docs/framework/lit/guide/migrating.md @@ -272,7 +272,7 @@ Lit v9 table state is atom-backed and controller-driven. The controller requests | `table.state` | Full registered table state by default, or selected state from the second argument to `tableController.table(...)`. | | `table.store.state` | Current full table state snapshot. | | `table.atoms..get()` | Narrow current-value read for one state slice. | -| `table.Subscribe` | Template helper for selecting table state while rendering. | +| `table.subscribe` | Template helper for selecting table state while rendering. | | `table.baseAtoms.` | Internal writable atoms. Prefer feature APIs or external atoms. | ### Accessing State @@ -319,20 +319,23 @@ table.state.pagination Passing `(state) => state` is equivalent to the default selector and is no longer necessary. -### Selecting State with `table.Subscribe` +### Selecting State with `table.subscribe` ```ts -${table.Subscribe({ - selector: (state) => ({ - pagination: state.pagination, - }), - children: ({ pagination }) => html` +private paginationSelector = (state) => ({ + pagination: state.pagination, +}) + +${table.subscribe( + table.store, + this.paginationSelector, + ({ pagination }) => html` Page ${pagination.pageIndex + 1} `, -})} +)} ``` -`table.Subscribe` can also accept a `source`, but the current Lit adapter invalidates the host through the table store subscription. Treat source mode as render-time selection convenience. +It is advised to use a stable reference for the selector function, such as a class method or an arrow function defined outside of render, to avoid unnecessary re-renders. ### Controlled State diff --git a/docs/framework/lit/guide/row-selection.md b/docs/framework/lit/guide/row-selection.md index 7d8aad7b16..15af39680c 100644 --- a/docs/framework/lit/guide/row-selection.md +++ b/docs/framework/lit/guide/row-selection.md @@ -58,7 +58,7 @@ console.log(table.getFilteredSelectedRowModel().rows) //get filtered client-side console.log(table.getGroupedSelectedRowModel().rows) //get grouped client-side selected rows ``` -In event handlers or other non-render code, you can also read the current snapshot with `table.atoms.rowSelection.get()`. In your `render` method, prefer `table.state.rowSelection` (or `table.Subscribe`), since the `TableController` keeps the host updated through the table store subscription. +In event handlers or other non-render code, you can also read the current snapshot with `table.atoms.rowSelection.get()`. In your `render` method, prefer `table.state.rowSelection` (or the `table.subscribe` directive), since the `TableController` keeps the host updated through the table store subscription. > Note: If you are using `manualPagination`, be aware that the `getSelectedRowModel` API will only return selected rows on the current page because table row models can only generate rows based on the `data` that is passed in. Row selection state, however, can contain row ids that are not present in the `data` array just fine. diff --git a/docs/framework/lit/guide/table-state.md b/docs/framework/lit/guide/table-state.md index aceb1f4902..b8129beca6 100644 --- a/docs/framework/lit/guide/table-state.md +++ b/docs/framework/lit/guide/table-state.md @@ -70,7 +70,7 @@ There are two different questions when reading table state: - Do you only need the current value? - Or should the Lit host update when that value changes? -Use a direct atom or store read for the current value. Use `table.state` or `table.Subscribe` in render output when the host should reflect selected table state. +Use a direct atom or store read for the current value. Use `table.state` or `table.subscribe` in render output when the host should reflect selected table state. #### Reading State Without Subscribing @@ -88,7 +88,7 @@ const tableState = table.store.state const pagination = table.store.state.pagination ``` -These reads are current-value reads. The `TableController` handles host invalidation through its subscriptions to the table store and options store. If the UI needs to stay reactive to table state changes, use `table.state`, `table.Subscribe`, or a TanStack Store subscription. +These reads are current-value reads. The `TableController` handles host invalidation through its subscriptions to the table store and options store. If the UI needs to stay reactive to table state changes, use `table.state`, `table.subscribe`, or a TanStack Store subscription. #### Reading Reactive State with TableController @@ -109,22 +109,28 @@ const table = this.tableController.table( table.state.pagination ``` -#### Selecting State with table.Subscribe +#### Selecting State with table.subscribe -Use `table.Subscribe` in templates to select a slice of table state while rendering. +Use `table.subscribe` in templates to select a slice of table state while rendering. ```ts -${table.Subscribe({ - selector: (state) => ({ - pagination: state.pagination, - }), - children: ({ pagination }) => html` +private paginationSelector = (state) => ({ + pagination: state.pagination, +}) + +${table.subscribe( + table.store, + this.paginationSelector, + ({ pagination }) => html` Page ${pagination.pageIndex + 1} `, -})} +)} ``` -`table.Subscribe` can also accept a `source`, but in the current Lit adapter host invalidation is wired through the full `table.store` subscription. Treat source mode as a render-time selection convenience, not a guarantee of source-only host invalidation. +The template will only be evaluated when the selected state slice changes. The `table.subscribe` API is useful for fine-grained reactivity in templates. +It is important to provide a stable reference for the selector function to avoid unnecessary re-renders. You can define the selector as a class property or method to ensure it remains stable across renders. + + ### Setting Table State diff --git a/docs/framework/lit/reference/classes/SubscribeDirective.md b/docs/framework/lit/reference/classes/SubscribeDirective.md new file mode 100644 index 0000000000..cba28097d1 --- /dev/null +++ b/docs/framework/lit/reference/classes/SubscribeDirective.md @@ -0,0 +1,327 @@ +--- +id: SubscribeDirective +title: SubscribeDirective +--- + +# Class: SubscribeDirective + +Defined in: [packages/lit-table/src/subscribe-directive.ts:46](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L46) + +An asynchronous Lit directive that subscribes to a `@tanstack/lit-store` +source and triggers re-renders specifically for the template portion it wraps. +* It uses a "fake" `ReactiveControllerHost` to bridge the gap between +TanStack's standard controller requirements and the `AsyncDirective` lifecycle. + +## Extends + +- `AsyncDirective` + +## Constructors + +### Constructor + +```ts +new SubscribeDirective(_partInfo): SubscribeDirective; +``` + +Defined in: node\_modules/.pnpm/lit-html@3.3.3/node\_modules/lit-html/development/directive.d.ts:61 + +#### Parameters + +##### \_partInfo + +`PartInfo` + +#### Returns + +`SubscribeDirective` + +#### Inherited from + +```ts +AsyncDirective.constructor +``` + +## Properties + +### isConnected + +```ts +isConnected: boolean; +``` + +Defined in: node\_modules/.pnpm/lit-html@3.3.3/node\_modules/lit-html/development/async-directive.d.ts:143 + +The connection state for this Directive. + +#### Inherited from + +```ts +AsyncDirective.isConnected +``` + +## Accessors + +### \_$isConnected + +#### Get Signature + +```ts +get _$isConnected(): boolean; +``` + +Defined in: node\_modules/.pnpm/lit-html@3.3.3/node\_modules/lit-html/development/directive.d.ts:62 + +##### Returns + +`boolean` + +#### Inherited from + +```ts +AsyncDirective._$isConnected +``` + +## Methods + +### \_$initialize() + +```ts +_$initialize( + part, + parent, + attributeIndex): void; +``` + +Defined in: node\_modules/.pnpm/lit-html@3.3.3/node\_modules/lit-html/development/async-directive.d.ts:150 + +Initialize the part with internal fields + +#### Parameters + +##### part + +`Part` + +##### parent + +`Disconnectable` + +##### attributeIndex + +`number` | `undefined` + +#### Returns + +`void` + +#### Inherited from + +```ts +AsyncDirective._$initialize +``` + +*** + +### disconnected() + +```ts +disconnected(): void; +``` + +Defined in: [packages/lit-table/src/subscribe-directive.ts:141](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L141) + +Cleans up the controller subscription when the directive is removed from the DOM. + +#### Returns + +`void` + +#### Overrides + +```ts +AsyncDirective.disconnected +``` + +*** + +### reconnected() + +```ts +reconnected(): void; +``` + +Defined in: [packages/lit-table/src/subscribe-directive.ts:146](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L146) + +Restores the controller subscription when the directive is re-attached to the DOM. + +#### Returns + +`void` + +#### Overrides + +```ts +AsyncDirective.reconnected +``` + +*** + +### render() + +#### Call Signature + +```ts +render(source, template): unknown; +``` + +Defined in: [packages/lit-table/src/subscribe-directive.ts:64](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L64) + +Renders the entire state of the source without a selector. + +##### Type Parameters + +###### TSource + +`TSource` + +##### Parameters + +###### source + +[`SelectionSource`](../type-aliases/SelectionSource.md)\<`TSource`\> + +The store or atom to subscribe to. + +###### template + +`TemplateFunction`\<`TSource`\> + +The render function receiving the full state. + +##### Returns + +`unknown` + +##### Overrides + +```ts +AsyncDirective.render +``` + +#### Call Signature + +```ts +render( + source, + selector, + template): unknown; +``` + +Defined in: [packages/lit-table/src/subscribe-directive.ts:75](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L75) + +Renders a specific slice of state derived via a selector function. + +##### Type Parameters + +###### TSource + +`TSource` + +###### TSelected + +`TSelected` + +##### Parameters + +###### source + +[`SelectionSource`](../type-aliases/SelectionSource.md)\<`TSource`\> + +The store or atom to subscribe to. + +###### selector + +`Selector`\<`TSource`, `TSelected`\> + +A function to extract the relevant slice of state. + +###### template + +`TemplateFunction`\<`TSelected`\> + +The render function receiving the selected state slice. + +##### Returns + +`unknown` + +##### Overrides + +```ts +AsyncDirective.render +``` + +*** + +### setValue() + +```ts +setValue(value): void; +``` + +Defined in: node\_modules/.pnpm/lit-html@3.3.3/node\_modules/lit-html/development/async-directive.d.ts:161 + +Sets the value of the directive's Part outside the normal `update`/`render` +lifecycle of a directive. + +This method should not be called synchronously from a directive's `update` +or `render`. + +#### Parameters + +##### value + +`unknown` + +The value to set + +#### Returns + +`void` + +#### Inherited from + +```ts +AsyncDirective.setValue +``` + +*** + +### update() + +```ts +update(_part, args): unknown; +``` + +Defined in: [packages/lit-table/src/subscribe-directive.ts:90](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L90) + +#### Parameters + +##### \_part + +`Part` + +##### args + +\[[`SelectionSource`](../type-aliases/SelectionSource.md)\<`any`\>, `TemplateFunction`\<`any`\>\] | \[[`SelectionSource`](../type-aliases/SelectionSource.md)\<`any`\>, `Selector`\<`any`, `any`\>, `TemplateFunction`\<`any`\>\] + +#### Returns + +`unknown` + +#### Overrides + +```ts +AsyncDirective.update +``` diff --git a/docs/framework/lit/reference/classes/TableController.md b/docs/framework/lit/reference/classes/TableController.md index 8d1cde26f5..eeb5412df6 100644 --- a/docs/framework/lit/reference/classes/TableController.md +++ b/docs/framework/lit/reference/classes/TableController.md @@ -5,7 +5,7 @@ title: TableController # Class: TableController\ -Defined in: [TableController.ts:138](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L138) +Defined in: [packages/lit-table/src/TableController.ts:117](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L117) A Lit ReactiveController for TanStack Table integration. @@ -56,7 +56,7 @@ class MyTable extends LitElement { new TableController(host): TableController; ``` -Defined in: [TableController.ts:149](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L149) +Defined in: [packages/lit-table/src/TableController.ts:128](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L128) #### Parameters @@ -76,7 +76,7 @@ Defined in: [TableController.ts:149](https://github.com/TanStack/table/blob/main host: ReactiveControllerHost; ``` -Defined in: [TableController.ts:142](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L142) +Defined in: [packages/lit-table/src/TableController.ts:121](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L121) ## Methods @@ -86,7 +86,7 @@ Defined in: [TableController.ts:142](https://github.com/TanStack/table/blob/main hostConnected(): void; ``` -Defined in: [TableController.ts:250](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L250) +Defined in: [packages/lit-table/src/TableController.ts:210](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L210) Called when the host is connected to the component tree. For custom element hosts, this corresponds to the `connectedCallback()` lifecycle, @@ -110,7 +110,7 @@ ReactiveController.hostConnected hostDisconnected(): void; ``` -Defined in: [TableController.ts:254](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L254) +Defined in: [packages/lit-table/src/TableController.ts:214](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L214) Called when the host is disconnected from the component tree. For custom element hosts, this corresponds to the `disconnectedCallback()` lifecycle, @@ -135,7 +135,7 @@ ReactiveController.hostDisconnected table(tableOptions, selector?): LitTable; ``` -Defined in: [TableController.ts:169](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L169) +Defined in: [packages/lit-table/src/TableController.ts:148](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L148) Returns the Lit-backed table instance for the current render pass. diff --git a/docs/framework/lit/reference/functions/FlexRender-1.md b/docs/framework/lit/reference/functions/FlexRender-1.md index 77a681d381..26fe947fd8 100644 --- a/docs/framework/lit/reference/functions/FlexRender-1.md +++ b/docs/framework/lit/reference/functions/FlexRender-1.md @@ -9,7 +9,7 @@ title: FlexRender function FlexRender(props): string | TemplateResult | null; ``` -Defined in: [flexRender.ts:90](https://github.com/TanStack/table/blob/main/packages/lit-table/src/flexRender.ts#L90) +Defined in: [packages/lit-table/src/flexRender.ts:90](https://github.com/TanStack/table/blob/main/packages/lit-table/src/flexRender.ts#L90) Simplified component wrapper of `flexRender`. Use this utility function to render headers, cells, or footers with custom markup. Only one prop (`cell`, `header`, or `footer`) may be passed. diff --git a/docs/framework/lit/reference/functions/createTableHook.md b/docs/framework/lit/reference/functions/createTableHook.md index 167d29c70a..6167c70940 100644 --- a/docs/framework/lit/reference/functions/createTableHook.md +++ b/docs/framework/lit/reference/functions/createTableHook.md @@ -9,7 +9,7 @@ title: createTableHook function createTableHook(__namedParameters): CreateTableHookResult; ``` -Defined in: [createTableHook.ts:503](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L503) +Defined in: [packages/lit-table/src/createTableHook.ts:503](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L503) Creates a custom table hook with pre-bound components for composition. diff --git a/docs/framework/lit/reference/functions/flexRender.md b/docs/framework/lit/reference/functions/flexRender.md index 335f6180d3..cf133cdc28 100644 --- a/docs/framework/lit/reference/functions/flexRender.md +++ b/docs/framework/lit/reference/functions/flexRender.md @@ -9,7 +9,7 @@ title: flexRender function flexRender(Comp, props): string | TemplateResult | null; ``` -Defined in: [flexRender.ts:22](https://github.com/TanStack/table/blob/main/packages/lit-table/src/flexRender.ts#L22) +Defined in: [packages/lit-table/src/flexRender.ts:22](https://github.com/TanStack/table/blob/main/packages/lit-table/src/flexRender.ts#L22) Renders a Lit table template value with the provided context props. diff --git a/docs/framework/lit/reference/index.md b/docs/framework/lit/reference/index.md index edd43a16b2..e0cabbd6ce 100644 --- a/docs/framework/lit/reference/index.md +++ b/docs/framework/lit/reference/index.md @@ -7,6 +7,7 @@ title: "@tanstack/lit-table" ## Classes +- [SubscribeDirective](classes/SubscribeDirective.md) - [TableController](classes/TableController.md) ## Interfaces @@ -28,7 +29,11 @@ title: "@tanstack/lit-table" - [CreateTableHookOptions](type-aliases/CreateTableHookOptions.md) - [FlexRenderProps](type-aliases/FlexRenderProps.md) - [LitTable](type-aliases/LitTable.md) -- [SubscribeSource](type-aliases/SubscribeSource.md) +- [SelectionSource](type-aliases/SelectionSource.md) + +## Variables + +- [subscribe](variables/subscribe.md) ## Functions diff --git a/docs/framework/lit/reference/interfaces/CreateTableHookResult.md b/docs/framework/lit/reference/interfaces/CreateTableHookResult.md index 2108b77ca9..2c6bc65772 100644 --- a/docs/framework/lit/reference/interfaces/CreateTableHookResult.md +++ b/docs/framework/lit/reference/interfaces/CreateTableHookResult.md @@ -5,7 +5,7 @@ title: CreateTableHookResult # Interface: CreateTableHookResult\ -Defined in: [createTableHook.ts:358](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L358) +Defined in: [packages/lit-table/src/createTableHook.ts:358](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L358) ## Type Parameters @@ -33,7 +33,7 @@ Defined in: [createTableHook.ts:358](https://github.com/TanStack/table/blob/main appFeatures: TFeatures; ``` -Defined in: [createTableHook.ts:365](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L365) +Defined in: [packages/lit-table/src/createTableHook.ts:365](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L365) The features object that was passed to `createTableHook`. @@ -45,7 +45,7 @@ The features object that was passed to `createTableHook`. createAppColumnHelper: () => AppColumnHelper; ``` -Defined in: [createTableHook.ts:370](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L370) +Defined in: [packages/lit-table/src/createTableHook.ts:370](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L370) A column helper pre-bound to `TFeatures` and the registered components, so the cell/header/footer render props expose the bound components. @@ -68,7 +68,7 @@ the cell/header/footer render props expose the bound components. useAppTable: (host, tableOptions, selector?) => object; ``` -Defined in: [createTableHook.ts:381](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L381) +Defined in: [packages/lit-table/src/createTableHook.ts:381](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L381) Creates a controller-like object whose `table()` method returns a table with the `App*` wrapper functions, a bound `FlexRender`, and the registered @@ -120,7 +120,7 @@ table: () => AppLitTable(host) => ContextConsumer>, ReactiveControllerHost & HTMLElement>; ``` -Defined in: [createTableHook.ts:410](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L410) +Defined in: [packages/lit-table/src/createTableHook.ts:410](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L410) Reads the cell instance from a `@lit/context` `ContextConsumer`. lit never provides an extended cell through context, so this is a BARE `Cell`. @@ -149,7 +149,7 @@ provides an extended cell through context, so this is a BARE `Cell`. useHeaderContext: (host) => ContextConsumer>, ReactiveControllerHost & HTMLElement>; ``` -Defined in: [createTableHook.ts:420](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L420) +Defined in: [packages/lit-table/src/createTableHook.ts:420](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L420) Reads the header instance from a `@lit/context` `ContextConsumer`. lit never provides an extended header through context, so this is a BARE `Header`. @@ -178,7 +178,7 @@ provides an extended header through context, so this is a BARE `Header`. useTableContext: (host) => ContextConsumer>, ReactiveControllerHost & HTMLElement>; ``` -Defined in: [createTableHook.ts:400](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L400) +Defined in: [packages/lit-table/src/createTableHook.ts:400](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L400) Reads the table provided by the nearest ancestor that called `useAppTable`, via a `@lit/context` `ContextConsumer`. This is the BARE `LitTable` written diff --git a/docs/framework/lit/reference/type-aliases/AppCellContext.md b/docs/framework/lit/reference/type-aliases/AppCellContext.md index e5687913fc..7e013ef7fb 100644 --- a/docs/framework/lit/reference/type-aliases/AppCellContext.md +++ b/docs/framework/lit/reference/type-aliases/AppCellContext.md @@ -9,7 +9,7 @@ title: AppCellContext type AppCellContext = object; ``` -Defined in: [createTableHook.ts:48](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L48) +Defined in: [packages/lit-table/src/createTableHook.ts:48](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L48) Enhanced CellContext with pre-bound cell components. The `cell` property includes the registered cellComponents. @@ -40,7 +40,7 @@ The `cell` property includes the registered cellComponents. cell: Cell & BoundComponents & object; ``` -Defined in: [createTableHook.ts:54](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L54) +Defined in: [packages/lit-table/src/createTableHook.ts:54](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L54) #### Type Declaration @@ -62,7 +62,7 @@ FlexRender: () => TemplateResult | string | null; column: Column; ``` -Defined in: [createTableHook.ts:58](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L58) +Defined in: [packages/lit-table/src/createTableHook.ts:58](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L58) *** @@ -72,7 +72,7 @@ Defined in: [createTableHook.ts:58](https://github.com/TanStack/table/blob/main/ getValue: CellContext["getValue"]; ``` -Defined in: [createTableHook.ts:59](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L59) +Defined in: [packages/lit-table/src/createTableHook.ts:59](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L59) *** @@ -82,7 +82,7 @@ Defined in: [createTableHook.ts:59](https://github.com/TanStack/table/blob/main/ renderValue: CellContext["renderValue"]; ``` -Defined in: [createTableHook.ts:60](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L60) +Defined in: [packages/lit-table/src/createTableHook.ts:60](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L60) *** @@ -92,7 +92,7 @@ Defined in: [createTableHook.ts:60](https://github.com/TanStack/table/blob/main/ row: Row; ``` -Defined in: [createTableHook.ts:61](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L61) +Defined in: [packages/lit-table/src/createTableHook.ts:61](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L61) *** @@ -102,4 +102,4 @@ Defined in: [createTableHook.ts:61](https://github.com/TanStack/table/blob/main/ table: Table; ``` -Defined in: [createTableHook.ts:62](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L62) +Defined in: [packages/lit-table/src/createTableHook.ts:62](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L62) diff --git a/docs/framework/lit/reference/type-aliases/AppColumnDefBase.md b/docs/framework/lit/reference/type-aliases/AppColumnDefBase.md index a63ca58943..bfc76c591c 100644 --- a/docs/framework/lit/reference/type-aliases/AppColumnDefBase.md +++ b/docs/framework/lit/reference/type-aliases/AppColumnDefBase.md @@ -9,7 +9,7 @@ title: AppColumnDefBase type AppColumnDefBase = Omit, "cell" | "header" | "footer"> & object; ``` -Defined in: [createTableHook.ts:97](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L97) +Defined in: [packages/lit-table/src/createTableHook.ts:97](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L97) Enhanced column definition base with pre-bound components in cell/header/footer contexts. diff --git a/docs/framework/lit/reference/type-aliases/AppColumnDefTemplate.md b/docs/framework/lit/reference/type-aliases/AppColumnDefTemplate.md index da58117d70..10e2a5e356 100644 --- a/docs/framework/lit/reference/type-aliases/AppColumnDefTemplate.md +++ b/docs/framework/lit/reference/type-aliases/AppColumnDefTemplate.md @@ -9,7 +9,7 @@ title: AppColumnDefTemplate type AppColumnDefTemplate = string | (props) => any; ``` -Defined in: [createTableHook.ts:90](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L90) +Defined in: [packages/lit-table/src/createTableHook.ts:90](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L90) Template type for column definitions that can be a string or a function. diff --git a/docs/framework/lit/reference/type-aliases/AppColumnHelper.md b/docs/framework/lit/reference/type-aliases/AppColumnHelper.md index d79489a9d1..267fff63b1 100644 --- a/docs/framework/lit/reference/type-aliases/AppColumnHelper.md +++ b/docs/framework/lit/reference/type-aliases/AppColumnHelper.md @@ -9,7 +9,7 @@ title: AppColumnHelper type AppColumnHelper = object; ``` -Defined in: [createTableHook.ts:173](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L173) +Defined in: [packages/lit-table/src/createTableHook.ts:173](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L173) Enhanced column helper with pre-bound components in cell/header/footer contexts. This enables TypeScript to know about the registered components when defining columns. @@ -40,7 +40,7 @@ This enables TypeScript to know about the registered components when defining co accessor: (accessor, column) => TAccessor extends AccessorFn ? AccessorFnColumnDef : AccessorKeyColumnDef; ``` -Defined in: [createTableHook.ts:183](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L183) +Defined in: [packages/lit-table/src/createTableHook.ts:183](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L183) Creates a data column definition with an accessor key or function. The cell, header, and footer contexts include pre-bound components. @@ -77,7 +77,7 @@ The cell, header, and footer contexts include pre-bound components. columns: (columns) => ColumnDef[] & [...TColumns]; ``` -Defined in: [createTableHook.ts:214](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L214) +Defined in: [packages/lit-table/src/createTableHook.ts:214](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L214) Wraps an array of column definitions to preserve each column's individual TValue type. @@ -105,7 +105,7 @@ Wraps an array of column definitions to preserve each column's individual TValue display: (column) => DisplayColumnDef; ``` -Defined in: [createTableHook.ts:222](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L222) +Defined in: [packages/lit-table/src/createTableHook.ts:222](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L222) Creates a display column definition for non-data columns. The cell, header, and footer contexts include pre-bound components. @@ -128,7 +128,7 @@ The cell, header, and footer contexts include pre-bound components. group: (column) => GroupColumnDef; ``` -Defined in: [createTableHook.ts:235](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L235) +Defined in: [packages/lit-table/src/createTableHook.ts:235](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L235) Creates a group column definition with nested child columns. The cell, header, and footer contexts include pre-bound components. diff --git a/docs/framework/lit/reference/type-aliases/AppDisplayColumnDef.md b/docs/framework/lit/reference/type-aliases/AppDisplayColumnDef.md index a43611d4f8..7f80e00a3a 100644 --- a/docs/framework/lit/reference/type-aliases/AppDisplayColumnDef.md +++ b/docs/framework/lit/reference/type-aliases/AppDisplayColumnDef.md @@ -9,7 +9,7 @@ title: AppDisplayColumnDef type AppDisplayColumnDef = Omit, "cell" | "header" | "footer"> & object; ``` -Defined in: [createTableHook.ts:121](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L121) +Defined in: [packages/lit-table/src/createTableHook.ts:121](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L121) Enhanced display column definition with pre-bound components. diff --git a/docs/framework/lit/reference/type-aliases/AppGroupColumnDef.md b/docs/framework/lit/reference/type-aliases/AppGroupColumnDef.md index 3c0d519106..69c0e75c2e 100644 --- a/docs/framework/lit/reference/type-aliases/AppGroupColumnDef.md +++ b/docs/framework/lit/reference/type-aliases/AppGroupColumnDef.md @@ -9,7 +9,7 @@ title: AppGroupColumnDef type AppGroupColumnDef = Omit, "cell" | "header" | "footer" | "columns"> & object; ``` -Defined in: [createTableHook.ts:144](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L144) +Defined in: [packages/lit-table/src/createTableHook.ts:144](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L144) Enhanced group column definition with pre-bound components. diff --git a/docs/framework/lit/reference/type-aliases/AppHeaderContext.md b/docs/framework/lit/reference/type-aliases/AppHeaderContext.md index dd27eaab94..a2da9caa18 100644 --- a/docs/framework/lit/reference/type-aliases/AppHeaderContext.md +++ b/docs/framework/lit/reference/type-aliases/AppHeaderContext.md @@ -9,7 +9,7 @@ title: AppHeaderContext type AppHeaderContext = object; ``` -Defined in: [createTableHook.ts:69](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L69) +Defined in: [packages/lit-table/src/createTableHook.ts:69](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L69) Enhanced HeaderContext with pre-bound header components. The `header` property includes the registered headerComponents. @@ -40,7 +40,7 @@ The `header` property includes the registered headerComponents. column: Column; ``` -Defined in: [createTableHook.ts:75](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L75) +Defined in: [packages/lit-table/src/createTableHook.ts:75](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L75) *** @@ -50,7 +50,7 @@ Defined in: [createTableHook.ts:75](https://github.com/TanStack/table/blob/main/ header: Header & BoundComponents & object; ``` -Defined in: [createTableHook.ts:76](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L76) +Defined in: [packages/lit-table/src/createTableHook.ts:76](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L76) #### Type Declaration @@ -72,4 +72,4 @@ FlexRender: () => TemplateResult | string | null; table: Table; ``` -Defined in: [createTableHook.ts:80](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L80) +Defined in: [packages/lit-table/src/createTableHook.ts:80](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L80) diff --git a/docs/framework/lit/reference/type-aliases/AppLitTable.md b/docs/framework/lit/reference/type-aliases/AppLitTable.md index 60c9e5007b..2d74a6aa64 100644 --- a/docs/framework/lit/reference/type-aliases/AppLitTable.md +++ b/docs/framework/lit/reference/type-aliases/AppLitTable.md @@ -9,7 +9,7 @@ title: AppLitTable type AppLitTable = LitTable & NoInfer & object; ``` -Defined in: [createTableHook.ts:288](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L288) +Defined in: [packages/lit-table/src/createTableHook.ts:288](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L288) Extended table API returned by useAppTable with all App wrapper functions diff --git a/docs/framework/lit/reference/type-aliases/BoundComponents.md b/docs/framework/lit/reference/type-aliases/BoundComponents.md index 0f73d6ab2e..6e602b3638 100644 --- a/docs/framework/lit/reference/type-aliases/BoundComponents.md +++ b/docs/framework/lit/reference/type-aliases/BoundComponents.md @@ -9,7 +9,7 @@ title: BoundComponents type BoundComponents = { [TKey in keyof TComponents]: () => ReturnType }; ``` -Defined in: [createTableHook.ts:34](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L34) +Defined in: [packages/lit-table/src/createTableHook.ts:34](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L34) ## Type Parameters diff --git a/docs/framework/lit/reference/type-aliases/ComponentType.md b/docs/framework/lit/reference/type-aliases/ComponentType.md index dc7a43145a..37cb672734 100644 --- a/docs/framework/lit/reference/type-aliases/ComponentType.md +++ b/docs/framework/lit/reference/type-aliases/ComponentType.md @@ -9,7 +9,7 @@ title: ComponentType type ComponentType = (props) => any; ``` -Defined in: [createTableHook.ts:32](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L32) +Defined in: [packages/lit-table/src/createTableHook.ts:32](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L32) ## Type Parameters diff --git a/docs/framework/lit/reference/type-aliases/CreateTableHookOptions.md b/docs/framework/lit/reference/type-aliases/CreateTableHookOptions.md index f67b599ad4..cc8073add5 100644 --- a/docs/framework/lit/reference/type-aliases/CreateTableHookOptions.md +++ b/docs/framework/lit/reference/type-aliases/CreateTableHookOptions.md @@ -9,7 +9,7 @@ title: CreateTableHookOptions type CreateTableHookOptions = Omit, "columns" | "data" | "store" | "state" | "initialState"> & object; ``` -Defined in: [createTableHook.ts:253](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L253) +Defined in: [packages/lit-table/src/createTableHook.ts:253](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L253) Options for creating a table hook with pre-bound components and default table options. Extends all TableOptions except 'columns' | 'data' | 'store' | 'state' | 'initialState'. diff --git a/docs/framework/lit/reference/type-aliases/FlexRenderProps.md b/docs/framework/lit/reference/type-aliases/FlexRenderProps.md index 870f7f0699..39711fc8c5 100644 --- a/docs/framework/lit/reference/type-aliases/FlexRenderProps.md +++ b/docs/framework/lit/reference/type-aliases/FlexRenderProps.md @@ -24,7 +24,7 @@ type FlexRenderProps = }; ``` -Defined in: [flexRender.ts:56](https://github.com/TanStack/table/blob/main/packages/lit-table/src/flexRender.ts#L56) +Defined in: [packages/lit-table/src/flexRender.ts:56](https://github.com/TanStack/table/blob/main/packages/lit-table/src/flexRender.ts#L56) Simplified component wrapper of `flexRender`. Use this utility function to render headers, cells, or footers with custom markup. Only one prop (`cell`, `header`, or `footer`) may be passed. diff --git a/docs/framework/lit/reference/type-aliases/LitTable.md b/docs/framework/lit/reference/type-aliases/LitTable.md index efc37cd0d6..6b9f8753c8 100644 --- a/docs/framework/lit/reference/type-aliases/LitTable.md +++ b/docs/framework/lit/reference/type-aliases/LitTable.md @@ -9,7 +9,7 @@ title: LitTable type LitTable = Omit, "store"> & object; ``` -Defined in: [TableController.ts:30](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L30) +Defined in: [packages/lit-table/src/TableController.ts:20](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L20) The extended table type returned by the Lit adapter. Includes a `Subscribe` method for fine-grained state subscriptions @@ -63,129 +63,42 @@ readonly store: Table["store"]; #### Deprecated Prefer `table.state` for render reads, -`table.atoms..get()` for slice snapshots, or `table.Subscribe` for +`table.atoms..get()` for slice snapshots, or `table.subscribe` for explicit subscriptions. `table.store.state` is a current-value snapshot and is easy to misuse in render code. -### Subscribe() +### subscribe ```ts -Subscribe: { - (props): string | TemplateResult; - (props): string | TemplateResult; - (props): string | TemplateResult; -}; +subscribe: typeof subscribe; ``` -Subscribe to a selected slice of table state, or to a single source (atom or store). - -**Lit note:** `TableController` still wires host updates via the full `table.store` -subscription — source mode matches the React API and reads `source.get()` at render -time. True source-only invalidation can be added later via `source.subscribe`. - -#### Call Signature - -```ts -(props): string | TemplateResult; -``` - -##### Type Parameters - -###### TSourceValue - -`TSourceValue` - -##### Parameters - -###### props - -###### children - -(`state`) => `TemplateResult` \| `string` \| `TemplateResult` \| `string` - -###### selector? - -`undefined` - -###### source - -[`SubscribeSource`](SubscribeSource.md)\<`TSourceValue`\> - -##### Returns - -`string` \| `TemplateResult` - -#### Call Signature - -```ts -(props): string | TemplateResult; -``` - -##### Type Parameters - -###### TSourceValue - -`TSourceValue` - -###### TSubscribeSelected - -`TSubscribeSelected` - -##### Parameters - -###### props - -###### children - -(`state`) => `TemplateResult` \| `string` \| `TemplateResult` \| `string` - -###### selector - -(`state`) => `TSubscribeSelected` - -###### source - -[`SubscribeSource`](SubscribeSource.md)\<`TSourceValue`\> - -##### Returns - -`string` \| `TemplateResult` - -#### Call Signature - -```ts -(props): string | TemplateResult; -``` - -##### Type Parameters - -###### TSubscribeSelected - -`TSubscribeSelected` - -##### Parameters - -###### props - -###### children - -(`state`) => `TemplateResult` \| `string` \| `TemplateResult` \| `string` - -###### selector - -(`state`) => `TSubscribeSelected` - -##### Returns - -`string` \| `TemplateResult` +Subscribes to the table's underlying state store within a Lit template. +Re-renders only the targeted template slice when the observed state changes. #### Example ```ts -table.Subscribe({ - selector: (state) => ({ rowSelection: state.rowSelection }), - children: (state) => html`
${JSON.stringify(state)}
`, -}) +// 1. Subscribe to a specific state slice (re-renders ONLY when rowSelection changes) +html` +
+${table.subscribe( +table.store, +(state) => state.rowSelection, +(rowSelection) => html`Selected: ${JSON.stringify(rowSelection)}` +)} +
+` + +// 2. Subscribe to the full state (re-renders on any state mutation) +html` +
+${table.subscribe( +table.store, +(state) => html`Total rows: ${state.rowModel.rows.length}` +)} +
+` ``` ## Type Parameters diff --git a/docs/framework/lit/reference/type-aliases/SelectionSource.md b/docs/framework/lit/reference/type-aliases/SelectionSource.md new file mode 100644 index 0000000000..feffb121cb --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/SelectionSource.md @@ -0,0 +1,22 @@ +--- +id: SelectionSource +title: SelectionSource +--- + +# Type Alias: SelectionSource\ + +```ts +type SelectionSource = + | Atom + | ReadonlyAtom + | Store +| ReadonlyStore; +``` + +Defined in: [packages/lit-table/src/subscribe-directive.ts:13](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L13) + +## Type Parameters + +### TValue + +`TValue` diff --git a/docs/framework/lit/reference/type-aliases/SubscribeSource.md b/docs/framework/lit/reference/type-aliases/SubscribeSource.md deleted file mode 100644 index 8a59c803bc..0000000000 --- a/docs/framework/lit/reference/type-aliases/SubscribeSource.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: SubscribeSource -title: SubscribeSource ---- - -# Type Alias: SubscribeSource\ - -```ts -type SubscribeSource = - | Atom - | ReadonlyAtom - | Store -| ReadonlyStore; -``` - -Defined in: [TableController.ts:19](https://github.com/TanStack/table/blob/main/packages/lit-table/src/TableController.ts#L19) - -## Type Parameters - -### TValue - -`TValue` diff --git a/docs/framework/lit/reference/variables/subscribe.md b/docs/framework/lit/reference/variables/subscribe.md new file mode 100644 index 0000000000..72c84f206b --- /dev/null +++ b/docs/framework/lit/reference/variables/subscribe.md @@ -0,0 +1,96 @@ +--- +id: subscribe +title: subscribe +--- + +# Variable: subscribe() + +```ts +const subscribe: { + (source, template): DirectiveResult; + (source, selector, template): DirectiveResult; +}; +``` + +Defined in: [packages/lit-table/src/subscribe-directive.ts:182](https://github.com/TanStack/table/blob/main/packages/lit-table/src/subscribe-directive.ts#L182) + +A Lit directive that subscribes to a source (Store or Atom) +and efficiently updates only the wrapped template +when the state or selected slice changes. + +## Call Signature + +```ts +(source, template): DirectiveResult; +``` + +Subscribes to the entire source state without filtering. + +### Type Parameters + +#### TSource + +`TSource` + +### Parameters + +#### source + +[`SelectionSource`](../type-aliases/SelectionSource.md)\<`TSource`\> + +#### template + +`TemplateFunction`\<`TSource`\> + +### Returns + +`DirectiveResult`\<*typeof* [`SubscribeDirective`](../classes/SubscribeDirective.md)\> + +## Call Signature + +```ts +( + source, + selector, +template): DirectiveResult; +``` + +Subscribes to a specific slice of the source state via a selector, +preventing unnecessary re-renders when other parts of the state change. + +### Type Parameters + +#### TSource + +`TSource` + +#### TSelected + +`TSelected` + +### Parameters + +#### source + +[`SelectionSource`](../type-aliases/SelectionSource.md)\<`TSource`\> + +#### selector + +`Selector`\<`TSource`, `TSelected`\> + +#### template + +`TemplateFunction`\<`TSelected`\> + +### Returns + +`DirectiveResult`\<*typeof* [`SubscribeDirective`](../classes/SubscribeDirective.md)\> + +## Example + +```ts +// Without a selector (subscribes to entire state) +html`
${subscribe(myStore, (state) => html`${state.count}`)}
` +* // With a selector (only updates when `count` changes) +html`
${subscribe(myStore, state => state.count, (count) => html`${count}`)}
` +``` diff --git a/docs/reference/index/functions/filterFn_arrHas.md b/docs/reference/index/functions/filterFn_arrHas.md deleted file mode 100644 index 5a7a77bc08..0000000000 --- a/docs/reference/index/functions/filterFn_arrHas.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -id: filterFn_arrHas -title: filterFn_arrHas ---- - -# Function: filterFn\_arrHas() - -```ts -function filterFn_arrHas( - row, - columnId, - filterValue): boolean; -``` - -Defined in: [fns/filterFns.ts:299](https://github.com/TanStack/table/blob/main/packages/table-core/src/fns/filterFns.ts#L299) - -Keeps rows whose scalar column value equals at least one filter value. - -## Type Parameters - -### TFeatures - -`TFeatures` *extends* [`TableFeatures`](../interfaces/TableFeatures.md) - -### TData - -`TData` *extends* [`RowData`](../type-aliases/RowData.md) - -## Parameters - -### row - -[`Row`](../type-aliases/Row.md)\<`TFeatures`, `TData`\> - -### columnId - -`string` - -### filterValue - -`unknown`[] - -## Returns - -`boolean` diff --git a/docs/reference/index/index.md b/docs/reference/index/index.md index e11fd158ca..14cb25957c 100644 --- a/docs/reference/index/index.md +++ b/docs/reference/index/index.md @@ -277,6 +277,7 @@ title: index - [coreRowModelsFeature](variables/coreRowModelsFeature.md) - [coreRowsFeature](variables/coreRowsFeature.md) - [coreTablesFeature](variables/coreTablesFeature.md) +- [filterFn\_arrHas](variables/filterFn_arrHas.md) - [filterFn\_arrIncludes](variables/filterFn_arrIncludes.md) - [filterFn\_arrIncludesAll](variables/filterFn_arrIncludesAll.md) - [filterFn\_arrIncludesSome](variables/filterFn_arrIncludesSome.md) @@ -334,7 +335,6 @@ title: index - [createPaginatedRowModel](functions/createPaginatedRowModel.md) - [createSortedRowModel](functions/createSortedRowModel.md) - [expandRows](functions/expandRows.md) -- [filterFn\_arrHas](functions/filterFn_arrHas.md) - [flattenBy](functions/flattenBy.md) - [functionalUpdate](functions/functionalUpdate.md) - [getFunctionNameInfo](functions/getFunctionNameInfo.md) diff --git a/docs/reference/index/variables/filterFn_arrHas.md b/docs/reference/index/variables/filterFn_arrHas.md new file mode 100644 index 0000000000..8b1a832d3c --- /dev/null +++ b/docs/reference/index/variables/filterFn_arrHas.md @@ -0,0 +1,32 @@ +--- +id: filterFn_arrHas +title: filterFn_arrHas +--- + +# Variable: filterFn\_arrHas + +```ts +const filterFn_arrHas: (row, columnId, filterValue) => boolean & object; +``` + +Defined in: [fns/filterFns.ts:299](https://github.com/TanStack/table/blob/main/packages/table-core/src/fns/filterFns.ts#L299) + +Keeps rows whose scalar column value equals at least one filter value. + +## Type Declaration + +### autoRemove() + +```ts +autoRemove: (val) => boolean; +``` + +#### Parameters + +##### val + +`any` + +#### Returns + +`boolean` diff --git a/docs/reference/index/variables/filterFns.md b/docs/reference/index/variables/filterFns.md index 3ff640c0b6..e3a70e2565 100644 --- a/docs/reference/index/variables/filterFns.md +++ b/docs/reference/index/variables/filterFns.md @@ -17,39 +17,27 @@ Pass this object to filtered row model creation or extend it with custom filter ## Type Declaration -### arrHas() +### arrHas ```ts -arrHas: (row, columnId, filterValue) => boolean = filterFn_arrHas; +arrHas: (row, columnId, filterValue) => boolean & object = filterFn_arrHas; ``` -Keeps rows whose scalar column value equals at least one filter value. - -#### Type Parameters - -##### TFeatures - -`TFeatures` *extends* [`TableFeatures`](../interfaces/TableFeatures.md) - -##### TData - -`TData` *extends* [`RowData`](../type-aliases/RowData.md) - -#### Parameters - -##### row +#### Type Declaration -[`Row`](../type-aliases/Row.md)\<`TFeatures`, `TData`\> +##### autoRemove() -##### columnId +```ts +autoRemove: (val) => boolean; +``` -`string` +###### Parameters -##### filterValue +###### val -`unknown`[] +`any` -#### Returns +###### Returns `boolean` diff --git a/examples/angular/basic-app-table/package.json b/examples/angular/basic-app-table/package.json index 64c4f8dc1b..ed1a3ba21e 100644 --- a/examples/angular/basic-app-table/package.json +++ b/examples/angular/basic-app-table/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/basic-external-atoms/package.json b/examples/angular/basic-external-atoms/package.json index 760609b04f..e279f7d4d1 100644 --- a/examples/angular/basic-external-atoms/package.json +++ b/examples/angular/basic-external-atoms/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/basic-external-state/package.json b/examples/angular/basic-external-state/package.json index 244ada2add..503bf5125d 100644 --- a/examples/angular/basic-external-state/package.json +++ b/examples/angular/basic-external-state/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/basic-inject-table/package.json b/examples/angular/basic-inject-table/package.json index f27ba2229c..bb3b8a6c40 100644 --- a/examples/angular/basic-inject-table/package.json +++ b/examples/angular/basic-inject-table/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/column-groups/package.json b/examples/angular/column-groups/package.json index 9a58092f6d..4749100ee6 100644 --- a/examples/angular/column-groups/package.json +++ b/examples/angular/column-groups/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/column-ordering/package.json b/examples/angular/column-ordering/package.json index e373c854a0..666e5aea9d 100644 --- a/examples/angular/column-ordering/package.json +++ b/examples/angular/column-ordering/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/column-pinning-split/package.json b/examples/angular/column-pinning-split/package.json index 5d9f0dfe26..fd1c99e0d2 100644 --- a/examples/angular/column-pinning-split/package.json +++ b/examples/angular/column-pinning-split/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/column-pinning-sticky/package.json b/examples/angular/column-pinning-sticky/package.json index e36c94b58e..6f5bfc4e4a 100644 --- a/examples/angular/column-pinning-sticky/package.json +++ b/examples/angular/column-pinning-sticky/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/column-pinning/package.json b/examples/angular/column-pinning/package.json index 4731b33fa7..63635df71b 100644 --- a/examples/angular/column-pinning/package.json +++ b/examples/angular/column-pinning/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/column-resizing-performant/package.json b/examples/angular/column-resizing-performant/package.json index 549ba392e8..e9e2fad583 100644 --- a/examples/angular/column-resizing-performant/package.json +++ b/examples/angular/column-resizing-performant/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/column-resizing/package.json b/examples/angular/column-resizing/package.json index d93e3cdb43..a03b3017ac 100644 --- a/examples/angular/column-resizing/package.json +++ b/examples/angular/column-resizing/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/column-sizing/package.json b/examples/angular/column-sizing/package.json index be4bfd39c9..8ec204558e 100644 --- a/examples/angular/column-sizing/package.json +++ b/examples/angular/column-sizing/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/column-visibility/package.json b/examples/angular/column-visibility/package.json index 7656fea50d..4753b652c8 100644 --- a/examples/angular/column-visibility/package.json +++ b/examples/angular/column-visibility/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/custom-plugin/package.json b/examples/angular/custom-plugin/package.json index 3a24b0f658..69f999db10 100644 --- a/examples/angular/custom-plugin/package.json +++ b/examples/angular/custom-plugin/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/editable/package.json b/examples/angular/editable/package.json index 209e278da5..87de95b52d 100644 --- a/examples/angular/editable/package.json +++ b/examples/angular/editable/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/expanding/package.json b/examples/angular/expanding/package.json index a2dced0c1e..756042e6d8 100644 --- a/examples/angular/expanding/package.json +++ b/examples/angular/expanding/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/filters-faceted/package.json b/examples/angular/filters-faceted/package.json index 87e9fe58a0..9591f68075 100644 --- a/examples/angular/filters-faceted/package.json +++ b/examples/angular/filters-faceted/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/filters-fuzzy/package.json b/examples/angular/filters-fuzzy/package.json index 811859dc7f..320ee0fe77 100644 --- a/examples/angular/filters-fuzzy/package.json +++ b/examples/angular/filters-fuzzy/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/filters/package.json b/examples/angular/filters/package.json index fb94c22428..da3b7f3812 100644 --- a/examples/angular/filters/package.json +++ b/examples/angular/filters/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/grouping/package.json b/examples/angular/grouping/package.json index f59034b4a1..b0a9848a26 100644 --- a/examples/angular/grouping/package.json +++ b/examples/angular/grouping/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/kitchen-sink/package.json b/examples/angular/kitchen-sink/package.json index 793e2cc951..acb0d47e6e 100644 --- a/examples/angular/kitchen-sink/package.json +++ b/examples/angular/kitchen-sink/package.json @@ -6,6 +6,7 @@ "dev": "ng serve --port 5173", "build": "ng build", "lint": "eslint ./src", + "test:types": "ng build", "watch": "ng build --watch --configuration development" }, "private": true, diff --git a/examples/angular/pagination/package.json b/examples/angular/pagination/package.json index 71b021ebf9..56c14467e2 100644 --- a/examples/angular/pagination/package.json +++ b/examples/angular/pagination/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/remote-data/package.json b/examples/angular/remote-data/package.json index 622a9e5135..c13be48623 100644 --- a/examples/angular/remote-data/package.json +++ b/examples/angular/remote-data/package.json @@ -9,6 +9,7 @@ "watch": "ng build --watch --configuration development", "test": "ng test", "lint": "eslint ./src", + "test:types": "ng build", "serve:ssr:remote-data": "node dist/remote-data/server/server.mjs" }, "private": true, diff --git a/examples/angular/row-dnd/package.json b/examples/angular/row-dnd/package.json index 60d4231a50..ca9e3a4f79 100644 --- a/examples/angular/row-dnd/package.json +++ b/examples/angular/row-dnd/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/row-pinning/package.json b/examples/angular/row-pinning/package.json index 503e6013a4..d28329f082 100644 --- a/examples/angular/row-pinning/package.json +++ b/examples/angular/row-pinning/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/row-selection-signal/package.json b/examples/angular/row-selection-signal/package.json index cee88e5a40..d240406b6c 100644 --- a/examples/angular/row-selection-signal/package.json +++ b/examples/angular/row-selection-signal/package.json @@ -7,7 +7,8 @@ "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/row-selection/package.json b/examples/angular/row-selection/package.json index d1c6d4ec82..434980c20f 100644 --- a/examples/angular/row-selection/package.json +++ b/examples/angular/row-selection/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/signal-input/package.json b/examples/angular/signal-input/package.json index efeae7032c..5375946d42 100644 --- a/examples/angular/signal-input/package.json +++ b/examples/angular/signal-input/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/sorting/package.json b/examples/angular/sorting/package.json index d489bba869..7f39ee2378 100644 --- a/examples/angular/sorting/package.json +++ b/examples/angular/sorting/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/sub-components/package.json b/examples/angular/sub-components/package.json index 8e3873aebd..d1ad8d3a9d 100644 --- a/examples/angular/sub-components/package.json +++ b/examples/angular/sub-components/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/virtualized-columns/package.json b/examples/angular/virtualized-columns/package.json index 9ace2b8f89..295e6eb93d 100644 --- a/examples/angular/virtualized-columns/package.json +++ b/examples/angular/virtualized-columns/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/virtualized-infinite-scrolling/package.json b/examples/angular/virtualized-infinite-scrolling/package.json index 7cf140aac4..7ee3dd75ae 100644 --- a/examples/angular/virtualized-infinite-scrolling/package.json +++ b/examples/angular/virtualized-infinite-scrolling/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/virtualized-rows/package.json b/examples/angular/virtualized-rows/package.json index 2cdc3ce72f..8cd48e4b23 100644 --- a/examples/angular/virtualized-rows/package.json +++ b/examples/angular/virtualized-rows/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/with-tanstack-form/package.json b/examples/angular/with-tanstack-form/package.json index 769230dc55..c74c3c397b 100644 --- a/examples/angular/with-tanstack-form/package.json +++ b/examples/angular/with-tanstack-form/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/angular/with-tanstack-query/package.json b/examples/angular/with-tanstack-query/package.json index f22b5080ef..9db47ba0da 100644 --- a/examples/angular/with-tanstack-query/package.json +++ b/examples/angular/with-tanstack-query/package.json @@ -6,7 +6,8 @@ "dev": "ng serve --port 5173", "build": "ng build", "watch": "ng build --watch --configuration development", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "ng build" }, "private": true, "packageManager": "pnpm@11.9.0", diff --git a/examples/lit/basic-app-table/package.json b/examples/lit/basic-app-table/package.json index 8eea95bd7a..dfb79d887a 100644 --- a/examples/lit/basic-app-table/package.json +++ b/examples/lit/basic-app-table/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/basic-external-atoms/package.json b/examples/lit/basic-external-atoms/package.json index e18b08d1b7..a92243d003 100644 --- a/examples/lit/basic-external-atoms/package.json +++ b/examples/lit/basic-external-atoms/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/basic-external-state/package.json b/examples/lit/basic-external-state/package.json index 4e823a9be8..083ed2fe89 100644 --- a/examples/lit/basic-external-state/package.json +++ b/examples/lit/basic-external-state/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/basic-subscribe/README.md b/examples/lit/basic-subscribe/README.md new file mode 100644 index 0000000000..f7dbd88811 --- /dev/null +++ b/examples/lit/basic-subscribe/README.md @@ -0,0 +1,34 @@ +# Example: Basic Subscribe + +This example demonstrates fine-grained state subscriptions using `table.subscribe()` for optimized rendering in Lit tables. + +**Key Features:** + +- **Fine-grained subscriptions**: Using `table.subscribe()` to subscribe only to the state each component needs +- **TableController**: Lit ReactiveController for managing table instances and subscriptions +- **External atoms**: State management via `createAtom` from `@tanstack/store` for independent control +- **Performance optimized**: Only UI elements re-render when their subscribed state changes +- **Complete feature set**: Row selection, column filtering, global filtering, sorting, and pagination + +**What this shows:** + +1. **Table body** - Re-renders only when filtering or pagination state changes +2. **Pagination controls** - Re-render only when pagination state changes (page index/size) +3. **Row selection** - Includes per-row checkboxes and page-level select-all +4. **Selection summary** - Re-renders only when row selection changes +5. **Sortable headers** - Click headers to sort data +6. **Data regeneration** - Buttons to test with 1,000 and 200,000 rows +7. **Table state debug view** - Full table state displayed as JSON for debugging + +This pattern is ideal for large tables where you want to minimize unnecessary re-renders by having precise control over which components subscribe to which state slices. + +## Running the Example + +To run this example: + +```bash +npm install +npm run start +``` + +The example will start a dev server with hot module reloading. The table loads with 1,000 rows by default and can be stress-tested with 200,000 rows. diff --git a/examples/lit/basic-subscribe/index.html b/examples/lit/basic-subscribe/index.html new file mode 100644 index 0000000000..dbb4aedf26 --- /dev/null +++ b/examples/lit/basic-subscribe/index.html @@ -0,0 +1,13 @@ + + + + + + TanStack Lit Table - Basic Subscribe + + +
+ + + + diff --git a/examples/lit/basic-subscribe/package.json b/examples/lit/basic-subscribe/package.json new file mode 100644 index 0000000000..03df322006 --- /dev/null +++ b/examples/lit/basic-subscribe/package.json @@ -0,0 +1,23 @@ +{ + "name": "tanstack-lit-table-example-basic-subscribe", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "start": "vite", + "lint": "eslint ./src", + "test:types": "tsc --noEmit" + }, + "dependencies": { + "@faker-js/faker": "^10.5.0", + "@tanstack/lit-store": "^0.14.0", + "@tanstack/lit-table": "^9.0.0-beta.23", + "lit": "^3.3.3" + }, + "devDependencies": { + "@rollup/plugin-replace": "^6.0.3", + "typescript": "6.0.3", + "vite": "^8.1.0" + } +} diff --git a/examples/lit/basic-subscribe/src/main.ts b/examples/lit/basic-subscribe/src/main.ts new file mode 100644 index 0000000000..c44c03229b --- /dev/null +++ b/examples/lit/basic-subscribe/src/main.ts @@ -0,0 +1,540 @@ +import { customElement, state } from 'lit/decorators.js' +import { LitElement, html } from 'lit' +import { repeat } from 'lit/directives/repeat.js' +import { + FlexRender, + TableController, + columnFilteringFeature, + createColumnHelper, + createFilteredRowModel, + createPaginatedRowModel, + filterFns, + globalFilteringFeature, + rowPaginationFeature, + rowSelectionFeature, + subscribe, + tableFeatures, +} from '@tanstack/lit-table' +import { createAtom } from '@tanstack/lit-store' +import { makeData } from './makeData' +import type { HeaderContext, RowSelectionState } from '@tanstack/lit-table' +import type { Person } from './makeData' + +/** + * This example mirrors the React `basic-subscribe` example: it shows fine-grained + * re-rendering with `table.subscribe`. Each part of the UI subscribes only to the + * slice of state it needs, so toggling one row, typing in a filter, or paging only + * re-renders the affected region instead of the whole table. + * + * Reach for these patterns only when you hit a real performance issue. + */ + +const features = tableFeatures({ + rowPaginationFeature, + rowSelectionFeature, + columnFilteringFeature, + globalFilteringFeature, + // Row models must be registered as part of the features so the table actually + // filters and paginates `table.getRowModel()`. + filteredRowModel: createFilteredRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filterFns, +}) + +const columnHelper = createColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.display({ + id: 'select', + // Select-all checkbox re-renders only when filtering or row selection changes. + header: ({ table }) => + subscribe( + table.store, + (state) => ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + rowSelection: state.rowSelection, + }), + () => html` + + `, + ), + // Each row's checkbox subscribes only to its own selection value, so toggling + // one row re-renders just that checkbox. + cell: ({ row, table }) => + subscribe( + table.atoms.rowSelection, + (rowSelection) => rowSelection[row.id], + (isRowSelected) => html` + + `, + ), + }), + columnHelper.accessor('firstName', { + header: 'First Name', + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => html`Last Name`, + cell: (info) => info.getValue(), + }), + columnHelper.accessor('age', { + header: () => 'Age', + }), + columnHelper.accessor('visits', { + header: () => html`Visits`, + }), + columnHelper.accessor('status', { + header: 'Status', + }), + columnHelper.accessor('progress', { + header: 'Profile Progress', + }), +]) + +// Raise just the row selection slice to an external atom for full control over it, +// matching the React example. Other slices stay internal to the table. +const rowSelectionAtom = createAtom({}) + +@customElement('lit-table-example') +class LitTableExample extends LitElement { + @state() + private _data: Array = makeData(1_000) + + private tableController = new TableController(this) + + private table = this.tableController.table( + { + features, + columns, + data: this._data, + getRowId: (row) => row.id, + enableRowSelection: true, + atoms: { + rowSelection: rowSelectionAtom, + }, + debugTable: true, + }, + () => null, // subscribe to no table state by default; use table.subscribe below + ) + + // Stable selector references so the directive can skip re-running when the host + // re-renders with the same source + selector. + private getBodyState = (state: ReturnType) => ({ + columnFilters: state.columnFilters, + globalFilter: state.globalFilter, + pagination: state.pagination, + }) + + protected updated(changedProperties: Map) { + if (changedProperties.has('_data')) { + this.table.setOptions((prev) => ({ ...prev, data: this._data })) + } + } + + private renderColumnFilter( + context: HeaderContext, + ) { + const { column, table } = context + if (!column.getCanFilter()) return null + + const firstValue = table + .getPreFilteredRowModel() + .flatRows[0]?.getValue(column.id) + + // Re-render the filter inputs only when the column filters change. + return subscribe(table.atoms.columnFilters, () => + typeof firstValue === 'number' + ? html` +
+ { + const value = (e.currentTarget as HTMLInputElement).value + column.setFilterValue((old: [unknown, unknown]) => [ + value, + old?.[1], + ]) + }} + placeholder="Min" + class="filter-input" + /> + { + const value = (e.currentTarget as HTMLInputElement).value + column.setFilterValue((old: [unknown, unknown]) => [ + old?.[0], + value, + ]) + }} + placeholder="Max" + class="filter-input" + /> +
+ ` + : html` + { + column.setFilterValue( + (e.currentTarget as HTMLInputElement).value, + ) + }} + placeholder="Search..." + class="filter-input" + /> + `, + ) + } + + protected render() { + return html` +
+
+ + +
+ + +
+ ${this.table.subscribe( + this.table.store, + (state) => state.globalFilter, + (globalFilter) => html` + + this.table.setGlobalFilter( + (e.currentTarget as HTMLInputElement).value, + )} + class="summary-panel" + placeholder="Search all columns..." + /> + `, + )} +
+ +
+ + + + ${repeat( + this.table.getHeaderGroups(), + (hg) => hg.id, + (headerGroup) => html` + + ${repeat( + headerGroup.headers, + (h) => h.id, + (header) => html` + + `, + )} + + `, + )} + + + + ${this.table.subscribe( + this.table.store, + this.getBodyState, + () => html` + + ${repeat( + this.table.getRowModel().rows, + (row) => row.id, + (row) => html` + + ${repeat( + row.getAllCells(), + (cell) => cell.id, + (cell) => html` `, + )} + + `, + )} + + + + + + + + `, + )} +
+ ${header.isPlaceholder + ? null + : html` +
${FlexRender({ header })}
+ ${header.column.getCanFilter() + ? this.renderColumnFilter(header.getContext()) + : null} + `} +
${FlexRender({ cell })}
+ ${this.table.subscribe( + rowSelectionAtom, + () => html` + + `, + )} + + Page Rows + (${this.table.getRowModel().rows.length.toLocaleString()}) +
+ +
+ + + ${this.table.subscribe( + this.table.store, + (state) => state.pagination, + (pagination) => html` +
+ + + + + +
Page
+ + ${(pagination.pageIndex + 1).toLocaleString()} of + ${this.table.getPageCount().toLocaleString()} + +
+ + | Go to page: + { + const target = e.currentTarget as HTMLInputElement + const page = target.value ? Number(target.value) - 1 : 0 + this.table.setPageIndex(page) + }} + class="page-size-input" + /> + + +
+ `, + )} + +
+ + + ${this.table.subscribe( + rowSelectionAtom, + (rowSelection) => html` +
+ ${Object.keys(rowSelection).length.toLocaleString()} of + ${this.table + .getPreFilteredRowModel() + .rows.length.toLocaleString()} + Total Rows Selected +
+ `, + )} + +
+
+ + + + ${this.table.subscribe( + this.table.store, + (state) => state, + (state) => html`
${JSON.stringify(state, null, 2)}
`, + )} +
+ + + ` + } +} diff --git a/examples/lit/basic-subscribe/src/makeData.ts b/examples/lit/basic-subscribe/src/makeData.ts new file mode 100644 index 0000000000..6b96907d32 --- /dev/null +++ b/examples/lit/basic-subscribe/src/makeData.ts @@ -0,0 +1,43 @@ +import { faker } from '@faker-js/faker' + +export type Person = { + id: string + firstName: string + lastName: string + age: number + visits: number + progress: number + status: 'relationship' | 'complicated' | 'single' +} + +const range = (len: number) => { + const arr: Array = [] + for (let i = 0; i < len; i++) { + arr.push(i) + } + return arr +} + +const newPerson = (): Person => { + return { + id: faker.string.uuid(), + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + age: faker.number.int(40), + visits: faker.number.int(1000), + progress: faker.number.int(100), + status: faker.helpers.shuffle([ + 'relationship', + 'complicated', + 'single', + ])[0], + } +} + +export function makeData(len: number) { + return range(len).map( + (): Person => ({ + ...newPerson(), + }), + ) +} diff --git a/examples/lit/basic-subscribe/tsconfig.json b/examples/lit/basic-subscribe/tsconfig.json new file mode 100644 index 0000000000..02eb5e4c87 --- /dev/null +++ b/examples/lit/basic-subscribe/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "emitDecoratorMetadata": true, + "noEmit": true, + "jsx": "react-jsx", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowJs": true + }, + "include": ["src", "vite.config.js", "vite.config.ts"] +} diff --git a/examples/lit/basic-subscribe/vite.config.js b/examples/lit/basic-subscribe/vite.config.js new file mode 100644 index 0000000000..75dafbd933 --- /dev/null +++ b/examples/lit/basic-subscribe/vite.config.js @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite' +import rollupReplace from '@rollup/plugin-replace' + +export default defineConfig({ + plugins: [ + rollupReplace({ + preventAssignment: true, + values: { + __DEV__: JSON.stringify(true), + 'process.env.NODE_ENV': JSON.stringify('development'), + }, + }), + ], +}) diff --git a/examples/lit/basic-table-controller/package.json b/examples/lit/basic-table-controller/package.json index 9ee8bdf954..4e28ffda07 100644 --- a/examples/lit/basic-table-controller/package.json +++ b/examples/lit/basic-table-controller/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-groups/package.json b/examples/lit/column-groups/package.json index 2d85ca983b..5495a124da 100644 --- a/examples/lit/column-groups/package.json +++ b/examples/lit/column-groups/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-ordering/package.json b/examples/lit/column-ordering/package.json index 48c4ce83b0..7da3aa4432 100644 --- a/examples/lit/column-ordering/package.json +++ b/examples/lit/column-ordering/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-pinning-split/package.json b/examples/lit/column-pinning-split/package.json index de84a767d5..8a47c881e4 100644 --- a/examples/lit/column-pinning-split/package.json +++ b/examples/lit/column-pinning-split/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-pinning-sticky/package.json b/examples/lit/column-pinning-sticky/package.json index a59e4a2968..c805d1eefc 100644 --- a/examples/lit/column-pinning-sticky/package.json +++ b/examples/lit/column-pinning-sticky/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-pinning/package.json b/examples/lit/column-pinning/package.json index 12655784f1..0c2ca71ea6 100644 --- a/examples/lit/column-pinning/package.json +++ b/examples/lit/column-pinning/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-resizing-performant/package.json b/examples/lit/column-resizing-performant/package.json index ad370fe6c3..115cbbeab3 100644 --- a/examples/lit/column-resizing-performant/package.json +++ b/examples/lit/column-resizing-performant/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-resizing/package.json b/examples/lit/column-resizing/package.json index d38098b1df..29e2186a93 100644 --- a/examples/lit/column-resizing/package.json +++ b/examples/lit/column-resizing/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-sizing/package.json b/examples/lit/column-sizing/package.json index e27c6a90ea..245ff6453e 100644 --- a/examples/lit/column-sizing/package.json +++ b/examples/lit/column-sizing/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/column-visibility/package.json b/examples/lit/column-visibility/package.json index 6b6cdadbf7..4d878f8071 100644 --- a/examples/lit/column-visibility/package.json +++ b/examples/lit/column-visibility/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/expanding/package.json b/examples/lit/expanding/package.json index 6cd93762a0..301ac57c85 100644 --- a/examples/lit/expanding/package.json +++ b/examples/lit/expanding/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/filters-faceted/package.json b/examples/lit/filters-faceted/package.json index d1dadc1502..a5c638da7b 100644 --- a/examples/lit/filters-faceted/package.json +++ b/examples/lit/filters-faceted/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/filters-fuzzy/package.json b/examples/lit/filters-fuzzy/package.json index 4b837246f6..5ce7e5cbb5 100644 --- a/examples/lit/filters-fuzzy/package.json +++ b/examples/lit/filters-fuzzy/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/filters/package.json b/examples/lit/filters/package.json index 327ed19617..ac1bfb1b3b 100644 --- a/examples/lit/filters/package.json +++ b/examples/lit/filters/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/grouping/package.json b/examples/lit/grouping/package.json index 7adf4e2f5e..510b40566b 100644 --- a/examples/lit/grouping/package.json +++ b/examples/lit/grouping/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/kitchen-sink/package.json b/examples/lit/kitchen-sink/package.json index 4a09e499ed..2e6a20a1eb 100644 --- a/examples/lit/kitchen-sink/package.json +++ b/examples/lit/kitchen-sink/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/pagination/package.json b/examples/lit/pagination/package.json index 523d015ce5..d867dad9ac 100644 --- a/examples/lit/pagination/package.json +++ b/examples/lit/pagination/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/row-pinning/package.json b/examples/lit/row-pinning/package.json index a1dced2817..8711dfb41f 100644 --- a/examples/lit/row-pinning/package.json +++ b/examples/lit/row-pinning/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/row-selection/package.json b/examples/lit/row-selection/package.json index a96a4b3429..13e4e3a423 100644 --- a/examples/lit/row-selection/package.json +++ b/examples/lit/row-selection/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/sorting-dynamic-data/package.json b/examples/lit/sorting-dynamic-data/package.json index 610e93cd16..6750258b86 100644 --- a/examples/lit/sorting-dynamic-data/package.json +++ b/examples/lit/sorting-dynamic-data/package.json @@ -5,7 +5,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "start": "vite" + "start": "vite", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/sorting/package.json b/examples/lit/sorting/package.json index 6351512ee3..3ad2052399 100644 --- a/examples/lit/sorting/package.json +++ b/examples/lit/sorting/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/sub-components/package.json b/examples/lit/sub-components/package.json index 7c6311b10c..111dd077ac 100644 --- a/examples/lit/sub-components/package.json +++ b/examples/lit/sub-components/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/virtualized-columns/package.json b/examples/lit/virtualized-columns/package.json index 21330bf1ce..6389e9daba 100644 --- a/examples/lit/virtualized-columns/package.json +++ b/examples/lit/virtualized-columns/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/virtualized-infinite-scrolling/package.json b/examples/lit/virtualized-infinite-scrolling/package.json index 1bd3ebd164..3f02202663 100644 --- a/examples/lit/virtualized-infinite-scrolling/package.json +++ b/examples/lit/virtualized-infinite-scrolling/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/lit/virtualized-rows/package.json b/examples/lit/virtualized-rows/package.json index aa81373476..8ad5c496f9 100644 --- a/examples/lit/virtualized-rows/package.json +++ b/examples/lit/virtualized-rows/package.json @@ -6,7 +6,8 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/preact/composable-tables/src/main.tsx b/examples/preact/composable-tables/src/main.tsx index ceb33f7a2d..4436723e0b 100644 --- a/examples/preact/composable-tables/src/main.tsx +++ b/examples/preact/composable-tables/src/main.tsx @@ -102,144 +102,132 @@ function UsersTable() { useTanStackTableDevtools(table) return ( - // Main selector on AppTable - selects all needed state in one place - ({ - // subscribe to specific states for re-rendering if you are optimizing for maximum performance - pagination: state.pagination, - sorting: state.sorting, - columnFilters: state.columnFilters, - })} - > - {({ sorting, columnFilters }) => ( -
- {/* Table toolbar using pre-bound component */} - + +
+ {/* Table toolbar using pre-bound component */} + - {/* Scroll container with a fixed max-height so larger page sizes + {/* Scroll container with a fixed max-height so larger page sizes scroll. If the App wrappers remount on state updates, the scroll position jumps back to the top on every keystroke/selection. */} -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((h) => ( - - {(header) => ( - + ))} + +
- {header.isPlaceholder ? null : ( +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((h) => ( + + {(header) => ( + + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getAllCells().map((c) => ( + + {(cell) => ( + + )} + + ))} + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((f) => ( + + {(footer) => { + const columnId = footer.column.id + const hasFilter = table.state.columnFilters.some( + (cf) => cf.id === columnId, + ) + + return ( + - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getAllCells().map((c) => ( - - {(cell) => ( - - )} - - ))} - - ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((f) => ( - - {(footer) => { - const columnId = footer.column.id - const hasFilter = columnFilters.some( - (cf) => cf.id === columnId, - ) - - return ( - - ) - }} - - ))} - - ))} - -
+ {header.isPlaceholder ? null : ( + <> + + + + {/* Show sort order number when multiple columns sorted */} + {table.state.sorting.length > 1 && + table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) > -1 && ( + + {table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) + 1} + + )} + + )} +
+ {/* Cell components are pre-bound via AppCell */} + +
+ {footer.isPlaceholder ? null : ( <> - - - - {/* Show sort order number when multiple columns sorted */} - {sorting.length > 1 && - sorting.findIndex( - (s) => s.id === header.column.id, - ) > -1 && ( - - {sorting.findIndex( - (s) => s.id === header.column.id, - ) + 1} - - )} + {/* Use FooterSum for numeric columns, FooterColumnId for others */} + {columnId === 'age' || + columnId === 'visits' || + columnId === 'progress' ? ( + <> + + {hasFilter && ( + + {' '} + (filtered) + + )} + + ) : columnId === 'actions' ? null : ( + <> + + {hasFilter && ( + + {' '} + ✓ + + )} + + )} )} - - )} - - ))} -
- {/* Cell components are pre-bound via AppCell */} -
- {footer.isPlaceholder ? null : ( - <> - {/* Use FooterSum for numeric columns, FooterColumnId for others */} - {columnId === 'age' || - columnId === 'visits' || - columnId === 'progress' ? ( - <> - - {hasFilter && ( - - {' '} - (filtered) - - )} - - ) : columnId === 'actions' ? null : ( - <> - - {hasFilter && ( - - {' '} - ✓ - - )} - - )} - - )} -
-
+ ) + }} + + ))} +
+
- {/* Pagination using pre-bound component */} - + {/* Pagination using pre-bound component */} + - {/* Row count using pre-bound component */} - -
- )} + {/* Row count using pre-bound component */} + +
) } @@ -319,141 +307,131 @@ function ProductsTable() { useTanStackTableDevtools(table) return ( - ({ - pagination: state.pagination, - sorting: state.sorting, - columnFilters: state.columnFilters, - })} - > - {({ sorting, columnFilters }) => ( -
- {/* Table toolbar using the same pre-bound component */} - + +
+ {/* Table toolbar using the same pre-bound component */} + - {/* Scroll container with a fixed max-height so larger page sizes + {/* Scroll container with a fixed max-height so larger page sizes scroll. If the App wrappers remount on state updates, the scroll position jumps back to the top on every keystroke/selection. */} -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((h) => ( - - {(header) => ( - + ))} + +
- {header.isPlaceholder ? null : ( +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((h) => ( + + {(header) => ( + + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getAllCells().map((c) => ( + + {(cell) => ( + + )} + + ))} + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((f) => ( + + {(footer) => { + const columnId = footer.column.id + const hasFilter = table.state.columnFilters.some( + (cf) => cf.id === columnId, + ) + + return ( + - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getAllCells().map((c) => ( - - {(cell) => ( - - )} - - ))} - - ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((f) => ( - - {(footer) => { - const columnId = footer.column.id - const hasFilter = columnFilters.some( - (cf) => cf.id === columnId, - ) - - return ( - - ) - }} - - ))} - - ))} - -
+ {header.isPlaceholder ? null : ( + <> + + + + {table.state.sorting.length > 1 && + table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) > -1 && ( + + {table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) + 1} + + )} + + )} +
+ {/* Cell components are pre-bound via AppCell */} + +
+ {footer.isPlaceholder ? null : ( <> - - - - {sorting.length > 1 && - sorting.findIndex( - (s) => s.id === header.column.id, - ) > -1 && ( - - {sorting.findIndex( - (s) => s.id === header.column.id, - ) + 1} - - )} + {/* Use FooterSum for numeric columns, FooterColumnId for others */} + {columnId === 'price' || + columnId === 'stock' || + columnId === 'rating' ? ( + <> + + {hasFilter && ( + + {' '} + (filtered) + + )} + + ) : ( + <> + + {hasFilter && ( + + {' '} + ✓ + + )} + + )} )} - - )} - - ))} -
- {/* Cell components are pre-bound via AppCell */} -
- {footer.isPlaceholder ? null : ( - <> - {/* Use FooterSum for numeric columns, FooterColumnId for others */} - {columnId === 'price' || - columnId === 'stock' || - columnId === 'rating' ? ( - <> - - {hasFilter && ( - - {' '} - (filtered) - - )} - - ) : ( - <> - - {hasFilter && ( - - {' '} - ✓ - - )} - - )} - - )} -
-
+ ) + }} + + ))} +
+
- {/* Pagination using the same pre-bound component */} - + {/* Pagination using the same pre-bound component */} + - {/* Row count using the same pre-bound component */} - -
- )} + {/* Row count using the same pre-bound component */} + +
) } diff --git a/examples/react/composable-tables/src/main.tsx b/examples/react/composable-tables/src/main.tsx index 082dd6cdf1..ffede25615 100644 --- a/examples/react/composable-tables/src/main.tsx +++ b/examples/react/composable-tables/src/main.tsx @@ -109,140 +109,128 @@ function UsersTable() { useTanStackTableDevtools(table) return ( - // Main selector on AppTable - selects all needed state in one place - ({ - // subscribe to specific states for re-rendering if you are optimizing for maximum performance - pagination: state.pagination, - sorting: state.sorting, - columnFilters: state.columnFilters, - })} - > - {({ sorting, columnFilters }) => ( -
- {/* Table toolbar using pre-bound component */} - -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((h) => ( - - {(header) => ( - + ))} + +
- {header.isPlaceholder ? null : ( + +
+ {/* Table toolbar using pre-bound component */} + +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((h) => ( + + {(header) => ( + + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getAllCells().map((c) => ( + + {(cell) => ( + + )} + + ))} + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((f) => ( + + {(footer) => { + const columnId = footer.column.id + const hasFilter = table.state.columnFilters.some( + (cf) => cf.id === columnId, + ) + + return ( + - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getAllCells().map((c) => ( - - {(cell) => ( - - )} - - ))} - - ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((f) => ( - - {(footer) => { - const columnId = footer.column.id - const hasFilter = columnFilters.some( - (cf) => cf.id === columnId, - ) - - return ( - - ) - }} - - ))} - - ))} - -
+ {header.isPlaceholder ? null : ( + <> + + + + {/* Show sort order number when multiple columns sorted */} + {table.state.sorting.length > 1 && + table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) > -1 && ( + + {table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) + 1} + + )} + + )} +
+ {/* Cell components are pre-bound via AppCell */} + +
+ {footer.isPlaceholder ? null : ( <> - - - - {/* Show sort order number when multiple columns sorted */} - {sorting.length > 1 && - sorting.findIndex( - (s) => s.id === header.column.id, - ) > -1 && ( - - {sorting.findIndex( - (s) => s.id === header.column.id, - ) + 1} - - )} + {/* Use FooterSum for numeric columns, FooterColumnId for others */} + {columnId === 'age' || + columnId === 'visits' || + columnId === 'progress' ? ( + <> + + {hasFilter && ( + + {' '} + (filtered) + + )} + + ) : columnId === 'actions' ? null : ( + <> + + {hasFilter && ( + + {' '} + ✓ + + )} + + )} )} - - )} - - ))} -
- {/* Cell components are pre-bound via AppCell */} -
- {footer.isPlaceholder ? null : ( - <> - {/* Use FooterSum for numeric columns, FooterColumnId for others */} - {columnId === 'age' || - columnId === 'visits' || - columnId === 'progress' ? ( - <> - - {hasFilter && ( - - {' '} - (filtered) - - )} - - ) : columnId === 'actions' ? null : ( - <> - - {hasFilter && ( - - {' '} - ✓ - - )} - - )} - - )} -
-
+ ) + }} + + ))} +
+
- {/* Pagination using pre-bound component */} - + {/* Pagination using pre-bound component */} + - {/* Row count using pre-bound component */} - -
- )} + {/* Row count using pre-bound component */} + +
) } @@ -327,137 +315,127 @@ function ProductsTable() { useTanStackTableDevtools(table) return ( - ({ - pagination: state.pagination, - sorting: state.sorting, - columnFilters: state.columnFilters, - })} - > - {({ sorting, columnFilters }) => ( -
- {/* Table toolbar using the same pre-bound component */} - -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((h) => ( - - {(header) => ( - + ))} + +
- {header.isPlaceholder ? null : ( + +
+ {/* Table toolbar using the same pre-bound component */} + +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((h) => ( + + {(header) => ( + + )} + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getAllCells().map((c) => ( + + {(cell) => ( + + )} + + ))} + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((f) => ( + + {(footer) => { + const columnId = footer.column.id + const hasFilter = table.state.columnFilters.some( + (cf) => cf.id === columnId, + ) + + return ( + - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getAllCells().map((c) => ( - - {(cell) => ( - - )} - - ))} - - ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((f) => ( - - {(footer) => { - const columnId = footer.column.id - const hasFilter = columnFilters.some( - (cf) => cf.id === columnId, - ) - - return ( - - ) - }} - - ))} - - ))} - -
+ {header.isPlaceholder ? null : ( + <> + + + + {table.state.sorting.length > 1 && + table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) > -1 && ( + + {table.state.sorting.findIndex( + (s) => s.id === header.column.id, + ) + 1} + + )} + + )} +
+ {/* Cell components are pre-bound via AppCell */} + +
+ {footer.isPlaceholder ? null : ( <> - - - - {sorting.length > 1 && - sorting.findIndex( - (s) => s.id === header.column.id, - ) > -1 && ( - - {sorting.findIndex( - (s) => s.id === header.column.id, - ) + 1} - - )} + {/* Use FooterSum for numeric columns, FooterColumnId for others */} + {columnId === 'price' || + columnId === 'stock' || + columnId === 'rating' ? ( + <> + + {hasFilter && ( + + {' '} + (filtered) + + )} + + ) : ( + <> + + {hasFilter && ( + + {' '} + ✓ + + )} + + )} )} - - )} - - ))} -
- {/* Cell components are pre-bound via AppCell */} -
- {footer.isPlaceholder ? null : ( - <> - {/* Use FooterSum for numeric columns, FooterColumnId for others */} - {columnId === 'price' || - columnId === 'stock' || - columnId === 'rating' ? ( - <> - - {hasFilter && ( - - {' '} - (filtered) - - )} - - ) : ( - <> - - {hasFilter && ( - - {' '} - ✓ - - )} - - )} - - )} -
-
+ ) + }} + + ))} +
+
- {/* Pagination using the same pre-bound component */} - + {/* Pagination using the same pre-bound component */} + - {/* Row count using the same pre-bound component */} - -
- )} + {/* Row count using the same pre-bound component */} + +
) } diff --git a/examples/solid/basic-app-table/package.json b/examples/solid/basic-app-table/package.json index 9b626df406..dc4147071b 100644 --- a/examples/solid/basic-app-table/package.json +++ b/examples/solid/basic-app-table/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/basic-app-table/src/makeData.ts b/examples/solid/basic-app-table/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/basic-app-table/src/makeData.ts +++ b/examples/solid/basic-app-table/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/basic-app-table/src/vite-env.d.ts b/examples/solid/basic-app-table/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/basic-app-table/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/basic-app-table/vite.config.ts b/examples/solid/basic-app-table/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/basic-app-table/vite.config.ts +++ b/examples/solid/basic-app-table/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/basic-external-atoms/package.json b/examples/solid/basic-external-atoms/package.json index 901695809f..eb1cd5b2a5 100644 --- a/examples/solid/basic-external-atoms/package.json +++ b/examples/solid/basic-external-atoms/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/basic-external-atoms/src/vite-env.d.ts b/examples/solid/basic-external-atoms/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/basic-external-atoms/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/basic-external-atoms/vite.config.ts b/examples/solid/basic-external-atoms/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/basic-external-atoms/vite.config.ts +++ b/examples/solid/basic-external-atoms/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/basic-external-state/package.json b/examples/solid/basic-external-state/package.json index 7407d744ef..009700b248 100644 --- a/examples/solid/basic-external-state/package.json +++ b/examples/solid/basic-external-state/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/basic-external-state/src/vite-env.d.ts b/examples/solid/basic-external-state/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/basic-external-state/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/basic-external-state/vite.config.ts b/examples/solid/basic-external-state/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/basic-external-state/vite.config.ts +++ b/examples/solid/basic-external-state/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/basic-use-table/package.json b/examples/solid/basic-use-table/package.json index 3357c345bc..cc42a66e84 100644 --- a/examples/solid/basic-use-table/package.json +++ b/examples/solid/basic-use-table/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/basic-use-table/src/vite-env.d.ts b/examples/solid/basic-use-table/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/basic-use-table/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/basic-use-table/vite.config.ts b/examples/solid/basic-use-table/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/basic-use-table/vite.config.ts +++ b/examples/solid/basic-use-table/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/column-groups/package.json b/examples/solid/column-groups/package.json index 7b2cf2d157..db6efefc98 100644 --- a/examples/solid/column-groups/package.json +++ b/examples/solid/column-groups/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-groups/src/makeData.ts b/examples/solid/column-groups/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/column-groups/src/makeData.ts +++ b/examples/solid/column-groups/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/column-groups/src/vite-env.d.ts b/examples/solid/column-groups/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-groups/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-groups/vite.config.ts b/examples/solid/column-groups/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/column-groups/vite.config.ts +++ b/examples/solid/column-groups/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/column-ordering/package.json b/examples/solid/column-ordering/package.json index 9baf4aa3eb..51ef26463c 100644 --- a/examples/solid/column-ordering/package.json +++ b/examples/solid/column-ordering/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-ordering/src/makeData.ts b/examples/solid/column-ordering/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/column-ordering/src/makeData.ts +++ b/examples/solid/column-ordering/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/column-ordering/src/vite-env.d.ts b/examples/solid/column-ordering/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-ordering/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-ordering/vite.config.ts b/examples/solid/column-ordering/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/column-ordering/vite.config.ts +++ b/examples/solid/column-ordering/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/column-pinning-split/package.json b/examples/solid/column-pinning-split/package.json index 14e1ec4b90..bb98336438 100644 --- a/examples/solid/column-pinning-split/package.json +++ b/examples/solid/column-pinning-split/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-pinning-split/src/vite-env.d.ts b/examples/solid/column-pinning-split/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-pinning-split/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-pinning-sticky/package.json b/examples/solid/column-pinning-sticky/package.json index c3b64b5259..0170d0d353 100644 --- a/examples/solid/column-pinning-sticky/package.json +++ b/examples/solid/column-pinning-sticky/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-pinning-sticky/src/App.tsx b/examples/solid/column-pinning-sticky/src/App.tsx index 52ea0c4925..4ae234a4d8 100644 --- a/examples/solid/column-pinning-sticky/src/App.tsx +++ b/examples/solid/column-pinning-sticky/src/App.tsx @@ -98,20 +98,17 @@ function App() { const refreshData = () => setData(makeData(20)) const stressTest = () => setData(makeData(1_000)) - const table = createTable( - { - features, - columns: defaultColumns, - get data() { - return data() - }, - debugTable: true, - debugHeaders: true, - debugColumns: true, - columnResizeMode: 'onChange', + const table = createTable({ + features, + columns: defaultColumns, + get data() { + return data() }, - (state) => state, - ) + debugTable: true, + debugHeaders: true, + debugColumns: true, + columnResizeMode: 'onChange', + }) const randomizeColumns = () => { table.setColumnOrder( diff --git a/examples/solid/column-pinning-sticky/src/vite-env.d.ts b/examples/solid/column-pinning-sticky/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-pinning-sticky/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-pinning/package.json b/examples/solid/column-pinning/package.json index fa7fa7c20e..ebe897b9c0 100644 --- a/examples/solid/column-pinning/package.json +++ b/examples/solid/column-pinning/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-pinning/src/vite-env.d.ts b/examples/solid/column-pinning/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-pinning/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-resizing-performant/package.json b/examples/solid/column-resizing-performant/package.json index 3609d3e191..6659ab82bd 100644 --- a/examples/solid/column-resizing-performant/package.json +++ b/examples/solid/column-resizing-performant/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-resizing-performant/src/App.tsx b/examples/solid/column-resizing-performant/src/App.tsx index 90964f169a..03c36adcc7 100644 --- a/examples/solid/column-resizing-performant/src/App.tsx +++ b/examples/solid/column-resizing-performant/src/App.tsx @@ -60,24 +60,18 @@ function App() { const refreshData = () => setData(makeData(200)) const stressTest = () => setData(makeData(2_000)) - const table = createTable( - { - features, - columns, - get data() { - return data() - }, - defaultColumn: { minSize: 60, maxSize: 800 }, - columnResizeMode: 'onChange', - debugTable: true, - debugHeaders: true, - debugColumns: true, + const table = createTable({ + features, + columns, + get data() { + return data() }, - (state) => ({ - columnSizing: state.columnSizing, - columnResizing: state.columnResizing, - }), - ) + defaultColumn: { minSize: 60, maxSize: 800 }, + columnResizeMode: 'onChange', + debugTable: true, + debugHeaders: true, + debugColumns: true, + }) const columnSizeVars = createMemo(() => { const headers = table.getFlatHeaders() diff --git a/examples/solid/column-resizing-performant/src/vite-env.d.ts b/examples/solid/column-resizing-performant/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-resizing-performant/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-resizing/package.json b/examples/solid/column-resizing/package.json index 8fa756344e..c26bfb8b58 100644 --- a/examples/solid/column-resizing/package.json +++ b/examples/solid/column-resizing/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-resizing/src/makeData.ts b/examples/solid/column-resizing/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/column-resizing/src/makeData.ts +++ b/examples/solid/column-resizing/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/column-resizing/src/vite-env.d.ts b/examples/solid/column-resizing/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-resizing/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-sizing/package.json b/examples/solid/column-sizing/package.json index 15b2a5f69a..87bf8ef611 100644 --- a/examples/solid/column-sizing/package.json +++ b/examples/solid/column-sizing/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-sizing/src/makeData.ts b/examples/solid/column-sizing/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/column-sizing/src/makeData.ts +++ b/examples/solid/column-sizing/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/column-sizing/src/vite-env.d.ts b/examples/solid/column-sizing/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-sizing/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-visibility/package.json b/examples/solid/column-visibility/package.json index f2cf4853c4..80c0897811 100644 --- a/examples/solid/column-visibility/package.json +++ b/examples/solid/column-visibility/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/column-visibility/src/makeData.ts b/examples/solid/column-visibility/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/column-visibility/src/makeData.ts +++ b/examples/solid/column-visibility/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/column-visibility/src/vite-env.d.ts b/examples/solid/column-visibility/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/column-visibility/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/column-visibility/vite.config.ts b/examples/solid/column-visibility/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/column-visibility/vite.config.ts +++ b/examples/solid/column-visibility/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/expanding/package.json b/examples/solid/expanding/package.json index 2bcd578452..a87c879a37 100644 --- a/examples/solid/expanding/package.json +++ b/examples/solid/expanding/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/expanding/src/vite-env.d.ts b/examples/solid/expanding/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/expanding/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/filters-faceted/package.json b/examples/solid/filters-faceted/package.json index dc1b833c52..6e65e4c6af 100644 --- a/examples/solid/filters-faceted/package.json +++ b/examples/solid/filters-faceted/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/filters-faceted/src/makeData.ts b/examples/solid/filters-faceted/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/filters-faceted/src/makeData.ts +++ b/examples/solid/filters-faceted/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/filters-faceted/src/vite-env.d.ts b/examples/solid/filters-faceted/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/filters-faceted/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/filters-faceted/vite.config.ts b/examples/solid/filters-faceted/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/filters-faceted/vite.config.ts +++ b/examples/solid/filters-faceted/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/filters-fuzzy/package.json b/examples/solid/filters-fuzzy/package.json index 0dc0603608..d1c2ac0d2a 100644 --- a/examples/solid/filters-fuzzy/package.json +++ b/examples/solid/filters-fuzzy/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/filters-fuzzy/src/vite-env.d.ts b/examples/solid/filters-fuzzy/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/filters-fuzzy/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/filters/package.json b/examples/solid/filters/package.json index a882c94f81..cad08dfc0e 100644 --- a/examples/solid/filters/package.json +++ b/examples/solid/filters/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/filters/src/makeData.ts b/examples/solid/filters/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/filters/src/makeData.ts +++ b/examples/solid/filters/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/filters/src/vite-env.d.ts b/examples/solid/filters/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/filters/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/filters/vite.config.ts b/examples/solid/filters/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/filters/vite.config.ts +++ b/examples/solid/filters/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/grouping/package.json b/examples/solid/grouping/package.json index 91704dbc9c..4d24b00b8c 100644 --- a/examples/solid/grouping/package.json +++ b/examples/solid/grouping/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/grouping/src/vite-env.d.ts b/examples/solid/grouping/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/grouping/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/kitchen-sink/package.json b/examples/solid/kitchen-sink/package.json index 1bf92f61e0..1a84644ca4 100644 --- a/examples/solid/kitchen-sink/package.json +++ b/examples/solid/kitchen-sink/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/kitchen-sink/src/App.tsx b/examples/solid/kitchen-sink/src/App.tsx index ba619c8ac6..6dc03aa5ad 100644 --- a/examples/solid/kitchen-sink/src/App.tsx +++ b/examples/solid/kitchen-sink/src/App.tsx @@ -24,6 +24,7 @@ import { createMemo, createSignal, onCleanup, + splitProps, } from 'solid-js' import { makeData } from './makeData' import type { JSX } from 'solid-js' @@ -146,6 +147,7 @@ function DebouncedInput( } & Omit, 'onChange' | 'value'>, ) { const [value, setValue] = createSignal(props.value) + const [, rest] = splitProps(props, ['value', 'onChange', 'debounce']) createEffect(() => { setValue(props.value) @@ -162,7 +164,7 @@ function DebouncedInput( return ( setValue(e.currentTarget.value)} /> diff --git a/examples/solid/kitchen-sink/src/vite-env.d.ts b/examples/solid/kitchen-sink/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/kitchen-sink/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/kitchen-sink/vite.config.ts b/examples/solid/kitchen-sink/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/kitchen-sink/vite.config.ts +++ b/examples/solid/kitchen-sink/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/pagination/package.json b/examples/solid/pagination/package.json index c2ab79f69b..2b8203a581 100644 --- a/examples/solid/pagination/package.json +++ b/examples/solid/pagination/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/pagination/src/vite-env.d.ts b/examples/solid/pagination/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/pagination/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/row-pinning/package.json b/examples/solid/row-pinning/package.json index bf3103e23d..62fb4c5ee5 100644 --- a/examples/solid/row-pinning/package.json +++ b/examples/solid/row-pinning/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/row-pinning/src/vite-env.d.ts b/examples/solid/row-pinning/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/row-pinning/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/row-selection/package.json b/examples/solid/row-selection/package.json index 8a889312fe..e6038291df 100644 --- a/examples/solid/row-selection/package.json +++ b/examples/solid/row-selection/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/row-selection/src/vite-env.d.ts b/examples/solid/row-selection/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/row-selection/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/row-selection/vite.config.ts b/examples/solid/row-selection/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/row-selection/vite.config.ts +++ b/examples/solid/row-selection/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/sorting/package.json b/examples/solid/sorting/package.json index ca7bcbdb6e..259a6d0f07 100644 --- a/examples/solid/sorting/package.json +++ b/examples/solid/sorting/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/sorting/src/makeData.ts b/examples/solid/sorting/src/makeData.ts index b9fb014aba..6311127267 100644 --- a/examples/solid/sorting/src/makeData.ts +++ b/examples/solid/sorting/src/makeData.ts @@ -36,7 +36,7 @@ const newPerson = (): Person => { export function makeData(...lens: Array) { const makeDataLevel = (depth = 0): Array => { const len = lens[depth] - return range(len).map((d): Person => { + return range(len).map((): Person => { return { ...newPerson(), subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined, diff --git a/examples/solid/sorting/src/vite-env.d.ts b/examples/solid/sorting/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/sorting/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/sorting/vite.config.ts b/examples/solid/sorting/vite.config.ts index d27427972d..b80a99b402 100644 --- a/examples/solid/sorting/vite.config.ts +++ b/examples/solid/sorting/vite.config.ts @@ -5,6 +5,5 @@ export default defineConfig({ plugins: [solidPlugin()], build: { target: 'esnext', - polyfillDynamicImport: false, }, }) diff --git a/examples/solid/sub-components/package.json b/examples/solid/sub-components/package.json index 5ff12b5e41..89118f5857 100644 --- a/examples/solid/sub-components/package.json +++ b/examples/solid/sub-components/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/sub-components/src/vite-env.d.ts b/examples/solid/sub-components/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/sub-components/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/virtualized-columns/package.json b/examples/solid/virtualized-columns/package.json index 73c7a446b2..d4812e1704 100644 --- a/examples/solid/virtualized-columns/package.json +++ b/examples/solid/virtualized-columns/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/virtualized-columns/src/vite-env.d.ts b/examples/solid/virtualized-columns/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/virtualized-columns/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/virtualized-infinite-scrolling/package.json b/examples/solid/virtualized-infinite-scrolling/package.json index 08c9a19726..49ba74201e 100644 --- a/examples/solid/virtualized-infinite-scrolling/package.json +++ b/examples/solid/virtualized-infinite-scrolling/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/virtualized-infinite-scrolling/src/App.tsx b/examples/solid/virtualized-infinite-scrolling/src/App.tsx index c15767b519..94077a0f54 100644 --- a/examples/solid/virtualized-infinite-scrolling/src/App.tsx +++ b/examples/solid/virtualized-infinite-scrolling/src/App.tsx @@ -15,7 +15,6 @@ import { For, Show, createMemo, onMount } from 'solid-js' import { fetchData } from './makeData' import type { Person, PersonApiResponse } from './makeData' import type { SortingState } from '@tanstack/solid-table' -import type { Virtualizer } from '@tanstack/solid-virtual' const fetchSize = 50 diff --git a/examples/solid/virtualized-infinite-scrolling/src/vite-env.d.ts b/examples/solid/virtualized-infinite-scrolling/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/virtualized-infinite-scrolling/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/virtualized-rows/package.json b/examples/solid/virtualized-rows/package.json index d4738c77b7..934a27a779 100644 --- a/examples/solid/virtualized-rows/package.json +++ b/examples/solid/virtualized-rows/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/virtualized-rows/src/App.tsx b/examples/solid/virtualized-rows/src/App.tsx index b05ab67d24..90d1b09c73 100644 --- a/examples/solid/virtualized-rows/src/App.tsx +++ b/examples/solid/virtualized-rows/src/App.tsx @@ -62,7 +62,7 @@ function App() { { accessorKey: 'createdAt', header: 'Created At', - cell: (info: any) => info.getValue().toLocaleString(), + cell: (info: any) => (info.getValue() as Date).toLocaleString(), size: 250, }, ] diff --git a/examples/solid/virtualized-rows/src/vite-env.d.ts b/examples/solid/virtualized-rows/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/virtualized-rows/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/with-tanstack-form/package.json b/examples/solid/with-tanstack-form/package.json index 602839db13..5b383c92e7 100644 --- a/examples/solid/with-tanstack-form/package.json +++ b/examples/solid/with-tanstack-form/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/with-tanstack-form/src/vite-env.d.ts b/examples/solid/with-tanstack-form/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/with-tanstack-form/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/with-tanstack-query/package.json b/examples/solid/with-tanstack-query/package.json index f0521283e5..a67941128d 100644 --- a/examples/solid/with-tanstack-query/package.json +++ b/examples/solid/with-tanstack-query/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/with-tanstack-query/src/vite-env.d.ts b/examples/solid/with-tanstack-query/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/solid/with-tanstack-query/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid/with-tanstack-router/package.json b/examples/solid/with-tanstack-router/package.json index c7fedb54b5..6a1a4a83f2 100644 --- a/examples/solid/with-tanstack-router/package.json +++ b/examples/solid/with-tanstack-router/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "license": "MIT", "devDependencies": { diff --git a/examples/solid/with-tanstack-router/src/components/table.tsx b/examples/solid/with-tanstack-router/src/components/table.tsx index 7819669a24..2727d8b60c 100644 --- a/examples/solid/with-tanstack-router/src/components/table.tsx +++ b/examples/solid/with-tanstack-router/src/components/table.tsx @@ -51,39 +51,33 @@ type Props> = { export default function Table>( props: Props, ) { - const table = createTable( - { - debugTable: true, - features, - get columns() { - return props.columns - }, - get data() { - return props.data - }, - manualFiltering: true, - manualPagination: true, - manualSorting: true, - get onSortingChange() { - return props.onSortingChange - }, - get onPaginationChange() { - return props.paginationOptions.onPaginationChange - }, - get rowCount() { - return props.paginationOptions.rowCount - }, + const table = createTable({ + debugTable: true, + features, + get columns() { + return props.columns }, - (state) => state, - ) + get data() { + return props.data + }, + manualFiltering: true, + manualPagination: true, + manualSorting: true, + get onSortingChange() { + return props.onSortingChange + }, + get onPaginationChange() { + return props.paginationOptions.onPaginationChange + }, + get rowCount() { + return props.paginationOptions.rowCount + }, + }) // Sync controlled state with table store createEffect(() => { - table.baseStore.setState((prev) => ({ - ...prev, - pagination: props.pagination, - sorting: props.sorting, - })) + table.baseAtoms.pagination.set(props.pagination) + table.baseAtoms.sorting.set(props.sorting) }) return ( diff --git a/examples/solid/with-tanstack-router/src/hooks/useFilters.ts b/examples/solid/with-tanstack-router/src/hooks/useFilters.ts index a8f8121035..c2cf891715 100644 --- a/examples/solid/with-tanstack-router/src/hooks/useFilters.ts +++ b/examples/solid/with-tanstack-router/src/hooks/useFilters.ts @@ -19,7 +19,7 @@ export function useFilters>( navigate({ search: () => ({}), replace: true, - } as Parameters[0]) + } as unknown as Parameters[0]) return { filters, setFilters, resetFilters } } diff --git a/examples/vanilla/basic/package.json b/examples/vanilla/basic/package.json index 93cbfc4034..dffda096b5 100644 --- a/examples/vanilla/basic/package.json +++ b/examples/vanilla/basic/package.json @@ -5,7 +5,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "start": "vite" + "start": "vite", + "test:types": "tsc --noEmit" }, "devDependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/vanilla/basic/src/vite-env.d.ts b/examples/vanilla/basic/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/vanilla/basic/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/vanilla/pagination/package.json b/examples/vanilla/pagination/package.json index 8064428954..6629babcd9 100644 --- a/examples/vanilla/pagination/package.json +++ b/examples/vanilla/pagination/package.json @@ -5,7 +5,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "start": "vite" + "start": "vite", + "test:types": "tsc --noEmit" }, "devDependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/vanilla/pagination/src/vite-env.d.ts b/examples/vanilla/pagination/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/vanilla/pagination/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/vanilla/sorting/package.json b/examples/vanilla/sorting/package.json index 46be898718..172f5b2d40 100644 --- a/examples/vanilla/sorting/package.json +++ b/examples/vanilla/sorting/package.json @@ -5,7 +5,8 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "start": "vite" + "start": "vite", + "test:types": "tsc --noEmit" }, "devDependencies": { "@faker-js/faker": "^10.5.0", diff --git a/examples/vanilla/sorting/src/vite-env.d.ts b/examples/vanilla/sorting/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/vanilla/sorting/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/lit-table/package.json b/packages/lit-table/package.json index fabd550df1..15e037cc72 100644 --- a/packages/lit-table/package.json +++ b/packages/lit-table/package.json @@ -57,7 +57,7 @@ "build": "tsdown" }, "dependencies": { - "@tanstack/store": "^0.11.0", + "@tanstack/lit-store": "^0.14.0", "@tanstack/table-core": "workspace:*" }, "devDependencies": { diff --git a/packages/lit-table/skills/lit/compose-with-tanstack-virtual/SKILL.md b/packages/lit-table/skills/lit/compose-with-tanstack-virtual/SKILL.md index e96a85f953..9d4e385e43 100644 --- a/packages/lit-table/skills/lit/compose-with-tanstack-virtual/SKILL.md +++ b/packages/lit-table/skills/lit/compose-with-tanstack-virtual/SKILL.md @@ -211,10 +211,10 @@ Same shape, but the virtualizer's `count` is `columns.length` (or visible column Always use `table.getRowModel().rows.length` as the count — that's the post-feature row array (sorted, filtered, paginated). The virtualizer should never wrap the raw `data` array. -## With `@tanstack/lit-table`'s `Subscribe` +## With `@tanstack/lit-table`'s `subscribe` -The current Lit adapter wires host invalidation through the full store, so re-renders are already triggered when slices change. Use `table.Subscribe` for render-time projections; don't expect source-mode invalidation savings yet. -Source: `packages/lit-table/src/TableController.ts`. +`subscribe` is an [async Lit directive](https://lit.dev/docs/api/custom-directives/#AsyncDirective) that enables fine-grained reactivity by updating only when the selected part of the store or atom changes. Rather than triggering a full re-render whenever the table state updates, you can use `subscribe` to watch specific slices of state and update only the affected parts of the template. This reduces unnecessary renders and improves performance for large tables or complex UI hierarchies. +Source: `packages/lit-table/src/subscribe-directive.ts`. ## Common Mistakes diff --git a/packages/lit-table/skills/lit/lit-table-controller/SKILL.md b/packages/lit-table/skills/lit/lit-table-controller/SKILL.md index e5c555e716..f7867542bb 100644 --- a/packages/lit-table/skills/lit/lit-table-controller/SKILL.md +++ b/packages/lit-table/skills/lit/lit-table-controller/SKILL.md @@ -18,11 +18,12 @@ sources: - TanStack/table:docs/framework/lit/lit-table.md - TanStack/table:docs/framework/lit/guide/table-state.md - TanStack/table:packages/lit-table/src/TableController.ts + - TanStack/table:packages/lit-table/src/subscribe-directive.ts - TanStack/table:packages/lit-table/src/reactivity.ts - TanStack/table:examples/lit/basic-table-controller/src/main.ts --- -> **Maintainer note:** the Lit adapter is scheduled for a rewrite alongside TanStack Lit Store during the v9 beta cycle. `TableController`'s invalidation model and `Subscribe` mode may change in a future beta. The patterns below match `9.0.0-alpha.48`. +> **Maintainer note:** the Lit adapter now runs on `@tanstack/lit-store`. `TableController` still wires host invalidation to the full `table.store`/`table.optionsStore`; the new `table.subscribe(source, selector?, template)` directive (replacing the old `table.Subscribe` helper) is what narrows re-rendering to a single template region. Further changes may land during the v9 beta cycle. `TableController` is the Lit-specific entry point for `@tanstack/lit-table`. It implements the Lit `ReactiveController` interface, hosts the underlying core `Table` instance, and bridges TanStack Store atom changes to `host.requestUpdate()` calls. This skill explains the lifecycle in detail. @@ -81,7 +82,7 @@ Key points: 1. **One core table per controller.** The first `.table(options)` call constructs it; later calls merge options into the same instance. 2. **Two subscriptions:** `table.store` (state) and `table.optionsStore` (options). Both call `host.requestUpdate()`. 3. **Subscriptions are torn down on `hostDisconnected`** and reset on `hostConnected`. -4. **`Subscribe` is whole-store.** The current adapter does not split host invalidation by source; `table.Subscribe` reads its source at render time, but the host still re-renders on any store change. +4. **The host re-renders on any store change.** `TableController` itself does not split invalidation by source. To narrow re-rendering to a single template region, wrap it in the `table.subscribe(source, selector?, template)` directive, which updates only that region when its selected value's reference changes (identity comparison). See the `lit/table-state` skill, Core Pattern 4. ## Lifecycle Diagram @@ -96,7 +97,7 @@ host.addController(this) this.tableController.table(opts) unsubscribe(st (later calls) table.setOptions(prev => ({ ...prev, ...opts })) │ ▼ - returns { ...table, Subscribe, FlexRender, state } + returns { ...table, subscribe, FlexRender, state } ``` ## Canonical Setup @@ -270,12 +271,12 @@ protected render() { Source: `packages/lit-table/src/TableController.ts`. -### HIGH Forgetting that `Subscribe` re-renders the host on any store change +### HIGH Expecting `table.subscribe` to stop the host from re-rendering -Wrong: assuming `table.Subscribe({ source: table.atoms.rowSelection, … })` makes the host invalidate only on selection changes. +Wrong: assuming `table.subscribe(table.atoms.rowSelection, …)` makes the host invalidate only on selection changes. -Correct: in the current adapter, the host's `requestUpdate()` is wired to the full `table.store` and `table.optionsStore`. `Subscribe` is a render-time projection convenience; it does not narrow host invalidation. Plan accordingly for large lists. -Source: `packages/lit-table/src/TableController.ts` (lines 200–218 + `_setupSubscriptions`). +Correct: the host's `requestUpdate()` is wired to the full `table.store` and `table.optionsStore`, so `render()` still runs on every store change. What `table.subscribe` narrows is the _wrapped template region_: it skips re-rendering that region (and re-running its template function) when its selected slice is unchanged. Wrap expensive regions (large `tbody` lists) in `table.subscribe` with a stable selector to get the benefit. +Source: `packages/lit-table/src/TableController.ts` (`_setupSubscriptions`); `packages/lit-table/src/subscribe-directive.ts`. ### HIGH Building `features` inside `render()` diff --git a/packages/lit-table/skills/lit/table-state/SKILL.md b/packages/lit-table/skills/lit/table-state/SKILL.md index 8f0bacb616..15b3bcd309 100644 --- a/packages/lit-table/skills/lit/table-state/SKILL.md +++ b/packages/lit-table/skills/lit/table-state/SKILL.md @@ -5,7 +5,7 @@ description: > (constructed once per LitElement host, `.table(options, selector?)` called per render), reading state via `table.state` / `table.store` / `table.atoms.`, rendering with `table.FlexRender` / `FlexRender`, fine-grained subscriptions - via `table.Subscribe`, owning slices with external atoms via `createAtom` + + via the `table.subscribe` directive, owning slices with external atoms via `createAtom` + `options.atoms`, and packaging shared config into `createTableHook` (`useAppTable`, `createAppColumnHelper`, `useTableContext`, `table.AppCell` / `table.AppHeader` / `table.AppFooter`). Routing keywords: @@ -22,15 +22,17 @@ sources: - TanStack/table:docs/framework/lit/guide/table-state.md - TanStack/table:docs/framework/lit/lit-table.md - TanStack/table:packages/lit-table/src/TableController.ts + - TanStack/table:packages/lit-table/src/subscribe-directive.ts - TanStack/table:packages/lit-table/src/createTableHook.ts - TanStack/table:packages/lit-table/src/flexRender.ts - TanStack/table:packages/lit-table/src/reactivity.ts - TanStack/table:examples/lit/basic-table-controller/src/main.ts - TanStack/table:examples/lit/basic-external-atoms/src/main.ts + - TanStack/table:examples/lit/basic-subscribe/src/main.ts - TanStack/table:examples/lit/basic-app-table/src/main.ts --- -> **Maintainer note:** the Lit adapter is scheduled for a rewrite alongside TanStack Lit Store during the v9 beta cycle. APIs in this skill (especially `table.Subscribe` and the `TableController` invalidation strategy) may change in a future beta. The patterns below match `9.0.0-alpha.48`. +> **Maintainer note:** the Lit adapter now runs on `@tanstack/lit-store`. The old `table.Subscribe({ source, selector, children })` helper has been replaced by the `table.subscribe(source, selector?, template)` directive (see Core Pattern 4). Further changes may land during the v9 beta cycle. This skill builds on `tanstack-table/state-management` and `tanstack-table/setup`. Read those first — `state-management` explains the v9 atom model. The Lit adapter wires that atom model into a `ReactiveController` (`TableController`) attached to a `LitElement` host. @@ -144,7 +146,7 @@ Source: `packages/lit-table/src/TableController.ts`. ### 2. `.table(options, selector?)` second argument -The selector is a function from full table state to whatever you want exposed on `table.state`. Default is full state. Narrowing helps document the host's actual data dependencies; **host invalidation is still routed through the full `table.store` subscription**, so source-scoped subscriptions are not yet a guarantee of source-only re-renders. +The selector is a function from full table state to whatever you want exposed on `table.state`. Default is full state. Narrowing documents the host's actual data dependencies. Note this selector only shapes `table.state`; **the host `render()` still re-runs on every `table.store` change** because `TableController` subscribes to the full store. To skip re-rendering an expensive template region when its slice is unchanged, wrap it in the `table.subscribe` directive (Core Pattern 4). ```ts const table = this.tableController.table( @@ -167,24 +169,44 @@ const sorting = table.atoms.sorting.get() const snapshot = table.state ``` -### 4. `table.Subscribe` in templates +### 4. `table.subscribe` directive for fine-grained updates -Use `table.Subscribe` to project a slice during render. It reads the current value at template time. **In the current Lit adapter, host invalidation is wired through the full `table.store` subscription** — treat source mode as a render-time selection convenience. +`table.subscribe` is a Lit async directive (backed by `@tanstack/lit-store`'s `TanStackStoreSelector`). It subscribes the wrapped template to a single source and re-renders **only that template region**, leaving the rest of the host template untouched. The region re-renders whenever the selected value's reference changes (identity comparison). Return a specific slice (e.g. `state => state.pagination`) to update only when that slice changes; a selector that builds a fresh object every call re-renders on every store emit. Unlike `table.state`, it can update its slice without re-running the whole host template. + +Two call signatures: ```ts -${table.Subscribe({ - selector: (s) => s.pagination, - children: (pagination) => html`Page ${pagination.pageIndex + 1}`, -})} - -// source mode -${table.Subscribe({ - source: table.atoms.rowSelection, - children: (rs) => html`${Object.keys(rs).length} selected`, -})} +// 1. Selected slice — re-renders only when the `pagination` reference changes +${table.subscribe( + table.store, + (state) => state.pagination, + (pagination) => html`Page ${pagination.pageIndex + 1}`, +)} + +// 2. Whole source — any source mutation re-renders the region +${table.subscribe( + table.atoms.rowSelection, + (rowSelection) => html`${Object.keys(rowSelection).length} selected`, +)} +``` + +`source` is any store or atom: `table.store` (full table state), `table.atoms.`, or an external atom you own. + +**Use a stable selector reference.** The directive memoizes on the identity of the `source` and `selector` you pass. Declare selectors as class fields/methods so the same reference is passed every render: + +```ts +private getBodyState = (state: ReturnType) => ({ + columnFilters: state.columnFilters, + pagination: state.pagination, +}) + +// ...in render(): +${this.table.subscribe(this.table.store, this.getBodyState, () => html`…`)} ``` -Source: `packages/lit-table/src/TableController.ts` (lines 200–218). +An inline `(state) => ({ … })` allocates a new function every render, which forces the directive to re-subscribe and re-render every time, defeating the optimization. + +Source: `packages/lit-table/src/subscribe-directive.ts`; `examples/lit/basic-subscribe/src/main.ts`. ### 5. External atoms with `createAtom` + `options.atoms` @@ -193,7 +215,7 @@ Move slice ownership to a TanStack Store atom. The table writes to your atom whe Precedence: `options.atoms[key]` > `options.state[key]` > internal `baseAtoms[key]`. ```ts -import { createAtom } from '@tanstack/store' +import { createAtom } from '@tanstack/lit-store' import { TableController, rowPaginationFeature, @@ -379,12 +401,20 @@ const table = this.tableController.table({ v9 generates feature APIs and state slices only for registered features. The missing-feature failure is the #1 v9 trap. Source: `docs/guide/features.md`. -### HIGH Forgetting that `table.Subscribe` invalidates the host on any store change +### HIGH Passing an inline selector to `table.subscribe` -Wrong: assuming `` only re-renders the host on row-selection changes. +Wrong: -Correct: in the current adapter, every store change invalidates the host. Selection inside `table.Subscribe` projects the value, but the host still re-renders whenever the `table.store` subscription fires. Source-only invalidation is noted as "can be added later" in source. -Source: `packages/lit-table/src/TableController.ts`. +```ts +${table.subscribe( + table.store, + (state) => ({ columnFilters: state.columnFilters, pagination: state.pagination }), // new fn every render + () => html`…`, +)} +``` + +Correct: declare the selector as a stable class field/method and pass that reference. The directive memoizes on `source` + `selector` identity; a fresh inline function forces it to re-subscribe and re-render every host update, so the wrapped region never gets skipped. Note also that the host `render()` itself still runs on every `table.store` change — `table.subscribe` only spares the _wrapped_ region from re-rendering, it does not stop the host update. +Source: `packages/lit-table/src/subscribe-directive.ts`; `examples/lit/basic-subscribe/src/main.ts`. ### HIGH `this` binding in the options getter diff --git a/packages/lit-table/src/TableController.ts b/packages/lit-table/src/TableController.ts index 32e5a4df73..a93cee53ca 100644 --- a/packages/lit-table/src/TableController.ts +++ b/packages/lit-table/src/TableController.ts @@ -1,26 +1,16 @@ import { constructTable } from '@tanstack/table-core' import { litReactivity } from './reactivity' import { FlexRender } from './flexRender' -import type { Atom, ReadonlyAtom, ReadonlyStore, Store } from '@tanstack/store' + +import { subscribe } from './subscribe-directive' import type { - NoInfer, RowData, Table, TableFeatures, TableOptions, TableState, } from '@tanstack/table-core' -import type { - ReactiveController, - ReactiveControllerHost, - TemplateResult, -} from 'lit' - -export type SubscribeSource = - | Atom - | ReadonlyAtom - | Store - | ReadonlyStore +import type { ReactiveController, ReactiveControllerHost } from 'lit' /** * The extended table type returned by the Lit adapter. @@ -34,51 +24,40 @@ export type LitTable< > = Omit, 'store'> & { /** * @deprecated Prefer `table.state` for render reads, - * `table.atoms..get()` for slice snapshots, or `table.Subscribe` for + * `table.atoms..get()` for slice snapshots, or `table.subscribe` for * explicit subscriptions. `table.store.state` is a current-value snapshot and * is easy to misuse in render code. */ readonly store: Table['store'] /** - * Subscribe to a selected slice of table state, or to a single source (atom or store). - * - * **Lit note:** `TableController` still wires host updates via the full `table.store` - * subscription — source mode matches the React API and reads `source.get()` at render - * time. True source-only invalidation can be added later via `source.subscribe`. + * Subscribes to the table's underlying state store within a Lit template. + * Re-renders only the targeted template slice when the observed state changes. * * @example * ```ts - * table.Subscribe({ - * selector: (state) => ({ rowSelection: state.rowSelection }), - * children: (state) => html`
${JSON.stringify(state)}
`, - * }) + * // 1. Subscribe to a specific state slice (re-renders ONLY when rowSelection changes) + * html` + *
+ * ${table.subscribe( + * table.store, + * (state) => state.rowSelection, + * (rowSelection) => html`Selected: ${JSON.stringify(rowSelection)}` + * )} + *
+ * ` + * + * // 2. Subscribe to the full state (re-renders on any state mutation) + * html` + *
+ * ${table.subscribe( + * table.store, + * (state) => html`Total rows: ${state.rowModel.rows.length}` + * )} + *
+ * ` * ``` */ - Subscribe: { - (props: { - source: SubscribeSource - selector?: undefined - children: - | ((state: Readonly) => TemplateResult | string) - | TemplateResult - | string - }): TemplateResult | string - (props: { - source: SubscribeSource - selector: (state: TSourceValue) => TSubscribeSelected - children: - | ((state: Readonly) => TemplateResult | string) - | TemplateResult - | string - }): TemplateResult | string - (props: { - selector: (state: NoInfer>) => TSubscribeSelected - children: - | ((state: Readonly) => TemplateResult | string) - | TemplateResult - | string - }): TemplateResult | string - } + subscribe: typeof subscribe /** * The selected state of the table. This state may not match the structure of * the full table state because it is selected by the selector function that @@ -203,28 +182,9 @@ export class TableController< // Capture for closure const tableInstance = this._table - // Attach Subscribe function - const Subscribe = function Subscribe(props: { - source?: SubscribeSource - selector?: (state: unknown) => unknown - children: - | ((state: Readonly) => TemplateResult | string) - | TemplateResult - | string - }): TemplateResult | string { - const source = props.source ?? tableInstance.store - const value = source.get() - const selectedState = - props.selector !== undefined ? props.selector(value) : value - if (typeof props.children === 'function') { - return props.children(selectedState as Readonly) - } - return props.children - } as LitTable['Subscribe'] - return { ...this._table, - Subscribe, + subscribe, FlexRender, get state() { return (selector?.(tableInstance.store.state) ?? diff --git a/packages/lit-table/src/index.ts b/packages/lit-table/src/index.ts index 8b0b6659f7..bbc1d2b1b8 100755 --- a/packages/lit-table/src/index.ts +++ b/packages/lit-table/src/index.ts @@ -2,4 +2,5 @@ export * from '@tanstack/table-core' export * from './flexRender' export * from './TableController' +export * from './subscribe-directive' export * from './createTableHook' diff --git a/packages/lit-table/src/reactivity.ts b/packages/lit-table/src/reactivity.ts index 01e180e246..fe4403acfc 100644 --- a/packages/lit-table/src/reactivity.ts +++ b/packages/lit-table/src/reactivity.ts @@ -1,4 +1,4 @@ -import { batch, createAtom } from '@tanstack/store' +import { batch, createAtom } from '@tanstack/lit-store' import type { TableAtomOptions, TableReactivityBindings, diff --git a/packages/lit-table/src/subscribe-directive.ts b/packages/lit-table/src/subscribe-directive.ts new file mode 100644 index 0000000000..1cebb77b12 --- /dev/null +++ b/packages/lit-table/src/subscribe-directive.ts @@ -0,0 +1,198 @@ +import { TanStackStoreSelector } from '@tanstack/lit-store' +import { AsyncDirective, directive } from 'lit/async-directive.js' +import { noChange } from 'lit' +import type { Part, ReactiveControllerHost } from 'lit' +import type { DirectiveResult } from 'lit/async-directive.js' +import type { + Atom, + ReadonlyAtom, + ReadonlyStore, + Store, +} from '@tanstack/lit-store' + +export type SelectionSource = + | Atom + | ReadonlyAtom + | Store + | ReadonlyStore + +/** + * A function that selects a specific slice of state from the source. + * @template TSource - The complete state type from the store/atom. + * @template TSelected - The extracted or derived state type. + */ +type Selector = (state: TSource) => TSelected + +/** + * A render function that takes the selected state and returns content + * (typically a `TemplateResult`) to be rendered by Lit. + * @template TSelected - The selected state passed into the template. + */ +type TemplateFunction = (value: TSelected) => unknown + +/** + * A simple identity selector used when no specific selection is needed, + * allowing the directive to subscribe to the entire state of the source. + * @template T - The type of the state being passed through unchanged. + */ +const identitySelector = (state: T): T => state + +/** + * An asynchronous Lit directive that subscribes to a `@tanstack/lit-store` + * source and triggers re-renders specifically for the template portion it wraps. + * * It uses a "fake" `ReactiveControllerHost` to bridge the gap between + * TanStack's standard controller requirements and the `AsyncDirective` lifecycle. + */ +export class SubscribeDirective extends AsyncDirective { + /** The `TanStackStoreSelector` controller that manages the subscription to the store/atom */ + private controller?: TanStackStoreSelector + + /** The latest source and selector used to determine if a new subscription is needed on updates */ + private latestSource?: SelectionSource + /* The latest selector function used to determine if a new subscription is needed on updates */ + private latestSelector?: Selector + /* The latest resolved template function to render the selected state slice */ + private resolvedTemplate?: TemplateFunction + /* A flag to track whether the directive has been initialized, ensuring that the first render sets up the subscription correctly. */ + private initialized = false + + /** + * Renders the entire state of the source without a selector. + * @param source - The store or atom to subscribe to. + * @param template - The render function receiving the full state. + */ + render( + source: SelectionSource, + template: TemplateFunction, + ): unknown + + /** + * Renders a specific slice of state derived via a selector function. + * @param source - The store or atom to subscribe to. + * @param selector - A function to extract the relevant slice of state. + * @param template - The render function receiving the selected state slice. + */ + render( + source: SelectionSource, + selector: Selector, + template: TemplateFunction, + ): unknown + + render( + _source: SelectionSource, + _selectorOrTemplate: Selector | TemplateFunction, + _template?: TemplateFunction, + ) { + /** The actual rendering is handled in the {@link update} method to ensure that template is only evaluated if needed */ + return noChange + } + + update( + _part: Part, + args: + | [SelectionSource, TemplateFunction] + | [SelectionSource, Selector, TemplateFunction], + ) { + const [source, selectorOrTemplate, template] = args + const isIdentitySubscription: boolean = template === undefined + + const selector = isIdentitySubscription + ? identitySelector + : (selectorOrTemplate as Selector) + + const actualTemplate = isIdentitySubscription + ? (selectorOrTemplate as TemplateFunction) + : template + + const sourceChanged = this.latestSource !== source + const selectorChanged = this.latestSelector !== selector + const shouldReinitialize = + !this.initialized || sourceChanged || selectorChanged + + if (shouldReinitialize) { + if (this.initialized) { + this.controller?.hostDisconnected() + this.controller = undefined + } + + this.latestSource = source + this.latestSelector = selector + this.resolvedTemplate = actualTemplate + + if (!this.controller) { + this.controller = new TanStackStoreSelector( + this.createFakeHost(), + () => this.latestSource, + (state) => this.latestSelector?.(state), + ) + } + + this.controller.hostUpdate() + this.initialized = true + + return this.resolvedTemplate?.(this.controller.value) + } + + // Host rerender with same source + selector, so we can skip updating + return noChange + } + + /** Cleans up the controller subscription when the directive is removed from the DOM. */ + disconnected() { + this.controller?.hostDisconnected() + } + + /** Restores the controller subscription when the directive is re-attached to the DOM. */ + reconnected() { + this.controller?.hostUpdate() + } + + /** + * Creates a mock `ReactiveControllerHost` allowing the `TanStackStoreSelector` + * to plug into the `AsyncDirective`'s update cycle using `setValue()`. + */ + private createFakeHost(): ReactiveControllerHost { + return { + addController: () => {}, + removeController: () => {}, + requestUpdate: () => { + if (this.resolvedTemplate && this.controller) { + this.setValue(this.resolvedTemplate(this.controller.value)) + } + }, + get updateComplete() { + return Promise.resolve(true) + }, + } + } +} + +/** + * A Lit directive that subscribes to a source (Store or Atom) + * and efficiently updates only the wrapped template + * when the state or selected slice changes. + * @example + * ```ts + * // Without a selector (subscribes to entire state) + * html`
${subscribe(myStore, (state) => html`${state.count}`)}
` + * * // With a selector (only updates when `count` changes) + * html`
${subscribe(myStore, state => state.count, (count) => html`${count}`)}
` + * ``` + */ +export const subscribe = directive(SubscribeDirective) as { + /** Subscribes to the entire source state without filtering. */ + ( + source: SelectionSource, + template: TemplateFunction, + ): DirectiveResult + + /** + * Subscribes to a specific slice of the source state via a selector, + * preventing unnecessary re-renders when other parts of the state change. + */ + ( + source: SelectionSource, + selector: Selector, + template: TemplateFunction, + ): DirectiveResult +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f0f674a01..d725b92e23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1937,6 +1937,31 @@ importers: specifier: ^8.1.0 version: 8.1.0(@types/node@26.0.0)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.46.2)(yaml@2.9.0) + examples/lit/basic-subscribe: + dependencies: + '@faker-js/faker': + specifier: ^10.5.0 + version: 10.5.0 + '@tanstack/lit-store': + specifier: ^0.14.0 + version: 0.14.0(lit@3.3.3) + '@tanstack/lit-table': + specifier: workspace:* + version: link:../../../packages/lit-table + lit: + specifier: ^3.3.3 + version: 3.3.3 + devDependencies: + '@rollup/plugin-replace': + specifier: ^6.0.3 + version: 6.0.3(rollup@4.61.1) + typescript: + specifier: 6.0.3 + version: 6.0.3 + vite: + specifier: ^8.1.0 + version: 8.1.0(@types/node@26.0.0)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.46.2)(yaml@2.9.0) + examples/lit/basic-table-controller: dependencies: '@faker-js/faker': @@ -8873,9 +8898,9 @@ importers: packages/lit-table: dependencies: - '@tanstack/store': - specifier: ^0.11.0 - version: 0.11.0 + '@tanstack/lit-store': + specifier: ^0.14.0 + version: 0.14.0(lit@3.3.3) '@tanstack/table-core': specifier: workspace:* version: link:../table-core @@ -13864,6 +13889,11 @@ packages: resolution: {integrity: sha512-Apyfkf0O8QqHQJjy/cB1QW2GpYTOyjf98nFAaYtzjBc0V5YzJoy0wnmZSLvwBJxE8/Hhy0Bsa+EPt/fdeJyXkw==} hasBin: true + '@tanstack/lit-store@0.14.0': + resolution: {integrity: sha512-Y7aS5V8b5IdAhJQ+Jx+pNAuHLIyuZlI/qybIEFBOXVivsx/GSYUuQNCesLoiEH9RrBi8+pU5D+UeHPmpnNSIQg==} + peerDependencies: + lit: ^3.0.0 + '@tanstack/lit-virtual@3.13.30': resolution: {integrity: sha512-JT/ooOEIJLieZqLyRF09t8iG/XhW2BzkHkO6MeFfa5UOdEPuRwMUBeivFYWLUX3gzq9tMnluzPslFF3xIa66qw==} peerDependencies: @@ -24049,6 +24079,11 @@ snapshots: std-env: 4.1.0 yaml: 2.9.0 + '@tanstack/lit-store@0.14.0(lit@3.3.3)': + dependencies: + '@tanstack/store': 0.11.0 + lit: 3.3.3 + '@tanstack/lit-virtual@3.13.30(lit@3.3.3)': dependencies: '@tanstack/virtual-core': 3.17.1