From 563b172bf8f09588a298af2435a56a1787606562 Mon Sep 17 00:00:00 2001 From: Frederic Bahr Date: Sat, 16 May 2026 19:33:32 +0200 Subject: [PATCH 1/4] feat: implement fine-grained rerendering control with an async Lit directive An async Lit directe `subscribe` is implemented to allow for fine-grained reactivity and rerendering of templates. To support this, `@tanstack/lit-store` is integrated. --- docs/framework/lit/guide/migrating.md | 21 +- docs/framework/lit/guide/table-state.md | 28 +- .../reference/classes/SubscribeDirective.md | 327 +++++++++++++ .../lit/reference/classes/TableController.md | 12 +- .../lit/reference/functions/FlexRender-1.md | 2 +- .../reference/functions/createTableHook.md | 2 +- .../lit/reference/functions/flexRender.md | 2 +- docs/framework/lit/reference/index.md | 7 +- .../reference/type-aliases/AppCellContext.md | 14 +- .../type-aliases/AppColumnDefBase.md | 2 +- .../type-aliases/AppColumnDefTemplate.md | 2 +- .../reference/type-aliases/AppColumnHelper.md | 10 +- .../type-aliases/AppDisplayColumnDef.md | 2 +- .../type-aliases/AppGroupColumnDef.md | 2 +- .../type-aliases/AppHeaderContext.md | 8 +- .../lit/reference/type-aliases/AppLitTable.md | 2 +- .../reference/type-aliases/ComponentType.md | 2 +- .../type-aliases/CreateTableHookOptions.md | 2 +- .../reference/type-aliases/FlexRenderProps.md | 2 +- .../lit/reference/type-aliases/LitTable.md | 139 ++---- .../reference/type-aliases/SelectionSource.md | 22 + .../reference/type-aliases/SubscribeSource.md | 22 - .../lit/reference/variables/subscribe.md | 96 ++++ examples/lit/basic-subscribe/README.md | 34 ++ examples/lit/basic-subscribe/index.html | 13 + examples/lit/basic-subscribe/package.json | 22 + examples/lit/basic-subscribe/src/main.ts | 442 ++++++++++++++++++ examples/lit/basic-subscribe/src/makeData.ts | 43 ++ examples/lit/basic-subscribe/tsconfig.json | 23 + examples/lit/basic-subscribe/vite.config.js | 14 + packages/lit-table/package.json | 4 +- .../compose-with-tanstack-virtual/SKILL.md | 6 +- packages/lit-table/src/TableController.ts | 96 ++-- packages/lit-table/src/index.ts | 1 + packages/lit-table/src/reactivity.ts | 2 +- packages/lit-table/src/subscribe-directive.ts | 198 ++++++++ pnpm-lock.yaml | 82 +++- 37 files changed, 1422 insertions(+), 286 deletions(-) create mode 100644 docs/framework/lit/reference/classes/SubscribeDirective.md create mode 100644 docs/framework/lit/reference/type-aliases/SelectionSource.md delete mode 100644 docs/framework/lit/reference/type-aliases/SubscribeSource.md create mode 100644 docs/framework/lit/reference/variables/subscribe.md create mode 100644 examples/lit/basic-subscribe/README.md create mode 100644 examples/lit/basic-subscribe/index.html create mode 100644 examples/lit/basic-subscribe/package.json create mode 100644 examples/lit/basic-subscribe/src/main.ts create mode 100644 examples/lit/basic-subscribe/src/makeData.ts create mode 100644 examples/lit/basic-subscribe/tsconfig.json create mode 100644 examples/lit/basic-subscribe/vite.config.js create mode 100644 packages/lit-table/src/subscribe-directive.ts diff --git a/docs/framework/lit/guide/migrating.md b/docs/framework/lit/guide/migrating.md index 10ef5c9e97..4c9874b350 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/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..4d0c749df9 --- /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/fredericbahr/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/fredericbahr/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/fredericbahr/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/fredericbahr/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/fredericbahr/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/fredericbahr/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..ce8c9692cb 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/fredericbahr/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/fredericbahr/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/fredericbahr/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/fredericbahr/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/fredericbahr/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/fredericbahr/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..ae61f53b9f 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/fredericbahr/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 2799d874db..ae0a256678 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): object; ``` -Defined in: [createTableHook.ts:427](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L427) +Defined in: [packages/lit-table/src/createTableHook.ts:427](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L427) 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..70481438f5 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/fredericbahr/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 4ad9f64782..ffe1f585ff 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) ## Type Aliases @@ -23,7 +24,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/type-aliases/AppCellContext.md b/docs/framework/lit/reference/type-aliases/AppCellContext.md index 1fbdd846bc..e2936c1a7b 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:42](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L42) +Defined in: [packages/lit-table/src/createTableHook.ts:42](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L42) 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 & TCellComponents & 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/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L48) #### Type Declaration @@ -62,7 +62,7 @@ FlexRender: () => TemplateResult | string | null; column: Column; ``` -Defined in: [createTableHook.ts:52](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L52) +Defined in: [packages/lit-table/src/createTableHook.ts:52](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L52) *** @@ -72,7 +72,7 @@ Defined in: [createTableHook.ts:52](https://github.com/TanStack/table/blob/main/ getValue: CellContext["getValue"]; ``` -Defined in: [createTableHook.ts:53](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L53) +Defined in: [packages/lit-table/src/createTableHook.ts:53](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L53) *** @@ -82,7 +82,7 @@ Defined in: [createTableHook.ts:53](https://github.com/TanStack/table/blob/main/ renderValue: CellContext["renderValue"]; ``` -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/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L54) *** @@ -92,7 +92,7 @@ Defined in: [createTableHook.ts:54](https://github.com/TanStack/table/blob/main/ row: Row; ``` -Defined in: [createTableHook.ts:55](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L55) +Defined in: [packages/lit-table/src/createTableHook.ts:55](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L55) *** @@ -102,4 +102,4 @@ Defined in: [createTableHook.ts:55](https://github.com/TanStack/table/blob/main/ table: Table; ``` -Defined in: [createTableHook.ts:56](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L56) +Defined in: [packages/lit-table/src/createTableHook.ts:56](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L56) diff --git a/docs/framework/lit/reference/type-aliases/AppColumnDefBase.md b/docs/framework/lit/reference/type-aliases/AppColumnDefBase.md index 3d1b211955..e8c9ac0cd6 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:91](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L91) +Defined in: [packages/lit-table/src/createTableHook.ts:91](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L91) 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 a412bd39e7..95570c0b0f 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:84](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L84) +Defined in: [packages/lit-table/src/createTableHook.ts:84](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L84) 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 60f2b28a92..eeb3b1ecdd 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:167](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L167) +Defined in: [packages/lit-table/src/createTableHook.ts:167](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L167) 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:177](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L177) +Defined in: [packages/lit-table/src/createTableHook.ts:177](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L177) 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:208](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L208) +Defined in: [packages/lit-table/src/createTableHook.ts:208](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L208) 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:216](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L216) +Defined in: [packages/lit-table/src/createTableHook.ts:216](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L216) 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:229](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L229) +Defined in: [packages/lit-table/src/createTableHook.ts:229](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L229) 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 63cf03144f..8cc2227e83 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:115](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L115) +Defined in: [packages/lit-table/src/createTableHook.ts:115](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L115) 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 3d58babc0b..e8db5f20fb 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:138](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L138) +Defined in: [packages/lit-table/src/createTableHook.ts:138](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L138) 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 3f5cd3718d..da018ce66a 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:63](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L63) +Defined in: [packages/lit-table/src/createTableHook.ts:63](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L63) 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: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/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L69) *** @@ -50,7 +50,7 @@ Defined in: [createTableHook.ts:69](https://github.com/TanStack/table/blob/main/ header: Header & THeaderComponents & object; ``` -Defined in: [createTableHook.ts:70](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L70) +Defined in: [packages/lit-table/src/createTableHook.ts:70](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L70) #### Type Declaration @@ -72,4 +72,4 @@ FlexRender: () => TemplateResult | string | null; table: Table; ``` -Defined in: [createTableHook.ts:74](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L74) +Defined in: [packages/lit-table/src/createTableHook.ts:74](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L74) diff --git a/docs/framework/lit/reference/type-aliases/AppLitTable.md b/docs/framework/lit/reference/type-aliases/AppLitTable.md index c06233f64c..d7fcf85844 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:282](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L282) +Defined in: [packages/lit-table/src/createTableHook.ts:282](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L282) Extended table API returned by useAppTable with all App wrapper functions diff --git a/docs/framework/lit/reference/type-aliases/ComponentType.md b/docs/framework/lit/reference/type-aliases/ComponentType.md index dc7a43145a..6055fe8e6a 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/fredericbahr/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 03203bdc4e..2b248f4feb 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:247](https://github.com/TanStack/table/blob/main/packages/lit-table/src/createTableHook.ts#L247) +Defined in: [packages/lit-table/src/createTableHook.ts:247](https://github.com/fredericbahr/table/blob/main/packages/lit-table/src/createTableHook.ts#L247) 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..f1d710acde 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/fredericbahr/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..477760955c 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/fredericbahr/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..728ccf9d18 --- /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/fredericbahr/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..c16fdf022c --- /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/fredericbahr/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/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..36e8d634a5 --- /dev/null +++ b/examples/lit/basic-subscribe/package.json @@ -0,0 +1,22 @@ +{ + "name": "tanstack-lit-table-example-basic-subscribe", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "start": "vite", + "lint": "eslint ./src" + }, + "dependencies": { + "@faker-js/faker": "^10.4.0", + "@tanstack/lit-store": "^0.13.2", + "@tanstack/lit-table": "^9.0.0-beta.6", + "lit": "^3.3.3" + }, + "devDependencies": { + "@rollup/plugin-replace": "^6.0.3", + "typescript": "6.0.3", + "vite": "^8.0.16" + } +} diff --git a/examples/lit/basic-subscribe/src/main.ts b/examples/lit/basic-subscribe/src/main.ts new file mode 100644 index 0000000000..25156b632a --- /dev/null +++ b/examples/lit/basic-subscribe/src/main.ts @@ -0,0 +1,442 @@ +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, + createSortedRowModel, + filterFns, + globalFilteringFeature, + rowPaginationFeature, + rowSelectionFeature, + rowSortingFeature, + sortFns, + tableFeatures, +} from '@tanstack/lit-table' +import { createAtom } from '@tanstack/lit-store' +import { makeData } from './makeData' +import type { + ColumnFiltersState, + LitTable, + PaginationState, + RowSelectionState, + TableFeature, +} from '@tanstack/lit-table' +import type { Person } from './makeData' + +/** + * This example demonstrates fine-grained state subscriptions using table.subscribe. + * Each part of the table subscribes only to the state it needs, optimizing re-renders. + * External atoms give you full control over state management. + */ + +const features = tableFeatures({ + rowPaginationFeature, + rowSelectionFeature, + columnFilteringFeature, + globalFilteringFeature, + rowSortingFeature, +}) + +const columnHelper = createColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.display({ + id: 'select', + header: 'Select', + cell: ({ row }) => html` + + `, + }), + columnHelper.accessor('firstName', { + header: 'First Name', + cell: (info) => info.getValue(), + }), + columnHelper.accessor('lastName', { + header: 'Last Name', + cell: (info) => info.getValue(), + }), + columnHelper.accessor('age', { + header: 'Age', + }), + columnHelper.accessor('visits', { + header: 'Visits', + }), + columnHelper.accessor('status', { + header: 'Status', + }), + columnHelper.accessor('progress', { + header: 'Profile Progress', + }), +]) + +// External state atoms for fine-grained control +const rowSelectionAtom = createAtom({}) +const columnFiltersAtom = createAtom([]) +const paginationAtom = createAtom({ + pageIndex: 0, + pageSize: 10, +}) + +@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, + rowModels: { + filteredRowModel: createFilteredRowModel(filterFns), + paginatedRowModel: createPaginatedRowModel(), + sortedRowModel: createSortedRowModel(sortFns), + }, + columns, + data: this._data, + getRowId: (row) => row.id, + enableRowSelection: true, + atoms: { + rowSelection: rowSelectionAtom, + columnFilters: columnFiltersAtom, + pagination: paginationAtom, + }, + debugTable: true, + }, + () => null, // subscribe to no table state by default + ) + + private getPaginationState = ( + state: ReturnType, + ) => state.pagination + + 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 })) + } + } + + protected render() { + return html` +
+
+ + +
+ +
+ + + + ${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 })} +
`} +
${FlexRender({ cell })}
+ ${this.table.subscribe( + rowSelectionAtom, + () => html` + + `, + )} + + Page Rows + (${this.table.getRowModel().rows.length.toLocaleString()}) +
+ +
+ + + ${this.table.subscribe( + this.table.store, + this.getPaginationState, + (pagination) => { + console.log('rendering pagination') + return 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/packages/lit-table/package.json b/packages/lit-table/package.json index de6e9ba70e..a982cef68d 100644 --- a/packages/lit-table/package.json +++ b/packages/lit-table/package.json @@ -57,8 +57,8 @@ "build": "tsdown" }, "dependencies": { - "@tanstack/store": "^0.11.0", - "@tanstack/table-core": "workspace:*" + "@tanstack/lit-store": "^0.14.0", + "@tanstack/table-core": "^9.0.0-beta.5" }, "devDependencies": { "@lit/context": "^1.1.6", 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/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 7124801b69..12e9ebe2a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,7 +63,7 @@ importers: version: 4.0.3 nx: specifier: ^22.7.5 - version: 22.7.5 + version: 22.7.5(debug@4.4.3) prettier: specifier: ^3.8.3 version: 3.8.3 @@ -1928,6 +1928,31 @@ importers: specifier: ^8.0.16 version: 8.0.16(@types/node@25.9.2)(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.4.0 + version: 10.4.0 + '@tanstack/lit-store': + specifier: ^0.13.2 + version: 0.13.2(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.0.16 + version: 8.0.16(@types/node@25.9.2)(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': @@ -8797,9 +8822,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 @@ -8887,13 +8912,13 @@ importers: version: 0.5.0(@angular/core@22.0.0(@angular/compiler@22.0.0)(rxjs@7.8.2))(@types/react@19.2.17)(preact@10.29.2)(react@19.2.7)(solid-js@1.9.13)(vue@3.5.35(typescript@6.0.3)) '@tanstack/react-devtools': specifier: '>=0.10.0' - version: 0.10.5(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.7))(react@19.2.7)(solid-js@1.9.13) + version: 0.10.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.7))(react@19.2.7)(solid-js@1.9.13) '@tanstack/table-devtools': specifier: workspace:* version: link:../table-devtools '@types/react-dom': specifier: '>=18.0.0' - version: 19.2.3(@types/react@19.2.16) + version: 19.2.3(@types/react@19.2.17) react-dom: specifier: '>=18' version: 19.2.6(react@19.2.7) @@ -9237,6 +9262,7 @@ packages: '@angular/animations@22.0.0': resolution: {integrity: sha512-Klo9ZiRj5ykXPliUmwy0eXvDad079YMy+Ob4EITSFSXVLRy55qv64/8SvWNtKEQPelF50H9O2vULoqpIvdWoAw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} + deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' peerDependencies: '@angular/core': 22.0.0 @@ -9349,6 +9375,7 @@ packages: '@angular/platform-browser-dynamic@22.0.0': resolution: {integrity: sha512-xGOk/XLg+XiuuFa5FEMsrYSdkuKIdx2Xqo1kAGpIdjnI0o7qV8pVmW7S/1NMmEULnbmjzxjFUtNX5RPZ17iA/Q==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} + deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: '@angular/common': 22.0.0 '@angular/compiler': 22.0.0 @@ -13741,6 +13768,16 @@ packages: resolution: {integrity: sha512-Psl+oDiidLvtctswkTQ1P6sQIihwrMLcdfQVfkLpO42oKwxWEr1lodWUHiOG5jFXsGwDDvpUv/WAdlmJF+yGpw==} hasBin: true + '@tanstack/lit-store@0.13.2': + resolution: {integrity: sha512-uAa5gQbmPOESokHM5t+AhQ3Ye8S2/bN+PB6CHKjHXixNN6aMDSQEtHoguPoMb3a3q/NeJ2NdiseyoUg4vsh/ng==} + peerDependencies: + lit: ^3.0.0 + + '@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.29': resolution: {integrity: sha512-fnFZ2cFcOEskbLIzNwPgN1sZ9R4xxG3wWsD8WnHRbKlUZUs9qs1lyYiakXaIHHpKedYNbhmYaP9iYYGoXwYSYQ==} peerDependencies: @@ -14109,9 +14146,6 @@ packages: peerDependencies: '@types/react': '*' - '@types/react@19.2.16': - resolution: {integrity: sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==} - '@types/react@19.2.17': resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} @@ -23719,6 +23753,16 @@ snapshots: semver: 7.8.2 yaml: 2.8.3 + '@tanstack/lit-store@0.13.2(lit@3.3.3)': + dependencies: + '@tanstack/store': 0.11.0 + lit: 3.3.3 + + '@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.29(lit@3.3.3)': dependencies: '@tanstack/virtual-core': 3.17.0 @@ -23771,11 +23815,11 @@ snapshots: '@tanstack/query-devtools@5.101.0': optional: true - '@tanstack/react-devtools@0.10.5(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.7))(react@19.2.7)(solid-js@1.9.13)': + '@tanstack/react-devtools@0.10.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.7))(react@19.2.7)(solid-js@1.9.13)': dependencies: '@tanstack/devtools': 0.12.2(csstype@3.2.3)(solid-js@1.9.13) - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) react: 19.2.7 react-dom: 19.2.6(react@19.2.7) transitivePeerDependencies: @@ -24178,10 +24222,6 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/react-dom@19.2.3(@types/react@19.2.16)': - dependencies: - '@types/react': 19.2.16 - '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: '@types/react': 19.2.17 @@ -24190,10 +24230,6 @@ snapshots: dependencies: '@types/react': 19.2.17 - '@types/react@19.2.16': - dependencies: - csstype: 3.2.3 - '@types/react@19.2.17': dependencies: csstype: 3.2.3 @@ -24824,7 +24860,7 @@ snapshots: postcss: 8.5.13 postcss-value-parser: 4.2.0 - axios@1.16.0: + axios@1.16.0(debug@4.4.3): dependencies: follow-redirects: 1.16.0(debug@4.4.3) form-data: 4.0.5 @@ -27241,7 +27277,7 @@ snapshots: dependencies: boolbase: 1.0.0 - nx@22.7.5: + nx@22.7.5(debug@4.4.3): dependencies: '@emnapi/core': 1.4.5 '@emnapi/runtime': 1.4.5 @@ -27256,7 +27292,7 @@ snapshots: ansi-styles: 4.3.0 argparse: 2.0.1 asynckit: 0.4.0 - axios: 1.16.0 + axios: 1.16.0(debug@4.4.3) balanced-match: 4.0.3 base64-js: 1.5.1 bl: 4.1.0 From 88865dfa944b59748f18035201054107544cd9be Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Mon, 29 Jun 2026 07:25:28 -0500 Subject: [PATCH 2/4] fix package.json --- packages/lit-table/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lit-table/package.json b/packages/lit-table/package.json index d1d9a0ee0b..15e037cc72 100644 --- a/packages/lit-table/package.json +++ b/packages/lit-table/package.json @@ -58,7 +58,7 @@ }, "dependencies": { "@tanstack/lit-store": "^0.14.0", - "@tanstack/table-core": "^9.0.0-beta.5" + "@tanstack/table-core": "workspace:*" }, "devDependencies": { "@lit/context": "^1.1.6", From 056c654420bafd1a319346f47c50dbfb1eb28073 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Mon, 29 Jun 2026 08:36:36 -0500 Subject: [PATCH 3/4] fix a ton of stuff --- docs/config.json | 4 + docs/framework/lit/guide/row-selection.md | 2 +- examples/angular/basic-app-table/package.json | 1 + .../angular/basic-external-atoms/package.json | 1 + .../angular/basic-external-state/package.json | 1 + .../angular/basic-inject-table/package.json | 3 +- examples/angular/column-groups/package.json | 3 +- examples/angular/column-ordering/package.json | 1 + .../angular/column-pinning-split/package.json | 3 +- .../column-pinning-sticky/package.json | 1 + examples/angular/column-pinning/package.json | 1 + .../column-resizing-performant/package.json | 1 + examples/angular/column-resizing/package.json | 3 +- examples/angular/column-sizing/package.json | 3 +- .../angular/column-visibility/package.json | 1 + examples/angular/custom-plugin/package.json | 3 +- examples/angular/editable/package.json | 1 + examples/angular/expanding/package.json | 1 + examples/angular/filters-faceted/package.json | 1 + examples/angular/filters-fuzzy/package.json | 3 +- examples/angular/filters/package.json | 1 + examples/angular/grouping/package.json | 3 +- examples/angular/kitchen-sink/package.json | 1 + examples/angular/pagination/package.json | 3 +- examples/angular/remote-data/package.json | 1 + examples/angular/row-dnd/package.json | 3 +- examples/angular/row-pinning/package.json | 3 +- .../angular/row-selection-signal/package.json | 3 +- examples/angular/row-selection/package.json | 3 +- examples/angular/signal-input/package.json | 3 +- examples/angular/sorting/package.json | 3 +- examples/angular/sub-components/package.json | 3 +- .../angular/virtualized-columns/package.json | 3 +- .../package.json | 3 +- .../angular/virtualized-rows/package.json | 3 +- .../angular/with-tanstack-form/package.json | 3 +- .../angular/with-tanstack-query/package.json | 3 +- examples/lit/basic-app-table/package.json | 3 +- .../lit/basic-external-atoms/package.json | 3 +- .../lit/basic-external-state/package.json | 3 +- examples/lit/basic-subscribe/package.json | 11 +- examples/lit/basic-subscribe/src/main.ts | 370 ++++++++----- .../lit/basic-table-controller/package.json | 3 +- examples/lit/column-groups/package.json | 3 +- examples/lit/column-ordering/package.json | 3 +- .../lit/column-pinning-split/package.json | 3 +- .../lit/column-pinning-sticky/package.json | 3 +- examples/lit/column-pinning/package.json | 3 +- .../column-resizing-performant/package.json | 3 +- examples/lit/column-resizing/package.json | 3 +- examples/lit/column-sizing/package.json | 3 +- examples/lit/column-visibility/package.json | 3 +- examples/lit/expanding/package.json | 3 +- examples/lit/filters-faceted/package.json | 3 +- examples/lit/filters-fuzzy/package.json | 3 +- examples/lit/filters/package.json | 3 +- examples/lit/grouping/package.json | 3 +- examples/lit/kitchen-sink/package.json | 3 +- examples/lit/pagination/package.json | 3 +- examples/lit/row-pinning/package.json | 3 +- examples/lit/row-selection/package.json | 3 +- .../lit/sorting-dynamic-data/package.json | 3 +- examples/lit/sorting/package.json | 3 +- examples/lit/sub-components/package.json | 3 +- examples/lit/virtualized-columns/package.json | 3 +- .../package.json | 3 +- examples/lit/virtualized-rows/package.json | 3 +- .../preact/composable-tables/src/main.tsx | 488 +++++++++--------- examples/react/composable-tables/src/main.tsx | 484 +++++++++-------- examples/solid/basic-app-table/package.json | 3 +- .../solid/basic-app-table/src/makeData.ts | 2 +- .../solid/basic-app-table/src/vite-env.d.ts | 1 + examples/solid/basic-app-table/vite.config.ts | 1 - .../solid/basic-external-atoms/package.json | 3 +- .../basic-external-atoms/src/vite-env.d.ts | 1 + .../solid/basic-external-atoms/vite.config.ts | 1 - .../solid/basic-external-state/package.json | 3 +- .../basic-external-state/src/vite-env.d.ts | 1 + .../solid/basic-external-state/vite.config.ts | 1 - examples/solid/basic-use-table/package.json | 3 +- .../solid/basic-use-table/src/vite-env.d.ts | 1 + examples/solid/basic-use-table/vite.config.ts | 1 - examples/solid/column-groups/package.json | 3 +- examples/solid/column-groups/src/makeData.ts | 2 +- .../solid/column-groups/src/vite-env.d.ts | 1 + examples/solid/column-groups/vite.config.ts | 1 - examples/solid/column-ordering/package.json | 3 +- .../solid/column-ordering/src/makeData.ts | 2 +- .../solid/column-ordering/src/vite-env.d.ts | 1 + examples/solid/column-ordering/vite.config.ts | 1 - .../solid/column-pinning-split/package.json | 3 +- .../column-pinning-split/src/vite-env.d.ts | 1 + .../solid/column-pinning-sticky/package.json | 3 +- .../solid/column-pinning-sticky/src/App.tsx | 1 - .../column-pinning-sticky/src/vite-env.d.ts | 1 + examples/solid/column-pinning/package.json | 3 +- .../solid/column-pinning/src/vite-env.d.ts | 1 + .../column-resizing-performant/package.json | 3 +- .../column-resizing-performant/src/App.tsx | 4 - .../src/vite-env.d.ts | 1 + examples/solid/column-resizing/package.json | 3 +- .../solid/column-resizing/src/makeData.ts | 2 +- .../solid/column-resizing/src/vite-env.d.ts | 1 + examples/solid/column-sizing/package.json | 3 +- examples/solid/column-sizing/src/makeData.ts | 2 +- .../solid/column-sizing/src/vite-env.d.ts | 1 + examples/solid/column-visibility/package.json | 3 +- .../solid/column-visibility/src/makeData.ts | 2 +- .../solid/column-visibility/src/vite-env.d.ts | 1 + .../solid/column-visibility/vite.config.ts | 1 - examples/solid/expanding/package.json | 3 +- examples/solid/expanding/src/vite-env.d.ts | 1 + examples/solid/filters-faceted/package.json | 3 +- .../solid/filters-faceted/src/makeData.ts | 2 +- .../solid/filters-faceted/src/vite-env.d.ts | 1 + examples/solid/filters-faceted/vite.config.ts | 1 - examples/solid/filters-fuzzy/package.json | 3 +- .../solid/filters-fuzzy/src/vite-env.d.ts | 1 + examples/solid/filters/package.json | 3 +- examples/solid/filters/src/makeData.ts | 2 +- examples/solid/filters/src/vite-env.d.ts | 1 + examples/solid/filters/vite.config.ts | 1 - examples/solid/grouping/package.json | 3 +- examples/solid/grouping/src/vite-env.d.ts | 1 + examples/solid/kitchen-sink/package.json | 3 +- examples/solid/kitchen-sink/src/App.tsx | 4 +- examples/solid/kitchen-sink/src/vite-env.d.ts | 1 + examples/solid/kitchen-sink/vite.config.ts | 1 - examples/solid/pagination/package.json | 3 +- examples/solid/pagination/src/vite-env.d.ts | 1 + examples/solid/row-pinning/package.json | 3 +- examples/solid/row-pinning/src/vite-env.d.ts | 1 + examples/solid/row-selection/package.json | 3 +- .../solid/row-selection/src/vite-env.d.ts | 1 + examples/solid/row-selection/vite.config.ts | 1 - examples/solid/sorting/package.json | 3 +- examples/solid/sorting/src/makeData.ts | 2 +- examples/solid/sorting/src/vite-env.d.ts | 1 + examples/solid/sorting/vite.config.ts | 1 - examples/solid/sub-components/package.json | 3 +- .../solid/sub-components/src/vite-env.d.ts | 1 + .../solid/virtualized-columns/package.json | 3 +- .../virtualized-columns/src/vite-env.d.ts | 1 + .../package.json | 3 +- .../src/App.tsx | 1 - .../src/vite-env.d.ts | 1 + examples/solid/virtualized-rows/package.json | 3 +- examples/solid/virtualized-rows/src/App.tsx | 2 +- .../solid/virtualized-rows/src/vite-env.d.ts | 1 + .../solid/with-tanstack-form/package.json | 3 +- .../with-tanstack-form/src/vite-env.d.ts | 1 + .../solid/with-tanstack-query/package.json | 3 +- .../with-tanstack-query/src/vite-env.d.ts | 1 + .../solid/with-tanstack-router/package.json | 3 +- .../src/components/table.tsx | 52 +- .../src/hooks/useFilters.ts | 2 +- examples/vanilla/basic/package.json | 3 +- examples/vanilla/basic/src/vite-env.d.ts | 1 + examples/vanilla/pagination/package.json | 3 +- examples/vanilla/pagination/src/vite-env.d.ts | 1 + examples/vanilla/sorting/package.json | 3 +- examples/vanilla/sorting/src/vite-env.d.ts | 1 + .../skills/lit/lit-table-controller/SKILL.md | 15 +- .../lit-table/skills/lit/table-state/SKILL.md | 72 ++- pnpm-lock.yaml | 18 +- 165 files changed, 1020 insertions(+), 832 deletions(-) create mode 100644 examples/solid/basic-app-table/src/vite-env.d.ts create mode 100644 examples/solid/basic-external-atoms/src/vite-env.d.ts create mode 100644 examples/solid/basic-external-state/src/vite-env.d.ts create mode 100644 examples/solid/basic-use-table/src/vite-env.d.ts create mode 100644 examples/solid/column-groups/src/vite-env.d.ts create mode 100644 examples/solid/column-ordering/src/vite-env.d.ts create mode 100644 examples/solid/column-pinning-split/src/vite-env.d.ts create mode 100644 examples/solid/column-pinning-sticky/src/vite-env.d.ts create mode 100644 examples/solid/column-pinning/src/vite-env.d.ts create mode 100644 examples/solid/column-resizing-performant/src/vite-env.d.ts create mode 100644 examples/solid/column-resizing/src/vite-env.d.ts create mode 100644 examples/solid/column-sizing/src/vite-env.d.ts create mode 100644 examples/solid/column-visibility/src/vite-env.d.ts create mode 100644 examples/solid/expanding/src/vite-env.d.ts create mode 100644 examples/solid/filters-faceted/src/vite-env.d.ts create mode 100644 examples/solid/filters-fuzzy/src/vite-env.d.ts create mode 100644 examples/solid/filters/src/vite-env.d.ts create mode 100644 examples/solid/grouping/src/vite-env.d.ts create mode 100644 examples/solid/kitchen-sink/src/vite-env.d.ts create mode 100644 examples/solid/pagination/src/vite-env.d.ts create mode 100644 examples/solid/row-pinning/src/vite-env.d.ts create mode 100644 examples/solid/row-selection/src/vite-env.d.ts create mode 100644 examples/solid/sorting/src/vite-env.d.ts create mode 100644 examples/solid/sub-components/src/vite-env.d.ts create mode 100644 examples/solid/virtualized-columns/src/vite-env.d.ts create mode 100644 examples/solid/virtualized-infinite-scrolling/src/vite-env.d.ts create mode 100644 examples/solid/virtualized-rows/src/vite-env.d.ts create mode 100644 examples/solid/with-tanstack-form/src/vite-env.d.ts create mode 100644 examples/solid/with-tanstack-query/src/vite-env.d.ts create mode 100644 examples/vanilla/basic/src/vite-env.d.ts create mode 100644 examples/vanilla/pagination/src/vite-env.d.ts create mode 100644 examples/vanilla/sorting/src/vite-env.d.ts 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/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/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/package.json b/examples/lit/basic-subscribe/package.json index 36e8d634a5..03df322006 100644 --- a/examples/lit/basic-subscribe/package.json +++ b/examples/lit/basic-subscribe/package.json @@ -6,17 +6,18 @@ "build": "vite build", "serve": "vite preview", "start": "vite", - "lint": "eslint ./src" + "lint": "eslint ./src", + "test:types": "tsc --noEmit" }, "dependencies": { - "@faker-js/faker": "^10.4.0", - "@tanstack/lit-store": "^0.13.2", - "@tanstack/lit-table": "^9.0.0-beta.6", + "@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.0.16" + "vite": "^8.1.0" } } diff --git a/examples/lit/basic-subscribe/src/main.ts b/examples/lit/basic-subscribe/src/main.ts index 25156b632a..062385d5dc 100644 --- a/examples/lit/basic-subscribe/src/main.ts +++ b/examples/lit/basic-subscribe/src/main.ts @@ -8,30 +8,25 @@ import { createColumnHelper, createFilteredRowModel, createPaginatedRowModel, - createSortedRowModel, filterFns, globalFilteringFeature, rowPaginationFeature, rowSelectionFeature, - rowSortingFeature, - sortFns, + subscribe, tableFeatures, } from '@tanstack/lit-table' import { createAtom } from '@tanstack/lit-store' import { makeData } from './makeData' -import type { - ColumnFiltersState, - LitTable, - PaginationState, - RowSelectionState, - TableFeature, -} from '@tanstack/lit-table' +import type { HeaderContext, RowSelectionState } from '@tanstack/lit-table' import type { Person } from './makeData' /** - * This example demonstrates fine-grained state subscriptions using table.subscribe. - * Each part of the table subscribes only to the state it needs, optimizing re-renders. - * External atoms give you full control over state management. + * 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({ @@ -39,7 +34,11 @@ const features = tableFeatures({ rowSelectionFeature, columnFilteringFeature, globalFilteringFeature, - rowSortingFeature, + // 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() @@ -47,29 +46,54 @@ const columnHelper = createColumnHelper() const columns = columnHelper.columns([ columnHelper.display({ id: 'select', - header: 'Select', - cell: ({ row }) => html` - - `, + // 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('lastName', { - header: 'Last Name', + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => html`Last Name`, cell: (info) => info.getValue(), }), columnHelper.accessor('age', { - header: 'Age', + header: () => 'Age', }), columnHelper.accessor('visits', { - header: 'Visits', + header: () => html`Visits`, }), columnHelper.accessor('status', { header: 'Status', @@ -79,13 +103,9 @@ const columns = columnHelper.columns([ }), ]) -// External state atoms for fine-grained control +// 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({}) -const columnFiltersAtom = createAtom([]) -const paginationAtom = createAtom({ - pageIndex: 0, - pageSize: 10, -}) @customElement('lit-table-example') class LitTableExample extends LitElement { @@ -97,29 +117,20 @@ class LitTableExample extends LitElement { private table = this.tableController.table( { features, - rowModels: { - filteredRowModel: createFilteredRowModel(filterFns), - paginatedRowModel: createPaginatedRowModel(), - sortedRowModel: createSortedRowModel(sortFns), - }, columns, data: this._data, getRowId: (row) => row.id, enableRowSelection: true, atoms: { rowSelection: rowSelectionAtom, - columnFilters: columnFiltersAtom, - pagination: paginationAtom, }, debugTable: true, }, - () => null, // subscribe to no table state by default + () => null, // subscribe to no table state by default; use table.subscribe below ) - private getPaginationState = ( - state: ReturnType, - ) => state.pagination - + // 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, @@ -132,6 +143,71 @@ class LitTableExample extends LitElement { } } + 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`
@@ -150,6 +226,26 @@ class LitTableExample extends LitElement {
+ +
+ ${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..." + /> + `, + )} +
+
@@ -166,14 +262,12 @@ class LitTableExample extends LitElement { `, )} @@ -182,7 +276,7 @@ class LitTableExample extends LitElement { )} - + ${this.table.subscribe( this.table.store, this.getBodyState, @@ -211,6 +305,7 @@ class LitTableExample extends LitElement { `, @@ -228,87 +323,82 @@ class LitTableExample extends LitElement {
- + ${this.table.subscribe( this.table.store, - this.getPaginationState, - (pagination) => { - console.log('rendering pagination') - return 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" - /> - - { + const target = e.currentTarget as HTMLInputElement + const page = target.value ? Number(target.value) - 1 : 0 + this.table.setPageIndex(page) }} - > - ${[10, 20, 30, 40, 50, 100].map( - (pageSize) => - html``, - )} - -
- ` - }, + class="page-size-input" + /> + + + + `, )}
- + ${this.table.subscribe( rowSelectionAtom, (rowSelection) => html` @@ -325,7 +415,7 @@ class LitTableExample extends LitElement {

- + ${this.table.subscribe( this.table.store, @@ -353,17 +443,14 @@ class LitTableExample extends LitElement { border-bottom: 1px solid lightgray; border-right: 1px solid lightgray; padding: 2px 4px; + text-align: left; + vertical-align: top; } td { padding: 2px 4px; } - .sortable-header { - cursor: pointer; - user-select: none; - } - .demo-root { padding: 0.5rem; } @@ -383,6 +470,19 @@ class LitTableExample extends LitElement { gap: 0.25rem; } + .filter-row { + display: flex; + gap: 0.25rem; + } + + .filter-input { + border: 1px solid currentColor; + border-radius: 0.25rem; + padding: 0.25rem; + width: 6rem; + font-weight: normal; + } + .demo-button, .page-size-input { border: 1px solid currentColor; @@ -405,7 +505,7 @@ class LitTableExample extends LitElement { .summary-panel { border: 1px solid currentColor; - box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); + border-radius: 0.25rem; padding: 0.5rem; width: 100%; } 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. */} -
-
${header.isPlaceholder ? null - : html`
- ${FlexRender({ header })} -
`} + : html` +
${FlexRender({ header })}
+ ${header.column.getCanFilter() + ? this.renderColumnFilter(header.getContext()) + : null} + `}
- - {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..2f740ae5e0 100644 --- a/examples/solid/column-pinning-sticky/src/App.tsx +++ b/examples/solid/column-pinning-sticky/src/App.tsx @@ -110,7 +110,6 @@ function App() { debugColumns: true, columnResizeMode: 'onChange', }, - (state) => state, ) const randomizeColumns = () => { 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..82ca72bb74 100644 --- a/examples/solid/column-resizing-performant/src/App.tsx +++ b/examples/solid/column-resizing-performant/src/App.tsx @@ -73,10 +73,6 @@ function App() { debugHeaders: true, debugColumns: true, }, - (state) => ({ - columnSizing: state.columnSizing, - columnResizing: state.columnResizing, - }), ) const columnSizeVars = createMemo(() => { 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/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/pnpm-lock.yaml b/pnpm-lock.yaml index 11e6df1e07..d725b92e23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1940,11 +1940,11 @@ importers: examples/lit/basic-subscribe: dependencies: '@faker-js/faker': - specifier: ^10.4.0 + specifier: ^10.5.0 version: 10.5.0 '@tanstack/lit-store': - specifier: ^0.13.2 - version: 0.13.2(lit@3.3.3) + specifier: ^0.14.0 + version: 0.14.0(lit@3.3.3) '@tanstack/lit-table': specifier: workspace:* version: link:../../../packages/lit-table @@ -1959,7 +1959,7 @@ importers: specifier: 6.0.3 version: 6.0.3 vite: - specifier: ^8.0.16 + 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: @@ -13889,11 +13889,6 @@ packages: resolution: {integrity: sha512-Apyfkf0O8QqHQJjy/cB1QW2GpYTOyjf98nFAaYtzjBc0V5YzJoy0wnmZSLvwBJxE8/Hhy0Bsa+EPt/fdeJyXkw==} hasBin: true - '@tanstack/lit-store@0.13.2': - resolution: {integrity: sha512-uAa5gQbmPOESokHM5t+AhQ3Ye8S2/bN+PB6CHKjHXixNN6aMDSQEtHoguPoMb3a3q/NeJ2NdiseyoUg4vsh/ng==} - peerDependencies: - lit: ^3.0.0 - '@tanstack/lit-store@0.14.0': resolution: {integrity: sha512-Y7aS5V8b5IdAhJQ+Jx+pNAuHLIyuZlI/qybIEFBOXVivsx/GSYUuQNCesLoiEH9RrBi8+pU5D+UeHPmpnNSIQg==} peerDependencies: @@ -24084,11 +24079,6 @@ snapshots: std-env: 4.1.0 yaml: 2.9.0 - '@tanstack/lit-store@0.13.2(lit@3.3.3)': - dependencies: - '@tanstack/store': 0.11.0 - lit: 3.3.3 - '@tanstack/lit-store@0.14.0(lit@3.3.3)': dependencies: '@tanstack/store': 0.11.0 From c6710d4150b542ada014c418dc30d1bf0c33d9ad Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Mon, 29 Jun 2026 08:39:14 -0500 Subject: [PATCH 4/4] regen docs --- examples/lit/basic-subscribe/src/main.ts | 90 +++++++++---------- .../solid/column-pinning-sticky/src/App.tsx | 22 +++-- .../column-resizing-performant/src/App.tsx | 24 +++-- 3 files changed, 65 insertions(+), 71 deletions(-) diff --git a/examples/lit/basic-subscribe/src/main.ts b/examples/lit/basic-subscribe/src/main.ts index 062385d5dc..c44c03229b 100644 --- a/examples/lit/basic-subscribe/src/main.ts +++ b/examples/lit/basic-subscribe/src/main.ts @@ -154,57 +154,55 @@ class LitTableExample extends LitElement { .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` + return subscribe(table.atoms.columnFilters, () => + typeof firstValue === 'number' + ? html` +
{ - column.setFilterValue( - (e.currentTarget as HTMLInputElement).value, - ) + const value = (e.currentTarget as HTMLInputElement).value + column.setFilterValue((old: [unknown, unknown]) => [ + value, + old?.[1], + ]) }} - placeholder="Search..." + 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" + /> + `, ) } diff --git a/examples/solid/column-pinning-sticky/src/App.tsx b/examples/solid/column-pinning-sticky/src/App.tsx index 2f740ae5e0..4ae234a4d8 100644 --- a/examples/solid/column-pinning-sticky/src/App.tsx +++ b/examples/solid/column-pinning-sticky/src/App.tsx @@ -98,19 +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() }, - ) + debugTable: true, + debugHeaders: true, + debugColumns: true, + columnResizeMode: 'onChange', + }) const randomizeColumns = () => { table.setColumnOrder( diff --git a/examples/solid/column-resizing-performant/src/App.tsx b/examples/solid/column-resizing-performant/src/App.tsx index 82ca72bb74..03c36adcc7 100644 --- a/examples/solid/column-resizing-performant/src/App.tsx +++ b/examples/solid/column-resizing-performant/src/App.tsx @@ -60,20 +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() }, - ) + defaultColumn: { minSize: 60, maxSize: 800 }, + columnResizeMode: 'onChange', + debugTable: true, + debugHeaders: true, + debugColumns: true, + }) const columnSizeVars = createMemo(() => { const headers = table.getFlatHeaders()