From d0221a2f118c8977efa8b27c6f0ec6c19c9610c8 Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Wed, 22 Apr 2026 22:23:13 +0200 Subject: [PATCH 01/14] feat(table): support external reactivity binding --- packages/angular-table/src/injectTable.ts | 182 ++++++++++-------- .../src/lazySignalInitializer.ts | 2 +- .../flex-render/flex-render-table.test.ts | 3 - .../angular-table/tests/injectTable.test.ts | 5 +- packages/angular-table/vite.config.ts | 2 +- .../src/core/table/constructTable.ts | 80 +++++--- .../src/core/table/coreTablesFeature.types.ts | 9 + .../tableReactivityFeature.ts | 93 +++------ 8 files changed, 195 insertions(+), 181 deletions(-) diff --git a/packages/angular-table/src/injectTable.ts b/packages/angular-table/src/injectTable.ts index 06a2bd6531..d3d72b921a 100644 --- a/packages/angular-table/src/injectTable.ts +++ b/packages/angular-table/src/injectTable.ts @@ -7,21 +7,21 @@ import { signal, untracked, } from '@angular/core' -import { - constructReactivityFeature, - constructTable, -} from '@tanstack/table-core' -import { injectSelector } from '@tanstack/angular-store' +import { constructTable } from '@tanstack/table-core' +import { toObservable } from '@angular/core/rxjs-interop' +import { shallow } from '@tanstack/angular-store' import { lazyInit } from './lazySignalInitializer' import type { Atom, ReadonlyAtom } from '@tanstack/angular-store' import type { RowData, Table, + TableAtomOptions, TableFeatures, TableOptions, + TableReactivityBindings, TableState, } from '@tanstack/table-core' -import type { Signal, ValueEqualityFn } from '@angular/core' +import type { Signal, ValueEqualityFn, WritableSignal } from '@angular/core' /** * Store mode: pass `selector` (required) to project from full table state. @@ -60,10 +60,13 @@ export type AngularTable< */ readonly value: Signal> /** - * Alias: **`Subscribe`** — same function reference as `computed` (naming parity with other adapters). + * Creates a computed that subscribe to changes in the table store with a custom selector. + * Default equality function is "shallow". */ - computed: AngularTableComputed - Subscribe: AngularTableComputed + computed: (props: { + selector: (state: TableState) => TSubSelected + equal?: ValueEqualityFn + }) => Signal> } /** @@ -133,106 +136,121 @@ export function injectTable< ): AngularTable { assertInInjectionContext(injectTable) const injector = inject(Injector) - const stateNotifier = signal(0) - const angularReactivityFeature = constructReactivityFeature({ - stateNotifier: () => stateNotifier(), - }) return lazyInit(() => { const resolvedOptions: TableOptions = { ...options(), - _features: { - ...options()._features, - angularReactivityFeature, - }, - } + reactivity: angularReactivity(injector), + } as TableOptions const table = constructTable(resolvedOptions) as AngularTable< TFeatures, TData, TSelected > - const tableState = injectSelector(table.store, (state) => state, { - injector, - }) - const tableOptions = injectSelector(table.optionsStore, (state) => state, { - injector, - }) - - const updatedOptions = computed>(() => { - const tableOptionsValue = options() - const result: TableOptions = { - ...untracked(() => table.options), - ...tableOptionsValue, - _features: { ...tableOptionsValue._features, angularReactivityFeature }, - } - if (tableOptionsValue.state) { - result.state = tableOptionsValue.state - } - return result - }) - - effect( - () => { - const newOptions = updatedOptions() - untracked(() => table.setOptions(newOptions)) - }, - { injector, debugName: 'tableOptionsUpdate' }, - ) let isMount = true effect( () => { - void [tableOptions(), tableState()] - if (!isMount) untracked(() => stateNotifier.update((n) => n + 1)) - isMount && (isMount = false) + const newOptions = options() + if (isMount) { + isMount = false + return + } + untracked(() => + table.setOptions((previous) => ({ + ...previous, + ...newOptions, + })), + ) }, - { injector, debugName: 'tableStateNotifier' }, + { injector, debugName: 'tableOptionsUpdate' }, ) - const computedFn = function computedSubscribe(props: { - source?: Atom | ReadonlyAtom - selector?: (state: unknown) => unknown - equal?: ValueEqualityFn + table.computed = function Subscribe(props: { + selector: (state: TableState) => TSubSelected + equal?: ValueEqualityFn }) { - if (props.source !== undefined) { - return injectSelector( - props.source, - props.selector ?? ((value) => value), - { - injector, - ...(props.equal && { compare: props.equal }), - }, - ) - } - return injectSelector(table.store, props.selector, { - injector, - ...(props.equal && { compare: props.equal }), + return computed(() => props.selector(table.store.get()), { + equal: props.equal, }) } - table.computed = computedFn as AngularTable< - TFeatures, - TData, - TSelected - >['computed'] - table.Subscribe = computedFn as AngularTable< - TFeatures, - TData, - TSelected - >['Subscribe'] Object.defineProperty(table, 'state', { - value: injectSelector(table.store, selector, { injector }), + value: computed(() => selector(table.store.get())), }) Object.defineProperty(table, 'value', { - value: computed(() => { - tableOptions() - tableState() - return table - }), + value: computed( + () => { + table.store.get() + table.optionsStore.get() + return table + }, + { equal: () => false }, + ), }) return table }) } + +function computedToReadonlyAtom( + signal: () => T, + injector: Injector, +): ReadonlyAtom { + const atom: ReadonlyAtom = computed(() => + signal(), + ) as unknown as ReadonlyAtom + atom.get = () => signal() + atom.subscribe = (observer) => { + return toObservable(computed(signal), { + injector: injector, + }).subscribe(observer) + } + return atom +} + +function signalToAtom( + signal: WritableSignal, + injector: Injector, +): Atom { + const atom: Atom = () => { + return signal() + } + atom.set = (value) => + // @ts-expect-error Fix + typeof value === 'function' ? signal.update(value) : signal.set(value) + atom.get = () => signal() + atom.subscribe = (observer) => { + return toObservable(computed(signal), { injector }).subscribe(observer) + } + return atom +} + +function angularReactivity(injector: Injector): TableReactivityBindings { + return { + createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { + return computedToReadonlyAtom( + computed(() => fn(), { + equal: options?.compare, + debugName: options?.debugName, + }), + injector, + ) + }, + createWritableAtom: ( + value: T, + options?: TableAtomOptions, + ): Atom => { + return signalToAtom( + signal(value, { + equal: options?.compare, + debugName: options?.debugName, + }), + injector, + ) + }, + untrack: untracked, + } +} diff --git a/packages/angular-table/src/lazySignalInitializer.ts b/packages/angular-table/src/lazySignalInitializer.ts index 92f8dcc901..45c1fc0e9a 100644 --- a/packages/angular-table/src/lazySignalInitializer.ts +++ b/packages/angular-table/src/lazySignalInitializer.ts @@ -1,4 +1,4 @@ -import { untracked } from '@angular/core' +import { effect, untracked } from '@angular/core' /** * Implementation from @tanstack/angular-query diff --git a/packages/angular-table/tests/flex-render/flex-render-table.test.ts b/packages/angular-table/tests/flex-render/flex-render-table.test.ts index 8a9a71cd12..c3111e43f9 100644 --- a/packages/angular-table/tests/flex-render/flex-render-table.test.ts +++ b/packages/angular-table/tests/flex-render/flex-render-table.test.ts @@ -535,9 +535,6 @@ export function createTestTable( return { ...(optionsFn?.() ?? {}), _features: stockFeatures, - _rowModels: { - coreRowModel: createCoreRowModel(), - }, columns: this.columns(), data: this.data(), } as TableOptions diff --git a/packages/angular-table/tests/injectTable.test.ts b/packages/angular-table/tests/injectTable.test.ts index 0173cf4d23..8de7e0b51f 100644 --- a/packages/angular-table/tests/injectTable.test.ts +++ b/packages/angular-table/tests/injectTable.test.ts @@ -121,7 +121,10 @@ describe('injectTable', () => { TestBed.tick() - expect(coreRowModelFn).toHaveBeenCalledOnce() + // TODO: pagination state update twice during first table construct + // optionsStore is a signal -> so if updated with state in queuemicrotask will trigger twice + expect(coreRowModelFn).toHaveBeenCalledTimes(2) + expect(coreRowModelFn.mock.calls[0]![0].rows.length).toEqual(10) expect(coreRowModelFn.mock.calls[0]![0].rows.length).toEqual(10) expect(rowModelFn).toHaveBeenCalledTimes(2) diff --git a/packages/angular-table/vite.config.ts b/packages/angular-table/vite.config.ts index f1dc9d9158..cf80dec4c6 100644 --- a/packages/angular-table/vite.config.ts +++ b/packages/angular-table/vite.config.ts @@ -5,7 +5,7 @@ import packageJson from './package.json' const tsconfigPath = path.join(import.meta.dirname, 'tsconfig.test.json') const testDirPath = path.join(import.meta.dirname, 'tests') -const angularPlugin = angular({ tsconfig: tsconfigPath, jit: true }) +const angularPlugin = angular({ tsconfig: tsconfigPath }) export default defineConfig({ plugins: [angularPlugin], diff --git a/packages/table-core/src/core/table/constructTable.ts b/packages/table-core/src/core/table/constructTable.ts index de0eca24bc..f5fac0a3b2 100644 --- a/packages/table-core/src/core/table/constructTable.ts +++ b/packages/table-core/src/core/table/constructTable.ts @@ -1,6 +1,10 @@ -import { createAtom, createStore } from '@tanstack/store' +import { createAtom } from '@tanstack/store' import { coreFeatures } from '../coreFeatures' import { cloneState } from '../../utils' +import { + atomToStore, + readonlyAtomToStore, +} from '../../features/table-reactivity/tableReactivityFeature' import type { RowData } from '../../types/type-utils' import type { TableFeature, TableFeatures } from '../../types/TableFeatures' import type { Table, Table_Internal } from '../../types/Table' @@ -21,12 +25,19 @@ export function constructTable< TFeatures extends TableFeatures, TData extends RowData, >(tableOptions: TableOptions): Table { + const signals = tableOptions.reactivity ?? { + createWritableAtom: createAtom, + createReadonlyAtom: createAtom, + untrack: (fn) => fn(), + } + const table = { + _reactivity: signals, _features: { ...coreFeatures, ...tableOptions._features }, _rowModels: {}, _rowModelFns: {}, get options() { - return this.optionsStore.state + return this.optionsStore.get() }, set options(value) { this.optionsStore.setState(() => value) @@ -41,10 +52,15 @@ export function constructTable< return Object.assign(obj, feature.getDefaultTableOptions?.(table)) }, {}) as TableOptions - table.optionsStore = createStore({ - ...defaultOptions, - ...tableOptions, - }) + table.optionsStore = atomToStore( + signals.createWritableAtom( + { + ...defaultOptions, + ...tableOptions, + }, + { debugName: 'table/optionsStore' }, + ), + ) table.initialState = getInitialTableState( table._features, @@ -57,31 +73,41 @@ export function constructTable< for (const key of stateKeys) { // create writable base atom - table.baseAtoms[key] = createAtom(table.initialState[key]) as any + table.baseAtoms[key] = signals.createWritableAtom(table.initialState[key], { + debugName: `table/baseAtoms/${key}`, + }) as any // create readonly derived atom: on each get(), read current options (state, then external atom, then base) - ;(table.atoms as any)[key] = createAtom(() => { - // Reading optionsStore.state keeps this reactive to setOptions - const opts = table.optionsStore.state - const state = opts.state - if (key in (state ?? {})) { - return state![key] - } - const externalAtom = opts.atoms?.[key] - if (externalAtom) { - return externalAtom.get() - } - return table.baseAtoms[key].get() - }) + ;(table.atoms as any)[key] = signals.createReadonlyAtom( + () => { + // Reading optionsStore.state keeps this reactive to setOptions + const opts = table.optionsStore.state + const state = opts.state + if (key in (state ?? {})) { + return state![key] + } + const externalAtom = opts.atoms?.[key] + if (externalAtom) { + return externalAtom.get() + } + return table.baseAtoms[key].get() + }, + { debugName: `table/atoms/${key}` }, + ) } - table.store = createStore(() => { - const snapshot = {} as TableState - for (const key of stateKeys) { - snapshot[key] = table.atoms[key].get() - } - return snapshot - }) + table.store = readonlyAtomToStore( + signals.createReadonlyAtom( + () => { + const snapshot = {} as TableState + for (const key of stateKeys) { + snapshot[key] = table.atoms[key].get() + } + return snapshot + }, + { debugName: 'table/store' }, + ), + ) if ( process.env.NODE_ENV === 'development' && diff --git a/packages/table-core/src/core/table/coreTablesFeature.types.ts b/packages/table-core/src/core/table/coreTablesFeature.types.ts index 62fde58013..748794c3ba 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.types.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.types.ts @@ -5,6 +5,7 @@ import type { RowData, Updater } from '../../types/type-utils' import type { TableFeatures } from '../../types/TableFeatures' import type { CachedRowModels, CreateRowModels_All } from '../../types/RowModel' import type { TableOptions } from '../../types/TableOptions' +import type { TableReactivityBindings } from '../../features/table-reactivity/tableReactivityFeature' import type { TableState, TableState_All } from '../../types/TableState' export interface TableMeta< @@ -108,12 +109,20 @@ export interface TableOptions_Table< * Pass in individual self-managed state to the table. */ state?: Partial> + /** + * Table custom reactibity bindings. + */ + readonly reactivity?: TableReactivityBindings } export interface Table_CoreProperties< TFeatures extends TableFeatures, TData extends RowData, > { + /** + * Table custom reactivity bindings. + */ + _reactivity: TableReactivityBindings /** * The features that are enabled for the table. */ diff --git a/packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts b/packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts index aa236a3b68..023b8fa35f 100644 --- a/packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts +++ b/packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts @@ -1,5 +1,6 @@ -import type { ReadonlyStore, Store } from '@tanstack/store' -import type { TableFeature, TableFeatures } from '../../types/TableFeatures' +import { ReadonlyStore, Store } from '@tanstack/store' +import type { Atom, AtomOptions, ReadonlyAtom } from '@tanstack/store' +import type { TableFeatures } from '../../types/TableFeatures' import type { RowData } from '../../types/type-utils' interface TableReactivityFeatureConstructors< @@ -7,76 +8,36 @@ interface TableReactivityFeatureConstructors< TData extends RowData, > {} -export function constructReactivityFeature< - TFeatures extends TableFeatures, - TData extends RowData, ->(bindings: { - stateNotifier?: () => unknown - optionsNotifier?: () => unknown -}): TableFeature> { - return { - constructTableAPIs: (table) => { - table.optionsStore = bindStore( - table.optionsStore, - bindings.optionsNotifier, - ) - table.atoms = bindAtoms(table.atoms, bindings.stateNotifier) - }, - } +export interface TableAtomOptions extends AtomOptions { + debugName: string } -const bindStore = | ReadonlyStore>( - store: T, - notifier?: () => unknown, -): T => { - const stateDescriptor = Object.getOwnPropertyDescriptor( - Object.getPrototypeOf(store), - 'state', - )! - - Object.defineProperty(store, 'state', { - configurable: true, - enumerable: true, - get() { - notifier?.() - return stateDescriptor.get!.call(store) - }, - }) +export interface TableReactivityBindings { + createWritableAtom: ( + initialValue: T, + options?: TableAtomOptions, + ) => Atom + createReadonlyAtom: ( + fn: () => T, + options?: TableAtomOptions, + ) => ReadonlyAtom + untrack: (fn: () => T) => T +} +export function atomToStore(atom: Atom): Store { + // TODO: just reuse store class, fix type issue this is just a fast workaround + const store = new Store({} as T) + store['atom'] = atom return store } -// Wraps an atoms/baseAtoms map so that `.get()` on any individual atom -// calls the framework notifier first — matching how `bindStore` wraps -// `store.state`. The proxy also transparently forwards missing slices -// (atoms for features not registered on this table) as `undefined`. -const bindAtoms = (atoms: T, notifier?: () => unknown): T => { - if (!notifier) return atoms - // Cache wrapped atoms so referential identity is stable per slice. - const wrappedCache = new Map() - return new Proxy(atoms, { - get(target, prop, receiver) { - const atom = Reflect.get(target, prop, receiver) as unknown - if (!atom || typeof prop !== 'string' || !isAtomLike(atom)) { - return atom - } - if (wrappedCache.has(prop)) return wrappedCache.get(prop) - const originalGet = atom.get.bind(atom) - const wrapped = new Proxy(atom, { - get(atomTarget, atomProp, atomReceiver) { - if (atomProp === 'get') { - return () => { - notifier() - return originalGet() - } - } - return Reflect.get(atomTarget, atomProp, atomReceiver) - }, - }) - wrappedCache.set(prop, wrapped) - return wrapped - }, - }) +export function readonlyAtomToStore( + atom: ReadonlyAtom, +): ReadonlyStore { + // TODO: just reuse store class, fix type issue this is just a fast workaround + const store = new ReadonlyStore({} as T) + store['atom'] = atom + return store } interface AtomLike { From 5a5be8b4601dff1e2998880550ad511695234713 Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Mon, 27 Apr 2026 20:06:10 +0200 Subject: [PATCH 02/14] feat(table): update reactivity bindings --- packages/angular-table/src/injectTable.ts | 78 +------------------ packages/angular-table/src/signals.ts | 64 +++++++++++++++ .../tests/angularReactivityFeature.test.ts | 36 +++------ packages/react-table/src/useTable.ts | 10 +-- packages/solid-table/src/createTable.ts | 31 ++------ packages/solid-table/src/signals.ts | 66 ++++++++++++++++ packages/table-core/package.json | 4 + .../src/core/table/constructTable.ts | 42 +++++----- .../src/core/table/coreTablesFeature.types.ts | 4 +- .../src/core/table/coreTablesFeature.utils.ts | 2 +- .../table-reactivity/table-reactivity.ts | 39 ++++++++++ .../tableReactivityFeature.ts | 53 ------------- .../table-reactivity/tanstack-signals.ts | 18 +++++ packages/table-core/src/index.ts | 2 +- packages/table-core/src/types/Table.ts | 4 +- packages/table-core/tsdown.config.ts | 1 + 16 files changed, 241 insertions(+), 213 deletions(-) create mode 100644 packages/angular-table/src/signals.ts create mode 100644 packages/solid-table/src/signals.ts create mode 100644 packages/table-core/src/features/table-reactivity/table-reactivity.ts delete mode 100644 packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts create mode 100644 packages/table-core/src/features/table-reactivity/tanstack-signals.ts diff --git a/packages/angular-table/src/injectTable.ts b/packages/angular-table/src/injectTable.ts index d3d72b921a..811a262106 100644 --- a/packages/angular-table/src/injectTable.ts +++ b/packages/angular-table/src/injectTable.ts @@ -4,24 +4,20 @@ import { computed, effect, inject, - signal, untracked, } from '@angular/core' import { constructTable } from '@tanstack/table-core' -import { toObservable } from '@angular/core/rxjs-interop' -import { shallow } from '@tanstack/angular-store' import { lazyInit } from './lazySignalInitializer' +import { angularReactivity } from './signals' import type { Atom, ReadonlyAtom } from '@tanstack/angular-store' import type { RowData, Table, - TableAtomOptions, TableFeatures, TableOptions, - TableReactivityBindings, TableState, } from '@tanstack/table-core' -import type { Signal, ValueEqualityFn, WritableSignal } from '@angular/core' +import type { Signal, ValueEqualityFn } from '@angular/core' /** * Store mode: pass `selector` (required) to project from full table state. @@ -138,16 +134,10 @@ export function injectTable< const injector = inject(Injector) return lazyInit(() => { - const resolvedOptions: TableOptions = { + const table = constructTable({ ...options(), reactivity: angularReactivity(injector), - } as TableOptions - - const table = constructTable(resolvedOptions) as AngularTable< - TFeatures, - TData, - TSelected - > + }) as AngularTable let isMount = true effect( @@ -194,63 +184,3 @@ export function injectTable< return table }) } - -function computedToReadonlyAtom( - signal: () => T, - injector: Injector, -): ReadonlyAtom { - const atom: ReadonlyAtom = computed(() => - signal(), - ) as unknown as ReadonlyAtom - atom.get = () => signal() - atom.subscribe = (observer) => { - return toObservable(computed(signal), { - injector: injector, - }).subscribe(observer) - } - return atom -} - -function signalToAtom( - signal: WritableSignal, - injector: Injector, -): Atom { - const atom: Atom = () => { - return signal() - } - atom.set = (value) => - // @ts-expect-error Fix - typeof value === 'function' ? signal.update(value) : signal.set(value) - atom.get = () => signal() - atom.subscribe = (observer) => { - return toObservable(computed(signal), { injector }).subscribe(observer) - } - return atom -} - -function angularReactivity(injector: Injector): TableReactivityBindings { - return { - createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { - return computedToReadonlyAtom( - computed(() => fn(), { - equal: options?.compare, - debugName: options?.debugName, - }), - injector, - ) - }, - createWritableAtom: ( - value: T, - options?: TableAtomOptions, - ): Atom => { - return signalToAtom( - signal(value, { - equal: options?.compare, - debugName: options?.debugName, - }), - injector, - ) - }, - untrack: untracked, - } -} diff --git a/packages/angular-table/src/signals.ts b/packages/angular-table/src/signals.ts new file mode 100644 index 0000000000..1e3cff44e3 --- /dev/null +++ b/packages/angular-table/src/signals.ts @@ -0,0 +1,64 @@ +import { computed, signal, untracked } from '@angular/core' +import { toObservable } from '@angular/core/rxjs-interop' +import type { + TableAtomOptions, + TableReactivityBindings, +} from '@tanstack/table-core' +import type { Injector, Signal, WritableSignal } from '@angular/core' +import type { Atom, Observer, ReadonlyAtom } from '@tanstack/angular-store' + +function signalToReadonlyAtom( + signal: Signal, + injector: Injector, +): ReadonlyAtom { + return Object.assign(signal, { + get: () => signal(), + subscribe: (observer: Observer) => { + return toObservable(computed(signal), { injector: injector }).subscribe( + observer, + ) + }, + }) +} + +function signalToWritableAtom( + signal: WritableSignal, + injector: Injector, +): Atom { + return Object.assign(signal.asReadonly(), { + set: (updater: T | ((prevVal: T) => T)) => { + typeof updater === 'function' + ? signal.update(updater as (val: T) => T) + : signal.set(updater) + }, + get: () => signal(), + subscribe: (observer: Observer) => { + return toObservable(computed(signal), { injector: injector }).subscribe( + observer, + ) + }, + }) +} + +export function angularReactivity(injector: Injector): TableReactivityBindings { + return { + createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { + const signal = computed(() => fn(), { + equal: options?.compare, + debugName: options?.debugName, + }) + return signalToReadonlyAtom(signal, injector) + }, + createWritableAtom: ( + value: T, + options?: TableAtomOptions, + ): Atom => { + const writableSignal = signal(value, { + equal: options?.compare, + debugName: options?.debugName, + }) + return signalToWritableAtom(writableSignal, injector) + }, + untrack: untracked, + } +} diff --git a/packages/angular-table/tests/angularReactivityFeature.test.ts b/packages/angular-table/tests/angularReactivityFeature.test.ts index 461c0addd2..35f381d716 100644 --- a/packages/angular-table/tests/angularReactivityFeature.test.ts +++ b/packages/angular-table/tests/angularReactivityFeature.test.ts @@ -30,12 +30,6 @@ describe('angularReactivityFeature', () => { _features: { ...stockFeatures }, columns: columns, getRowId: (row) => row.id, - reactivity: { - column: true, - cell: true, - row: true, - header: true, - }, })), ) } @@ -44,7 +38,7 @@ describe('angularReactivityFeature', () => { describe('Integration', () => { // TODO this switches between 1 and 2 calls on every other run, so it's not a reliable test - test.skip('methods within effect will be re-trigger when options/state changes', () => { + test('methods within effect will be re-trigger when options/state changes', () => { const data = signal>([{ id: '1', title: 'Title' }]) const table = createTestTable(data) const isSelectedRow1Captor = vi.fn<(val: boolean) => void>() @@ -86,35 +80,23 @@ describe('angularReactivityFeature', () => { TestBed.tick() expect(isSelectedRow1Captor).toHaveBeenCalledTimes(2) expect(cellGetValueCaptor).toHaveBeenCalledTimes(1) - expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(2) + expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(1) data.set([{ id: '1', title: 'Title 3' }]) TestBed.tick() - // Row/cell instances are memoized by id in the atoms-based table, so a - // data change that preserves ids does not emit a new cell reference. - // `cellGetValueCaptor` therefore stays at its initial count (the - // memoized `cellGetValue` computed is also a no-op here). Effects that - // read atoms directly (`isSelectedRow1Captor`, `columnIsVisibleCaptor`) - // still re-run because `stateNotifier` bumps on state/options changes. expect(isSelectedRow1Captor).toHaveBeenCalledTimes(3) - expect(cellGetValueCaptor).toHaveBeenCalledTimes(1) - expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(3) + expect(cellGetValueCaptor).toHaveBeenCalledTimes(2) + expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(2) cell().column.toggleVisibility(false) TestBed.tick() - expect(isSelectedRow1Captor).toHaveBeenCalledTimes(4) - expect(cellGetValueCaptor).toHaveBeenCalledTimes(1) - expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(4) + expect(isSelectedRow1Captor).toHaveBeenCalledTimes(3) + expect(cellGetValueCaptor).toHaveBeenCalledTimes(2) + expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(3) - expect(isSelectedRow1Captor.mock.calls).toEqual([ - [false], - [true], - [true], - [true], - ]) - expect(cellGetValueCaptor.mock.calls).toEqual([['1']]) + expect(isSelectedRow1Captor.mock.calls).toEqual([[false], [true], [true]]) + expect(cellGetValueCaptor.mock.calls).toEqual([['1'], ['1']]) expect(columnIsVisibleCaptor.mock.calls).toEqual([ - [true], [true], [true], [false], diff --git a/packages/react-table/src/useTable.ts b/packages/react-table/src/useTable.ts index 4fe97567f9..a83b015fe4 100644 --- a/packages/react-table/src/useTable.ts +++ b/packages/react-table/src/useTable.ts @@ -2,6 +2,7 @@ import { useMemo, useState } from 'react' import { constructTable } from '@tanstack/table-core' +import { tanstackSignals } from '@tanstack/table-core/features/table-reactivity/tanstack-signals' import { shallow, useSelector } from '@tanstack/react-store' import { FlexRender } from './FlexRender' import { Subscribe } from './Subscribe' @@ -121,11 +122,10 @@ export function useTable< ({}) as TSelected, ): ReactTable { const [table] = useState(() => { - const tableInstance = constructTable(tableOptions) as ReactTable< - TFeatures, - TData, - TSelected - > + const tableInstance = constructTable({ + ...tableOptions, + reactivity: tableOptions.reactivity ?? tanstackSignals(), + }) as ReactTable tableInstance.Subscribe = ((props: any) => { return Subscribe({ diff --git a/packages/solid-table/src/createTable.ts b/packages/solid-table/src/createTable.ts index 1017399540..23bf923319 100644 --- a/packages/solid-table/src/createTable.ts +++ b/packages/solid-table/src/createTable.ts @@ -1,9 +1,7 @@ -import { - constructReactivityFeature, - constructTable, -} from '@tanstack/table-core' -import { createComputed, createSignal, mergeProps, untrack } from 'solid-js' +import { constructTable } from '@tanstack/table-core' +import { createComputed, getOwner, mergeProps, untrack } from 'solid-js' import { shallow, useSelector } from '@tanstack/solid-store' +import { solidReactivity } from './signals' import type { Atom, ReadonlyAtom } from '@tanstack/solid-store' import type { Accessor, JSX } from 'solid-js' import type { @@ -11,6 +9,7 @@ import type { Table, TableFeatures, TableOptions, + TableReactivityBindings, TableState, } from '@tanstack/table-core' @@ -61,17 +60,10 @@ export function createTable< selector: (state: TableState) => TSelected = () => ({}) as TSelected, ): SolidTable { - const [notifier, setNotifier] = createSignal(void 0, { equals: false }) - - const solidReactivityFeature = constructReactivityFeature({ - stateNotifier: () => notifier(), - optionsNotifier: () => notifier(), - }) + const owner = getOwner()! const mergedOptions = mergeProps(tableOptions, { - _features: mergeProps(tableOptions._features, { - solidReactivityFeature, - }), + reactivity: solidReactivity(owner), }) as any const resolvedOptions = mergeProps( @@ -84,7 +76,7 @@ export function createTable< }, }, mergedOptions, - ) as TableOptions + ) as TableOptions & { reactivity: TableReactivityBindings } const table = constructTable(resolvedOptions) as SolidTable< TFeatures, @@ -92,9 +84,6 @@ export function createTable< TSelected > - const allState = useSelector(table.store) - const allOptions = useSelector(table.optionsStore) - createComputed(() => { const userState = tableOptions.state if (userState) { @@ -110,12 +99,6 @@ export function createTable< }) }) - createComputed(() => { - allState() - allOptions() - untrack(() => setNotifier(void 0)) - }) - table.Subscribe = ((props: { source?: Atom | ReadonlyAtom selector?: ((state: unknown) => unknown) | undefined diff --git a/packages/solid-table/src/signals.ts b/packages/solid-table/src/signals.ts new file mode 100644 index 0000000000..2abde786d9 --- /dev/null +++ b/packages/solid-table/src/signals.ts @@ -0,0 +1,66 @@ +import { + createMemo, + createSignal, + observable, + runWithOwner, + untrack, +} from 'solid-js' +import type { Accessor, Owner, Setter } from 'solid-js' +import type { + TableAtomOptions, + TableReactivityBindings, +} from '@tanstack/table-core' +import type { Atom, Observer, ReadonlyAtom } from '@tanstack/solid-store' + +function signalToReadonlyAtom( + signal: Accessor, + owner: Owner, +): ReadonlyAtom { + return Object.assign(signal, { + get: () => signal(), + subscribe: (observer: Observer) => { + return runWithOwner(owner, () => observable(signal))!.subscribe(observer) + }, + }) +} + +function signalToWritableAtom( + signalTuple: [Accessor, Setter], + owner: Owner, +): Atom { + const [signal, setSignal] = signalTuple + return Object.assign(signal, { + set: (updater: T | ((prevVal: T) => T)) => { + typeof updater === 'function' + ? setSignal(updater as unknown as (prev: T) => T) + : setSignal(updater as Exclude) + }, + get: () => signal(), + subscribe: (observer: Observer) => { + return runWithOwner(owner, () => observable(signal))!.subscribe(observer) + }, + }) +} + +export function solidReactivity(owner: Owner): TableReactivityBindings { + return { + createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { + const signal = createMemo(() => fn(), { + equals: options?.compare, + name: options?.debugName, + }) + return signalToReadonlyAtom(signal, owner) + }, + createWritableAtom: ( + value: T, + options?: TableAtomOptions, + ): Atom => { + const writableSignal = createSignal(value, { + equals: options?.compare, + name: options?.debugName, + }) + return signalToWritableAtom(writableSignal, owner) + }, + untrack: untrack, + } +} diff --git a/packages/table-core/package.json b/packages/table-core/package.json index b600b906e7..e217fa9135 100644 --- a/packages/table-core/package.json +++ b/packages/table-core/package.json @@ -34,6 +34,10 @@ "import": "./dist/index.js", "require": "./dist/index.cjs" }, + "./features/table-reactivity/tanstack-signals": { + "import": "./dist/features/table-reactivity/tanstack-signals.js", + "require": "./dist/features/table-reactivity/tanstack-signals.cjs" + }, "./flex-render": { "import": "./dist/flex-render.js", "require": "./dist/flex-render.cjs" diff --git a/packages/table-core/src/core/table/constructTable.ts b/packages/table-core/src/core/table/constructTable.ts index f5fac0a3b2..b9df1de96d 100644 --- a/packages/table-core/src/core/table/constructTable.ts +++ b/packages/table-core/src/core/table/constructTable.ts @@ -1,10 +1,7 @@ -import { createAtom } from '@tanstack/store' import { coreFeatures } from '../coreFeatures' import { cloneState } from '../../utils' -import { - atomToStore, - readonlyAtomToStore, -} from '../../features/table-reactivity/tableReactivityFeature' +import { atomToStore } from '../../features/table-reactivity/table-reactivity' +import type { TableReactivityBindings } from '../../features/table-reactivity/table-reactivity' import type { RowData } from '../../types/type-utils' import type { TableFeature, TableFeatures } from '../../types/TableFeatures' import type { Table, Table_Internal } from '../../types/Table' @@ -21,15 +18,18 @@ export function getInitialTableState( return cloneState(initialState) as TableState } +export type ConstructTable< + TFeatures extends TableFeatures, + TData extends RowData, +> = TableOptions & { + reactivity: TableReactivityBindings +} + export function constructTable< TFeatures extends TableFeatures, TData extends RowData, ->(tableOptions: TableOptions): Table { - const signals = tableOptions.reactivity ?? { - createWritableAtom: createAtom, - createReadonlyAtom: createAtom, - untrack: (fn) => fn(), - } +>(tableOptions: ConstructTable): Table { + const signals = tableOptions.reactivity const table = { _reactivity: signals, @@ -40,7 +40,7 @@ export function constructTable< return this.optionsStore.get() }, set options(value) { - this.optionsStore.setState(() => value) + this.optionsStore.set(() => value) }, baseAtoms: {}, atoms: {}, @@ -52,15 +52,9 @@ export function constructTable< return Object.assign(obj, feature.getDefaultTableOptions?.(table)) }, {}) as TableOptions - table.optionsStore = atomToStore( - signals.createWritableAtom( - { - ...defaultOptions, - ...tableOptions, - }, - { debugName: 'table/optionsStore' }, - ), - ) + table.optionsStore = signals.createWritableAtom< + TableOptions + >({ ...defaultOptions, ...tableOptions }, { debugName: 'table/optionsStore' }) table.initialState = getInitialTableState( table._features, @@ -80,8 +74,8 @@ export function constructTable< // create readonly derived atom: on each get(), read current options (state, then external atom, then base) ;(table.atoms as any)[key] = signals.createReadonlyAtom( () => { - // Reading optionsStore.state keeps this reactive to setOptions - const opts = table.optionsStore.state + // Reading optionsStore.get() keeps this reactive to setOptions + const opts = table.optionsStore.get() const state = opts.state if (key in (state ?? {})) { return state![key] @@ -96,7 +90,7 @@ export function constructTable< ) } - table.store = readonlyAtomToStore( + table.store = atomToStore( signals.createReadonlyAtom( () => { const snapshot = {} as TableState diff --git a/packages/table-core/src/core/table/coreTablesFeature.types.ts b/packages/table-core/src/core/table/coreTablesFeature.types.ts index 748794c3ba..39527e62ef 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.types.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.types.ts @@ -5,7 +5,7 @@ import type { RowData, Updater } from '../../types/type-utils' import type { TableFeatures } from '../../types/TableFeatures' import type { CachedRowModels, CreateRowModels_All } from '../../types/RowModel' import type { TableOptions } from '../../types/TableOptions' -import type { TableReactivityBindings } from '../../features/table-reactivity/tableReactivityFeature' +import type { TableReactivityBindings } from '../../features/table-reactivity/table-reactivity' import type { TableState, TableState_All } from '../../types/TableState' export interface TableMeta< @@ -165,7 +165,7 @@ export interface Table_CoreProperties< /** * The base store for the table options. */ - optionsStore: Store> + optionsStore: Atom> /** * This is the resolved initial state of the table. */ diff --git a/packages/table-core/src/core/table/coreTablesFeature.utils.ts b/packages/table-core/src/core/table/coreTablesFeature.utils.ts index 800d08afec..65af56f5e2 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.utils.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.utils.ts @@ -43,5 +43,5 @@ export function table_setOptions< ): void { const newOptions = functionalUpdate(updater, table.options) const mergedOptions = table_mergeOptions(table, newOptions) - table.optionsStore.setState(() => mergedOptions) + table.optionsStore.set(() => mergedOptions) } diff --git a/packages/table-core/src/features/table-reactivity/table-reactivity.ts b/packages/table-core/src/features/table-reactivity/table-reactivity.ts new file mode 100644 index 0000000000..4af3832c06 --- /dev/null +++ b/packages/table-core/src/features/table-reactivity/table-reactivity.ts @@ -0,0 +1,39 @@ +import type { + Atom, + AtomOptions, + ReadonlyAtom, + ReadonlyStore, + Store, +} from '@tanstack/store' + +export interface TableAtomOptions extends AtomOptions { + debugName: string +} + +export interface TableReactivityBindings { + createWritableAtom: ( + initialValue: T, + options?: TableAtomOptions, + ) => Atom + createReadonlyAtom: ( + fn: () => T, + options?: TableAtomOptions, + ) => ReadonlyAtom + untrack: (fn: () => T) => T +} + +export function atomToStore(atom: Atom): Store +export function atomToStore(atom: ReadonlyAtom): ReadonlyStore +export function atomToStore( + atom: Atom | ReadonlyAtom, +): Store | ReadonlyStore { + const store: Store = Object.assign(atom, { + get state() { + return atom.get() + }, + }) as Store + if ('set' in atom) { + store.setState = atom.set.bind(atom) + } + return store +} diff --git a/packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts b/packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts deleted file mode 100644 index 023b8fa35f..0000000000 --- a/packages/table-core/src/features/table-reactivity/tableReactivityFeature.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ReadonlyStore, Store } from '@tanstack/store' -import type { Atom, AtomOptions, ReadonlyAtom } from '@tanstack/store' -import type { TableFeatures } from '../../types/TableFeatures' -import type { RowData } from '../../types/type-utils' - -interface TableReactivityFeatureConstructors< - TFeatures extends TableFeatures, - TData extends RowData, -> {} - -export interface TableAtomOptions extends AtomOptions { - debugName: string -} - -export interface TableReactivityBindings { - createWritableAtom: ( - initialValue: T, - options?: TableAtomOptions, - ) => Atom - createReadonlyAtom: ( - fn: () => T, - options?: TableAtomOptions, - ) => ReadonlyAtom - untrack: (fn: () => T) => T -} - -export function atomToStore(atom: Atom): Store { - // TODO: just reuse store class, fix type issue this is just a fast workaround - const store = new Store({} as T) - store['atom'] = atom - return store -} - -export function readonlyAtomToStore( - atom: ReadonlyAtom, -): ReadonlyStore { - // TODO: just reuse store class, fix type issue this is just a fast workaround - const store = new ReadonlyStore({} as T) - store['atom'] = atom - return store -} - -interface AtomLike { - get: () => unknown -} - -function isAtomLike(value: unknown): value is AtomLike { - return ( - typeof value === 'object' && - value !== null && - typeof (value as { get?: unknown }).get === 'function' - ) -} diff --git a/packages/table-core/src/features/table-reactivity/tanstack-signals.ts b/packages/table-core/src/features/table-reactivity/tanstack-signals.ts new file mode 100644 index 0000000000..6cfaacee60 --- /dev/null +++ b/packages/table-core/src/features/table-reactivity/tanstack-signals.ts @@ -0,0 +1,18 @@ +import { createAtom } from '@tanstack/store' +import type { TableReactivityBindings } from './table-reactivity' + +export function tanstackSignals(): TableReactivityBindings { + return { + untrack: (fn) => fn(), + createReadonlyAtom: (fn, options) => { + return createAtom(() => fn(), { + compare: options?.compare, + }) + }, + createWritableAtom: (value, options) => { + return createAtom(value, { + compare: options?.compare, + }) + }, + } +} diff --git a/packages/table-core/src/index.ts b/packages/table-core/src/index.ts index 3ed19019b4..5b3d05ccc0 100755 --- a/packages/table-core/src/index.ts +++ b/packages/table-core/src/index.ts @@ -72,7 +72,7 @@ export * from './fns/sortFns' export * from './features/stockFeatures' // tableReactivityFeature -export * from './features/table-reactivity/tableReactivityFeature' +export * from './features/table-reactivity/table-reactivity' // columnFacetingFeature export * from './features/column-faceting/columnFacetingFeature' diff --git a/packages/table-core/src/types/Table.ts b/packages/table-core/src/types/Table.ts index 6c27d25a17..afa214e90c 100644 --- a/packages/table-core/src/types/Table.ts +++ b/packages/table-core/src/types/Table.ts @@ -1,4 +1,4 @@ -import type { ReadonlyStore } from '@tanstack/store' +import type { ReadonlyAtom, ReadonlyStore } from '@tanstack/store' import type { Table_ColumnFaceting } from '../features/column-faceting/columnFacetingFeature.types' import type { Table_ColumnResizing } from '../features/column-resizing/columnResizingFeature.types' import type { Table_ColumnFiltering } from '../features/column-filtering/columnFilteringFeature.types' @@ -128,5 +128,5 @@ export type Table_Internal< initialState: TableState_All baseAtoms: BaseAtoms_All atoms: Atoms_All - store: ReadonlyStore + store: ReadonlyAtom } diff --git a/packages/table-core/tsdown.config.ts b/packages/table-core/tsdown.config.ts index c305d9b999..533e215e93 100644 --- a/packages/table-core/tsdown.config.ts +++ b/packages/table-core/tsdown.config.ts @@ -5,6 +5,7 @@ export default defineConfig({ './src/index.ts', './src/static-functions.ts', './src/flex-render.ts', + './src/features/table-reactivity/tanstack-signals', ], format: ['esm', 'cjs'], unbundle: true, From e69b86d5c77e49441a3fc658a89a6edb1e6baab8 Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Tue, 28 Apr 2026 19:47:56 +0200 Subject: [PATCH 03/14] feat(table): add abstract `batch` fn to reactivity system --- packages/angular-table/src/signals.ts | 4 +++- packages/solid-table/src/signals.ts | 2 ++ .../table-core/src/core/table/coreTablesFeature.utils.ts | 3 +-- .../src/features/table-reactivity/table-reactivity.ts | 8 +++++--- .../src/features/table-reactivity/tanstack-signals.ts | 3 ++- packages/table-core/src/types/Table.ts | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/angular-table/src/signals.ts b/packages/angular-table/src/signals.ts index 1e3cff44e3..1b074434e2 100644 --- a/packages/angular-table/src/signals.ts +++ b/packages/angular-table/src/signals.ts @@ -1,11 +1,12 @@ import { computed, signal, untracked } from '@angular/core' import { toObservable } from '@angular/core/rxjs-interop' +import { batch } from '@tanstack/angular-store' +import type { Atom, Observer, ReadonlyAtom } from '@tanstack/angular-store' import type { TableAtomOptions, TableReactivityBindings, } from '@tanstack/table-core' import type { Injector, Signal, WritableSignal } from '@angular/core' -import type { Atom, Observer, ReadonlyAtom } from '@tanstack/angular-store' function signalToReadonlyAtom( signal: Signal, @@ -60,5 +61,6 @@ export function angularReactivity(injector: Injector): TableReactivityBindings { return signalToWritableAtom(writableSignal, injector) }, untrack: untracked, + batch: (fn) => fn(), } } diff --git a/packages/solid-table/src/signals.ts b/packages/solid-table/src/signals.ts index 2abde786d9..473bef5842 100644 --- a/packages/solid-table/src/signals.ts +++ b/packages/solid-table/src/signals.ts @@ -1,4 +1,5 @@ import { + batch, createMemo, createSignal, observable, @@ -62,5 +63,6 @@ export function solidReactivity(owner: Owner): TableReactivityBindings { return signalToWritableAtom(writableSignal, owner) }, untrack: untrack, + batch: batch, } } diff --git a/packages/table-core/src/core/table/coreTablesFeature.utils.ts b/packages/table-core/src/core/table/coreTablesFeature.utils.ts index 65af56f5e2..01872f17ed 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.utils.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.utils.ts @@ -1,4 +1,3 @@ -import { batch } from '@tanstack/store' import { cloneState, functionalUpdate } from '../../utils' import type { RowData, Updater } from '../../types/type-utils' import type { TableFeatures } from '../../types/TableFeatures' @@ -10,7 +9,7 @@ export function table_reset< TData extends RowData, >(table: Table_Internal): void { const snap = cloneState(table.initialState) - batch(() => { + table._reactivity.batch(() => { for (const key of Object.keys(snap) as Array) { ;(table.baseAtoms as any)[key].set(snap[key] as any) } diff --git a/packages/table-core/src/features/table-reactivity/table-reactivity.ts b/packages/table-core/src/features/table-reactivity/table-reactivity.ts index 4af3832c06..21fc33b65e 100644 --- a/packages/table-core/src/features/table-reactivity/table-reactivity.ts +++ b/packages/table-core/src/features/table-reactivity/table-reactivity.ts @@ -20,6 +20,7 @@ export interface TableReactivityBindings { options?: TableAtomOptions, ) => ReadonlyAtom untrack: (fn: () => T) => T + batch: (fn: () => void) => void } export function atomToStore(atom: Atom): Store @@ -27,11 +28,12 @@ export function atomToStore(atom: ReadonlyAtom): ReadonlyStore export function atomToStore( atom: Atom | ReadonlyAtom, ): Store | ReadonlyStore { - const store: Store = Object.assign(atom, { - get state() { + const store: Store = atom as Store + Object.defineProperty(atom, 'state', { + get() { return atom.get() }, - }) as Store + }) if ('set' in atom) { store.setState = atom.set.bind(atom) } diff --git a/packages/table-core/src/features/table-reactivity/tanstack-signals.ts b/packages/table-core/src/features/table-reactivity/tanstack-signals.ts index 6cfaacee60..d043568068 100644 --- a/packages/table-core/src/features/table-reactivity/tanstack-signals.ts +++ b/packages/table-core/src/features/table-reactivity/tanstack-signals.ts @@ -1,8 +1,9 @@ -import { createAtom } from '@tanstack/store' +import { batch, createAtom } from '@tanstack/store' import type { TableReactivityBindings } from './table-reactivity' export function tanstackSignals(): TableReactivityBindings { return { + batch: batch, untrack: (fn) => fn(), createReadonlyAtom: (fn, options) => { return createAtom(() => fn(), { diff --git a/packages/table-core/src/types/Table.ts b/packages/table-core/src/types/Table.ts index afa214e90c..e2be6d1d29 100644 --- a/packages/table-core/src/types/Table.ts +++ b/packages/table-core/src/types/Table.ts @@ -128,5 +128,5 @@ export type Table_Internal< initialState: TableState_All baseAtoms: BaseAtoms_All atoms: Atoms_All - store: ReadonlyAtom + store: ReadonlyStore } From 05c95dc5a2cc388fd88573db85531968fb1068bb Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Tue, 28 Apr 2026 20:31:26 +0200 Subject: [PATCH 04/14] feat(table): refactor vue, svelte, lit, preact adapters to support native reactivity --- packages/angular-table/package.json | 1 - .../tests/benchmarks/injectTable.benchmark.ts | 35 ---------- .../angular-table/tests/benchmarks/setup.ts | 60 ----------------- packages/lit-table/src/TableController.ts | 23 ++----- .../tests/unit/defaultReactivity.test.ts | 22 +++++++ packages/preact-table/package.json | 1 + packages/preact-table/src/signals.ts | 62 ++++++++++++++++++ packages/preact-table/src/useTable.ts | 10 +-- .../preact-table/tests/unit/signals.test.ts | 20 ++++++ .../svelte-table/src/createTable.svelte.ts | 64 +++++-------------- packages/svelte-table/src/signals.svelte.ts | 64 +++++++++++++++++++ .../src/core/table/constructTable.ts | 2 +- .../tests/helpers/generateTestTable.ts | 3 + .../tests/helpers/rowPinningHelpers.ts | 4 +- .../row-pinning/rowPinningFeature.test.ts | 3 + .../row-selection/rowSelectionFeature.test.ts | 14 ++++ .../columnGroupingFeature.test.ts | 2 + .../unit/core/columns/constructColumn.test.ts | 2 + .../unit/core/table/constructTable.test.ts | 2 + .../table/stockFeaturesInitialState.test.ts | 2 + .../tests/unit/core/tableAtoms.test.ts | 9 ++- packages/vue-table/src/signals.ts | 61 ++++++++++++++++++ packages/vue-table/src/useTable.ts | 32 ++-------- packages/vue-table/tests/unit/signals.test.ts | 22 +++++++ pnpm-lock.yaml | 18 ++++++ 25 files changed, 343 insertions(+), 195 deletions(-) delete mode 100644 packages/angular-table/tests/benchmarks/injectTable.benchmark.ts delete mode 100644 packages/angular-table/tests/benchmarks/setup.ts create mode 100644 packages/lit-table/tests/unit/defaultReactivity.test.ts create mode 100644 packages/preact-table/src/signals.ts create mode 100644 packages/preact-table/tests/unit/signals.test.ts create mode 100644 packages/svelte-table/src/signals.svelte.ts create mode 100644 packages/vue-table/src/signals.ts create mode 100644 packages/vue-table/tests/unit/signals.test.ts diff --git a/packages/angular-table/package.json b/packages/angular-table/package.json index 9f6d6b301c..92eb2f9ca7 100644 --- a/packages/angular-table/package.json +++ b/packages/angular-table/package.json @@ -55,7 +55,6 @@ "test:build": "publint --strict", "test:eslint": "eslint ./src", "test:lib": "vitest", - "test:benchmark": "vitest bench", "test:lib:dev": "vitest --watch", "test:types": "tsc && vitest --typecheck" }, diff --git a/packages/angular-table/tests/benchmarks/injectTable.benchmark.ts b/packages/angular-table/tests/benchmarks/injectTable.benchmark.ts deleted file mode 100644 index 78e24cd7d4..0000000000 --- a/packages/angular-table/tests/benchmarks/injectTable.benchmark.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { setTimeout } from 'node:timers/promises' -import { bench, describe } from 'vitest' -import { benchCases, columns, createTestTable, dataMap } from './setup' - -const nIteration = 5 - -for (const benchCase of benchCases) { - describe(`injectTable - ${benchCase.size} elements`, () => { - const data = dataMap[benchCase.size]! - - bench( - `No reactivity`, - async () => { - const table = createTestTable(false, data, columns) - await setTimeout(0) - table.getRowModel() - }, - { - iterations: nIteration, - }, - ) - - bench( - `Full reactivity`, - async () => { - const table = createTestTable(true, data, columns) - await setTimeout(0) - table.getRowModel() - }, - { - iterations: nIteration, - }, - ) - }) -} diff --git a/packages/angular-table/tests/benchmarks/setup.ts b/packages/angular-table/tests/benchmarks/setup.ts deleted file mode 100644 index 00fbe06dc4..0000000000 --- a/packages/angular-table/tests/benchmarks/setup.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { injectTable, stockFeatures } from '../../src' -import type { ColumnDef } from '../../src' - -export function createData(size: number) { - return Array.from({ length: size }, (_, index) => ({ - id: index, - title: `title-${index}`, - name: `name-${index}`, - })) -} - -export const columns: Array> = [ - { id: 'col1' }, - { id: 'col2' }, - { id: 'col3' }, - { id: 'col4' }, - { id: 'col5' }, - { id: 'col6' }, - { id: 'col7' }, -] - -export function createTestTable( - enableGranularReactivity: boolean, - data: Array, - columns: Array, -) { - return injectTable(() => ({ - _features: stockFeatures, - columns: columns, - data, - reactivity: { - table: enableGranularReactivity, - row: enableGranularReactivity, - column: enableGranularReactivity, - cell: enableGranularReactivity, - header: enableGranularReactivity, - }, - })) -} - -export const benchCases = [ - { size: 100, max: 5, threshold: 10 }, - { size: 1000, max: 25, threshold: 50 }, - { size: 2000, max: 50, threshold: 100 }, - { size: 5000, max: 100, threshold: 500 }, - { size: 10_000, max: 200, threshold: 1000 }, - { size: 25_000, max: 500, threshold: 1000 }, - { size: 50_000, max: 1500, threshold: 1000 }, - { size: 100_000, max: 2000, threshold: 1500 }, -] - -console.log('Seeding data...') - -export const dataMap = {} as Record> - -for (const benchCase of benchCases) { - dataMap[benchCase.size] = createData(benchCase.size) -} - -console.log('Seed data completed') diff --git a/packages/lit-table/src/TableController.ts b/packages/lit-table/src/TableController.ts index a8cbc84870..9941866cc0 100644 --- a/packages/lit-table/src/TableController.ts +++ b/packages/lit-table/src/TableController.ts @@ -1,7 +1,5 @@ -import { - constructReactivityFeature, - constructTable, -} from '@tanstack/table-core' +import { constructTable } from '@tanstack/table-core' +import { tanstackSignals } from '@tanstack/table-core/features/table-reactivity/tanstack-signals' import { FlexRender } from './flexRender' import type { Atom, ReadonlyAtom } from '@tanstack/store' import type { @@ -10,6 +8,7 @@ import type { Table, TableFeatures, TableOptions, + TableReactivityBindings, TableState, } from '@tanstack/table-core' import type { @@ -146,19 +145,11 @@ export class TableController< ({}) as TSelected, ): LitTable { if (!this._table) { - const litReactivityFeature = constructReactivityFeature( - { - stateNotifier: () => this._notifier, - optionsNotifier: () => this._notifier, - }, - ) - - const mergedOptions: TableOptions = { + const mergedOptions: TableOptions & { + reactivity: TableReactivityBindings + } = { ...tableOptions, - _features: { - ...tableOptions._features, - litReactivityFeature, - }, + reactivity: tableOptions.reactivity ?? tanstackSignals(), mergeOptions: ( defaultOptions: TableOptions, newOptions: Partial>, diff --git a/packages/lit-table/tests/unit/defaultReactivity.test.ts b/packages/lit-table/tests/unit/defaultReactivity.test.ts new file mode 100644 index 0000000000..9d8e572681 --- /dev/null +++ b/packages/lit-table/tests/unit/defaultReactivity.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, test } from 'vitest' +import { TableController } from '../../src/TableController' + +describe('TableController', () => { + test('uses default reactivity when constructing a table', () => { + const host = { + addController: () => {}, + requestUpdate: () => {}, + } + const controller = new TableController(host) + + const table = controller.table({ + _features: {}, + _rowModels: {}, + columns: [], + data: [], + }) + + expect(table._reactivity).toBeDefined() + expect(table.store.get()).toEqual({}) + }) +}) diff --git a/packages/preact-table/package.json b/packages/preact-table/package.json index 4963839875..19f501e364 100644 --- a/packages/preact-table/package.json +++ b/packages/preact-table/package.json @@ -57,6 +57,7 @@ "build": "tsdown" }, "dependencies": { + "@preact/signals": "^2.5.1", "@tanstack/preact-store": "^0.13.0", "@tanstack/table-core": "workspace:*" }, diff --git a/packages/preact-table/src/signals.ts b/packages/preact-table/src/signals.ts new file mode 100644 index 0000000000..eac4dcf904 --- /dev/null +++ b/packages/preact-table/src/signals.ts @@ -0,0 +1,62 @@ +import { batch, computed, signal, untracked } from '@preact/signals' +import type { + TableAtomOptions, + TableReactivityBindings, +} from '@tanstack/table-core' +import type { Atom, Observer, ReadonlyAtom } from '@tanstack/preact-store' + +function observerToCallback( + observerOrNext: Observer | ((value: T) => void), +): (value: T) => void { + return typeof observerOrNext === 'function' + ? observerOrNext + : (value) => observerOrNext.next?.(value) +} + +function signalToReadonlyAtom(source: { + value: T + subscribe: (observer: (value: T) => void) => () => void +}): ReadonlyAtom { + return Object.assign(source, { + get: () => source.value, + subscribe: ((observerOrNext: Observer | ((value: T) => void)) => { + const unsubscribe = source.subscribe(observerToCallback(observerOrNext)) + return { unsubscribe } + }) as ReadonlyAtom['subscribe'], + }) +} + +function signalToWritableAtom(source: { + value: T + subscribe: (observer: (value: T) => void) => () => void +}): Atom { + return Object.assign(source, { + set: (updater: T | ((prevVal: T) => T)) => { + source.value = + typeof updater === 'function' + ? (updater as (prevVal: T) => T)(source.value) + : updater + }, + get: () => source.value, + subscribe: ((observerOrNext: Observer | ((value: T) => void)) => { + const unsubscribe = source.subscribe(observerToCallback(observerOrNext)) + return { unsubscribe } + }) as Atom['subscribe'], + }) +} + +export function preactReactivity(): TableReactivityBindings { + return { + createReadonlyAtom: (fn: () => T, _options?: TableAtomOptions) => { + return signalToReadonlyAtom(computed(fn)) + }, + createWritableAtom: ( + value: T, + _options?: TableAtomOptions, + ): Atom => { + return signalToWritableAtom(signal(value)) + }, + untrack: untracked, + batch: batch, + } +} diff --git a/packages/preact-table/src/useTable.ts b/packages/preact-table/src/useTable.ts index 39e4edcc7a..13fef1430f 100644 --- a/packages/preact-table/src/useTable.ts +++ b/packages/preact-table/src/useTable.ts @@ -3,6 +3,7 @@ import { constructTable } from '@tanstack/table-core' import { shallow, useSelector } from '@tanstack/preact-store' import { FlexRender } from './FlexRender' import { Subscribe } from './Subscribe' +import { preactReactivity } from './signals' import type { CellData, RowData, @@ -92,11 +93,10 @@ export function useTable< ({}) as TSelected, ): PreactTable { const [table] = useState(() => { - const tableInstance = constructTable(tableOptions) as PreactTable< - TFeatures, - TData, - TSelected - > + const tableInstance = constructTable({ + ...tableOptions, + reactivity: tableOptions.reactivity ?? preactReactivity(), + }) as PreactTable tableInstance.Subscribe = ((props: any) => { return Subscribe({ diff --git a/packages/preact-table/tests/unit/signals.test.ts b/packages/preact-table/tests/unit/signals.test.ts new file mode 100644 index 0000000000..3f4efa04e7 --- /dev/null +++ b/packages/preact-table/tests/unit/signals.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, test } from 'vitest' +import { preactReactivity } from '../../src/signals' + +describe('preactReactivity', () => { + test('creates writable and readonly atoms from Preact signals', () => { + const reactivity = preactReactivity() + const count = reactivity.createWritableAtom(1, { debugName: 'count' }) + const doubled = reactivity.createReadonlyAtom(() => count.get() * 2, { + debugName: 'doubled', + }) + + expect(count.get()).toBe(1) + expect(doubled.get()).toBe(2) + + count.set((value) => value + 1) + + expect(count.get()).toBe(2) + expect(doubled.get()).toBe(4) + }) +}) diff --git a/packages/svelte-table/src/createTable.svelte.ts b/packages/svelte-table/src/createTable.svelte.ts index 0171065618..81a3be43fc 100644 --- a/packages/svelte-table/src/createTable.svelte.ts +++ b/packages/svelte-table/src/createTable.svelte.ts @@ -1,15 +1,14 @@ -import { - constructReactivityFeature, - constructTable, -} from '@tanstack/table-core' -import { shallow, useSelector } from '@tanstack/svelte-store' +import { constructTable } from '@tanstack/table-core' +import { useSelector } from '@tanstack/svelte-store' import { untrack } from 'svelte' import { mergeObjects } from './merge-objects' +import { svelteReactivity } from './signals.svelte' import type { RowData, Table, TableFeatures, TableOptions, + TableReactivityBindings, TableState, } from '@tanstack/table-core' @@ -38,23 +37,12 @@ export function createTable< selector: (state: TableState) => TSelected = () => ({}) as TSelected, ): SvelteTable { - // 1. Create $state-based notifier for reactivity feature - let notifierValue = $state(0) - - // 2. Construct reactivity feature (same pattern as solid/vue/angular) - const svelteReactivityFeature = constructReactivityFeature({ - stateNotifier: () => notifierValue, - optionsNotifier: () => notifierValue, - }) - - // 3. Merge reactivity feature into options using mergeObjects (preserves getters) + // 1. Merge reactivity into options using mergeObjects (preserves getters) const mergedOptions = mergeObjects(tableOptions, { - _features: mergeObjects(tableOptions._features, { - svelteReactivityFeature, - }), - }) as any + reactivity: tableOptions.reactivity ?? svelteReactivity(), + }) as TableOptions & { reactivity: TableReactivityBindings } - // 4. Set up resolved options with mergeOptions handler + // 2. Set up resolved options with mergeOptions handler const resolvedOptions = mergeObjects( { mergeOptions: ( @@ -65,42 +53,25 @@ export function createTable< }, }, mergedOptions, - ) as TableOptions + ) as TableOptions & { reactivity: TableReactivityBindings } - // 5. Construct table + // 3. Construct table const table = constructTable(resolvedOptions) as SvelteTable< TFeatures, TData, TSelected > - // 6. Subscribe to all state and options via useSelector - const allState = useSelector(table.store, (state) => state) - const allOptions = useSelector(table.optionsStore, (options) => options) - - // 7. Sync store changes -> notifier. - // Use $effect.pre so this runs before DOM updates (like Solid's createComputed). - // Use untrack for the write so the effect only depends on allState/allOptions, - // not on notifierValue itself (which would cause an infinite loop). - $effect.pre(() => { - allState.current - allOptions.current - untrack(() => { - notifierValue++ - }) - }) - - // 8. Sync options reactively. When controlled state changes (e.g., $state + // 4. Sync options reactively. When controlled state changes (e.g., $state // inside createTableState), the effect re-runs and calls setOptions. // Use $effect.pre so the table sees updated options BEFORE the DOM renders, // ensuring getRowModel() returns current data (not stale, one-frame-behind data). // The reactive reads (state getters, data getter) happen OUTSIDE untrack - // so they become dependencies. The setOptions call is INSIDE untrack to - // prevent tracking notifierValue (read via store.state's stateNotifier - // interceptor inside setOptions), which would cause an infinite loop. + // so they become dependencies. The setOptions call is INSIDE untrack so + // option writes do not subscribe this effect to table internals. $effect.pre(() => { // Read reactive getters to create $effect dependencies on external state - const state = mergedOptions.state + const state: Record | undefined = mergedOptions.state if (state) { for (const key in state) { void state[key] @@ -110,15 +81,12 @@ export function createTable< untrack(() => { table.setOptions((prev) => { - return mergeObjects(prev, mergedOptions) as TableOptions< - TFeatures, - TData - > + return mergeObjects(prev, mergedOptions) }) }) }) - // 9. State selector + // 5. State selector const stateStore = useSelector(table.store, selector) Object.defineProperty(table, 'state', { diff --git a/packages/svelte-table/src/signals.svelte.ts b/packages/svelte-table/src/signals.svelte.ts new file mode 100644 index 0000000000..de06ef472a --- /dev/null +++ b/packages/svelte-table/src/signals.svelte.ts @@ -0,0 +1,64 @@ +import { flushSync, untrack } from 'svelte' +import type { + TableAtomOptions, + TableReactivityBindings, +} from '@tanstack/table-core' +import type { Atom, Observer, ReadonlyAtom } from '@tanstack/svelte-store' + +function observerToCallback( + observerOrNext: Observer | ((value: T) => void), +): (value: T) => void { + return typeof observerOrNext === 'function' + ? observerOrNext + : (value) => observerOrNext.next?.(value) +} + +function subscribeToRune( + getValue: () => T, + observerOrNext: Observer | ((value: T) => void), +) { + const callback = observerToCallback(observerOrNext) + const unsubscribe = $effect.root(() => { + $effect(() => { + callback(getValue()) + }) + }) + + return { unsubscribe } +} + +export function svelteReactivity(): TableReactivityBindings { + return { + createReadonlyAtom: (fn: () => T, _options?: TableAtomOptions) => { + const value = $derived.by(fn) + + return { + get: () => value, + subscribe: ((observerOrNext: Observer | ((value: T) => void)) => { + return subscribeToRune(() => value, observerOrNext) + }) as ReadonlyAtom['subscribe'], + } + }, + createWritableAtom: ( + initialValue: T, + _options?: TableAtomOptions, + ): Atom => { + let value = $state(initialValue) + + return { + set: (updater: T | ((prevVal: T) => T)) => { + value = + typeof updater === 'function' + ? (updater as (prevVal: T) => T)(value) + : updater + }, + get: () => value, + subscribe: ((observerOrNext: Observer | ((value: T) => void)) => { + return subscribeToRune(() => value, observerOrNext) + }) as Atom['subscribe'], + } + }, + untrack: untrack, + batch: (fn) => flushSync(fn), + } +} diff --git a/packages/table-core/src/core/table/constructTable.ts b/packages/table-core/src/core/table/constructTable.ts index b9df1de96d..eb9a5125d6 100644 --- a/packages/table-core/src/core/table/constructTable.ts +++ b/packages/table-core/src/core/table/constructTable.ts @@ -21,7 +21,7 @@ export function getInitialTableState( export type ConstructTable< TFeatures extends TableFeatures, TData extends RowData, -> = TableOptions & { +> = Omit, 'reactivity'> & { reactivity: TableReactivityBindings } diff --git a/packages/table-core/tests/helpers/generateTestTable.ts b/packages/table-core/tests/helpers/generateTestTable.ts index c89144871e..3ebb3860b4 100644 --- a/packages/table-core/tests/helpers/generateTestTable.ts +++ b/packages/table-core/tests/helpers/generateTestTable.ts @@ -1,6 +1,7 @@ import { constructTable, coreFeatures } from '../../src' import { generateTestColumnDefs } from '../fixtures/data/generateTestColumnDefs' import { generateTestData } from '../fixtures/data/generateTestData' +import { tanstackSignals } from '../../src/features/table-reactivity/tanstack-signals' import type { Row, Table, @@ -26,6 +27,7 @@ export function generateTestTableWithData( return constructTable({ data, columns, + reactivity: tanstackSignals(), getSubRows: (row: Row) => row.subRows, ...options, _features: { @@ -43,6 +45,7 @@ export function generateTestTableFromData( return constructTable({ data, columns, + reactivity: tanstackSignals(), ...options, _features: { ...coreFeatures, diff --git a/packages/table-core/tests/helpers/rowPinningHelpers.ts b/packages/table-core/tests/helpers/rowPinningHelpers.ts index a74b25dcc9..4f13ed7660 100644 --- a/packages/table-core/tests/helpers/rowPinningHelpers.ts +++ b/packages/table-core/tests/helpers/rowPinningHelpers.ts @@ -2,11 +2,12 @@ import { vi } from 'vitest' import { getDefaultRowPinningState } from '../../src/features/row-pinning/rowPinningFeature.utils' import { constructTable, - createColumnHelper, coreFeatures, + createColumnHelper, rowPinningFeature, } from '../../src' import { generateTestData } from '../fixtures/data/generateTestData' +import { tanstackSignals } from '../../src/features/table-reactivity/tanstack-signals' import { generateTestTableWithData } from './generateTestTable' import type { ColumnDef, RowPinningState, TableOptions } from '../../src' import type { Person } from '../fixtures/data/types' @@ -77,6 +78,7 @@ export function createRowPinningTable( const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), data, columns, getSubRows: (row: any) => row.subRows, diff --git a/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts b/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts index eee05a687c..b688e272d7 100644 --- a/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts +++ b/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts @@ -12,6 +12,7 @@ import { createTableWithMockOnPinningChange, } from '../../../helpers/rowPinningHelpers' import { generateTestData } from '../../../fixtures/data/generateTestData' +import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' import type { ColumnDef, Row } from '../../../../src' import type { Person } from '../../../fixtures/data/types' @@ -176,6 +177,7 @@ describe('table methods', () => { _rowModels: { paginatedRowModel: createPaginatedRowModel(), }, + reactivity: tanstackSignals(), data, columns, getSubRows: (row) => row.subRows, @@ -215,6 +217,7 @@ describe('table methods', () => { _rowModels: { paginatedRowModel: createPaginatedRowModel(), }, + reactivity: tanstackSignals(), data, columns, getSubRows: (row) => row.subRows, diff --git a/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts b/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts index 65a510eb19..6634941df8 100644 --- a/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts +++ b/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts @@ -6,6 +6,7 @@ import { } from '../../../../src' import * as RowSelectionUtils from '../../../../src/features/row-selection/rowSelectionFeature.utils' import { generateTestData } from '../../../fixtures/data/generateTestData' +import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' import type { Person } from '../../../fixtures/data/types' import type { ColumnDef } from '../../../../src' @@ -41,6 +42,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -70,6 +72,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -99,6 +102,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -125,6 +129,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -150,6 +155,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -175,6 +181,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -200,6 +207,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -221,6 +229,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -242,6 +251,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -266,6 +276,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -292,6 +303,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, @@ -318,6 +330,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: (row) => row.index === 0, // only first row is selectable (of 2 sub-rows) renderFallbackValue: '', data, @@ -343,6 +356,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), enableRowSelection: true, renderFallbackValue: '', data, diff --git a/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts b/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts index 89c45cc525..1f374786f8 100644 --- a/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts +++ b/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts @@ -8,6 +8,7 @@ import { } from '../../../../src' import { createColumnHelper } from '../../../../src/helpers/columnHelper' import { generateTestData } from '../../../fixtures/data/generateTestData' +import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' import type { Person } from '../../../fixtures/data/types' import type { ColumnDef } from '../../../../src' @@ -46,6 +47,7 @@ describe('#getGroupedRowModel', () => { _rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns), }, + reactivity: tanstackSignals(), onStateChange() {}, renderFallbackValue: '', data, diff --git a/packages/table-core/tests/unit/core/columns/constructColumn.test.ts b/packages/table-core/tests/unit/core/columns/constructColumn.test.ts index 13f1e190cd..063f192bff 100644 --- a/packages/table-core/tests/unit/core/columns/constructColumn.test.ts +++ b/packages/table-core/tests/unit/core/columns/constructColumn.test.ts @@ -2,12 +2,14 @@ import { describe, expect, it } from 'vitest' import { coreColumnsFeature } from '../../../../src/core/columns/coreColumnsFeature' import { constructColumn } from '../../../../src/core/columns/constructColumn' import { constructTable } from '../../../../src' +import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' import type { ColumnDef } from '../../../../src/types/ColumnDef' describe('constructColumn', () => { it('should create a column with all core column APIs and properties', () => { const table = constructTable({ _features: { coreColumnsFeature }, + reactivity: tanstackSignals(), columns: [] as Array, data: [] as Array, }) diff --git a/packages/table-core/tests/unit/core/table/constructTable.test.ts b/packages/table-core/tests/unit/core/table/constructTable.test.ts index 0846fa18bd..da10f2048e 100644 --- a/packages/table-core/tests/unit/core/table/constructTable.test.ts +++ b/packages/table-core/tests/unit/core/table/constructTable.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest' import { constructTable, coreFeatures } from '../../../../src' +import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' describe('constructTable', () => { it('should create a table with all core table APIs and properties', () => { @@ -7,6 +8,7 @@ describe('constructTable', () => { _features: { ...coreFeatures, }, + reactivity: tanstackSignals(), columns: [], data: [], }) diff --git a/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts b/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts index 88d66abfd6..3af707bdb9 100644 --- a/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts +++ b/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts @@ -1,10 +1,12 @@ import { describe, expect, it } from 'vitest' import { constructTable, stockFeatures } from '../../../../src' +import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' describe('constructTable with stockFeatures', () => { it('should include all feature states in initial state', () => { const table = constructTable({ _features: stockFeatures, + reactivity: tanstackSignals(), columns: [], data: [], }) diff --git a/packages/table-core/tests/unit/core/tableAtoms.test.ts b/packages/table-core/tests/unit/core/tableAtoms.test.ts index e3c3312135..77699be9d2 100644 --- a/packages/table-core/tests/unit/core/tableAtoms.test.ts +++ b/packages/table-core/tests/unit/core/tableAtoms.test.ts @@ -6,8 +6,12 @@ import { rowSelectionFeature, rowSortingFeature, } from '../../../src' -import type { Table_Internal } from '../../../src' -import type { PaginationState, SortingState } from '../../../src' +import { tanstackSignals } from '../../../src/features/table-reactivity/tanstack-signals' +import type { + PaginationState, + SortingState, + Table_Internal, +} from '../../../src' const _features = { rowPaginationFeature, @@ -19,6 +23,7 @@ function makeTable(options: any = {}) { return constructTable({ _features, _rowModels: {}, + reactivity: tanstackSignals(), columns: [], data: [], ...options, diff --git a/packages/vue-table/src/signals.ts b/packages/vue-table/src/signals.ts new file mode 100644 index 0000000000..8b25caa53e --- /dev/null +++ b/packages/vue-table/src/signals.ts @@ -0,0 +1,61 @@ +import { computed, shallowRef, watch } from 'vue' +import type { + TableAtomOptions, + TableReactivityBindings, +} from '@tanstack/table-core' +import type { Atom, Observer, ReadonlyAtom } from '@tanstack/vue-store' +import type { ComputedRef, ShallowRef } from 'vue' + +function observerToCallback( + observerOrNext: Observer | ((value: T) => void), +): (value: T) => void { + return typeof observerOrNext === 'function' + ? observerOrNext + : (value) => observerOrNext.next?.(value) +} + +function refToReadonlyAtom(source: ComputedRef | ShallowRef): ReadonlyAtom { + return Object.assign(source, { + get: () => source.value, + subscribe: ((observerOrNext: Observer | ((value: T) => void)) => { + const stop = watch(source, observerToCallback(observerOrNext), { + flush: 'sync', + }) + return { unsubscribe: stop } + }) as ReadonlyAtom['subscribe'], + }) +} + +function refToWritableAtom(source: ShallowRef): Atom { + return Object.assign(source, { + set: (updater: T | ((prevVal: T) => T)) => { + source.value = + typeof updater === 'function' + ? (updater as (prevVal: T) => T)(source.value) + : updater + }, + get: () => source.value, + subscribe: ((observerOrNext: Observer | ((value: T) => void)) => { + const stop = watch(source, observerToCallback(observerOrNext), { + flush: 'sync', + }) + return { unsubscribe: stop } + }) as Atom['subscribe'], + }) +} + +export function vueReactivity(): TableReactivityBindings { + return { + createReadonlyAtom: (fn: () => T, _options?: TableAtomOptions) => { + return refToReadonlyAtom(computed(fn)) + }, + createWritableAtom: ( + value: T, + _options?: TableAtomOptions, + ): Atom => { + return refToWritableAtom(shallowRef(value) as ShallowRef) + }, + untrack: (fn) => fn(), + batch: (fn) => fn(), + } +} diff --git a/packages/vue-table/src/useTable.ts b/packages/vue-table/src/useTable.ts index 4cb84d7d94..3ff7822c10 100644 --- a/packages/vue-table/src/useTable.ts +++ b/packages/vue-table/src/useTable.ts @@ -1,10 +1,8 @@ -import { isRef, ref, unref, watch, watchEffect } from 'vue' -import { - constructReactivityFeature, - constructTable, -} from '@tanstack/table-core' +import { unref, watch } from 'vue' +import { constructTable } from '@tanstack/table-core' import { shallow, useSelector } from '@tanstack/vue-store' import { mergeProxy } from './merge-proxy' +import { vueReactivity } from './signals' import type { Atom, ReadonlyAtom } from '@tanstack/vue-store' import type { NoInfer, @@ -12,6 +10,7 @@ import type { Table, TableFeatures, TableOptions, + TableReactivityBindings, TableState, } from '@tanstack/table-core' import type { MaybeRef, VNode } from 'vue' @@ -106,8 +105,6 @@ export function useTable< selector: (state: TableState) => TSelected = () => ({}) as TSelected, ): VueTable { - const notifier = ref(0) - const syncTableOptions = ( table: Table, options: TableOptionsWithReactiveData, @@ -118,17 +115,9 @@ export function useTable< ) } - const vueReactivityFeature = constructReactivityFeature({ - stateNotifier: () => notifier.value, - optionsNotifier: () => notifier.value, - }) - const mergedOptions = { ...tableOptions, - _features: { - ...tableOptions._features, - vueReactivityFeature, - }, + reactivity: tableOptions.reactivity ?? vueReactivity(), } const resolvedOptions = mergeProxy( @@ -144,7 +133,7 @@ export function useTable< return mergeProxy(defaultOptions, newOptions) }, }, - ) as TableOptions + ) as TableOptions & { reactivity: TableReactivityBindings } const table = constructTable(resolvedOptions) as VueTable< TFeatures, @@ -152,15 +141,6 @@ export function useTable< TSelected > - const allState = useSelector(table.store, (state) => state) - const allOptions = useSelector(table.optionsStore, (state) => state) - - watchEffect(() => { - allState.value - allOptions.value - notifier.value++ - }) - watch( () => getReactiveOptionDeps( diff --git a/packages/vue-table/tests/unit/signals.test.ts b/packages/vue-table/tests/unit/signals.test.ts new file mode 100644 index 0000000000..49b8ddde34 --- /dev/null +++ b/packages/vue-table/tests/unit/signals.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, test } from 'vitest' +import { nextTick } from 'vue' +import { vueReactivity } from '../../src/signals' + +describe('vueReactivity', () => { + test('creates writable and readonly atoms from Vue refs', async () => { + const reactivity = vueReactivity() + const count = reactivity.createWritableAtom(1, { debugName: 'count' }) + const doubled = reactivity.createReadonlyAtom(() => count.get() * 2, { + debugName: 'doubled', + }) + + expect(count.get()).toBe(1) + expect(doubled.get()).toBe(2) + + count.set((value) => value + 1) + await nextTick() + + expect(count.get()).toBe(2) + expect(doubled.get()).toBe(4) + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 644514e09d..7599acd404 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6606,6 +6606,9 @@ importers: packages/preact-table: dependencies: + '@preact/signals': + specifier: ^2.5.1 + version: 2.9.0(preact@10.29.1) '@tanstack/preact-store': specifier: ^0.13.0 version: 0.13.0(preact@10.29.1) @@ -9463,6 +9466,14 @@ packages: '@babel/core': 7.x vite: 2.x || 3.x || 4.x || 5.x || 6.x || 7.x || 8.x + '@preact/signals-core@1.14.1': + resolution: {integrity: sha512-vxPpfXqrwUe9lpjqfYNjAF/0RF/eFGeLgdJzdmIIZjpOnTmGmAB4BjWone562mJGMRP4frU6iZ6ei3PDsu52Ng==} + + '@preact/signals@2.9.0': + resolution: {integrity: sha512-hYrY0KyUqkDgOl1qba/JGn6y81pXnurn21PMaxfcMwdncdZ3M/oVdmpTvEnsGjh48dIwDVc7bjWHqIsngSjYug==} + peerDependencies: + preact: '>= 10.25.0 || >=11.0.0-0' + '@prefresh/babel-plugin@0.5.3': resolution: {integrity: sha512-57LX2SHs4BX2s1IwCjNzTE2OJeEepRCNf1VTEpbNcUyHfMO68eeOWGDIt4ob9aYlW6PEWZ1SuwNikuoIXANDtQ==} @@ -18215,6 +18226,13 @@ snapshots: - rollup - supports-color + '@preact/signals-core@1.14.1': {} + + '@preact/signals@2.9.0(preact@10.29.1)': + dependencies: + '@preact/signals-core': 1.14.1 + preact: 10.29.1 + '@prefresh/babel-plugin@0.5.3': {} '@prefresh/core@1.5.9(preact@10.29.1)': From 5d55d049dbd18effbfece641989155637ed762dd Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:32:30 +0000 Subject: [PATCH 05/14] ci: apply automated fixes --- packages/vue-table/src/signals.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vue-table/src/signals.ts b/packages/vue-table/src/signals.ts index 8b25caa53e..af3b09cfaa 100644 --- a/packages/vue-table/src/signals.ts +++ b/packages/vue-table/src/signals.ts @@ -14,7 +14,9 @@ function observerToCallback( : (value) => observerOrNext.next?.(value) } -function refToReadonlyAtom(source: ComputedRef | ShallowRef): ReadonlyAtom { +function refToReadonlyAtom( + source: ComputedRef | ShallowRef, +): ReadonlyAtom { return Object.assign(source, { get: () => source.value, subscribe: ((observerOrNext: Observer | ((value: T) => void)) => { From 08ee80a1db9952574dad21cd73b8b5be6dcc4dab Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Thu, 30 Apr 2026 18:47:00 +0200 Subject: [PATCH 06/14] fix(react-table): try to fix set state in render --- packages/react-table/src/useTable.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/react-table/src/useTable.ts b/packages/react-table/src/useTable.ts index a83b015fe4..06064f03c0 100644 --- a/packages/react-table/src/useTable.ts +++ b/packages/react-table/src/useTable.ts @@ -1,11 +1,14 @@ 'use client' -import { useMemo, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { constructTable } from '@tanstack/table-core' import { tanstackSignals } from '@tanstack/table-core/features/table-reactivity/tanstack-signals' import { shallow, useSelector } from '@tanstack/react-store' import { FlexRender } from './FlexRender' import { Subscribe } from './Subscribe' +import type { Atom, ReadonlyAtom } from '@tanstack/react-store' +import type { FlexRenderProps } from './FlexRender' +import type { SubscribePropsWithStore } from './Subscribe' import type { CellData, RowData, @@ -14,10 +17,7 @@ import type { TableOptions, TableState, } from '@tanstack/table-core' -import type { Atom, ReadonlyAtom } from '@tanstack/react-store' import type { FunctionComponent, ReactNode } from 'react' -import type { FlexRenderProps } from './FlexRender' -import type { SubscribePropsWithStore } from './Subscribe' export type ReactTable< TFeatures extends TableFeatures, @@ -139,22 +139,26 @@ export function useTable< return tableInstance }) - // sync table options on every render - table.setOptions((prev) => ({ - ...prev, - ...tableOptions, - })) + useEffect(() => { + table.setOptions((prev) => ({ + ...prev, + ...tableOptions, + })) + }, [table, tableOptions]) const state = useSelector(table.store, selector, { compare: shallow }) + const options = useSelector(table.optionsStore, (options) => options, { + compare: shallow, + }) // we know this is not the most efficient way to return the table, // but it is required for the react compiler to work return useMemo( () => ({ ...table, - options: tableOptions, + options, state, }), - [table, tableOptions, state], + [table, options, state], ) as ReactTable } From f0baf47b217ed378757feaa216dc53c2aa2188b5 Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Fri, 1 May 2026 10:11:21 +0200 Subject: [PATCH 07/14] chore: add some internal docs in reactivity bindings --- .../tests/unit/defaultReactivity.test.ts | 2 +- packages/solid-table/src/createTable.ts | 1 + .../src/core/table/constructTable.ts | 2 +- .../src/core/table/coreTablesFeature.types.ts | 2 +- .../src/core/table/coreTablesFeature.utils.ts | 2 +- .../table-reactivity/table-reactivity.ts | 33 +++++++++++++++++++ 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/lit-table/tests/unit/defaultReactivity.test.ts b/packages/lit-table/tests/unit/defaultReactivity.test.ts index 9d8e572681..8ae0bdcffe 100644 --- a/packages/lit-table/tests/unit/defaultReactivity.test.ts +++ b/packages/lit-table/tests/unit/defaultReactivity.test.ts @@ -16,7 +16,7 @@ describe('TableController', () => { data: [], }) - expect(table._reactivity).toBeDefined() + expect(table.reactivity).toBeDefined() expect(table.store.get()).toEqual({}) }) }) diff --git a/packages/solid-table/src/createTable.ts b/packages/solid-table/src/createTable.ts index 09790fc798..fe62fde300 100644 --- a/packages/solid-table/src/createTable.ts +++ b/packages/solid-table/src/createTable.ts @@ -2,6 +2,7 @@ import { constructTable } from '@tanstack/table-core' import { createComputed, getOwner, mergeProps, untrack } from 'solid-js' import { shallow, useSelector } from '@tanstack/solid-store' import { FlexRender } from './FlexRender' +import { solidReactivity } from './signals' import type { Atom, ReadonlyAtom } from '@tanstack/solid-store' import type { Accessor, JSX } from 'solid-js' import type { diff --git a/packages/table-core/src/core/table/constructTable.ts b/packages/table-core/src/core/table/constructTable.ts index eb9a5125d6..07eee93214 100644 --- a/packages/table-core/src/core/table/constructTable.ts +++ b/packages/table-core/src/core/table/constructTable.ts @@ -32,7 +32,7 @@ export function constructTable< const signals = tableOptions.reactivity const table = { - _reactivity: signals, + reactivity: signals, _features: { ...coreFeatures, ...tableOptions._features }, _rowModels: {}, _rowModelFns: {}, diff --git a/packages/table-core/src/core/table/coreTablesFeature.types.ts b/packages/table-core/src/core/table/coreTablesFeature.types.ts index 39527e62ef..ab598ee296 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.types.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.types.ts @@ -122,7 +122,7 @@ export interface Table_CoreProperties< /** * Table custom reactivity bindings. */ - _reactivity: TableReactivityBindings + reactivity: TableReactivityBindings /** * The features that are enabled for the table. */ diff --git a/packages/table-core/src/core/table/coreTablesFeature.utils.ts b/packages/table-core/src/core/table/coreTablesFeature.utils.ts index 01872f17ed..2d93b16d9d 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.utils.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.utils.ts @@ -9,7 +9,7 @@ export function table_reset< TData extends RowData, >(table: Table_Internal): void { const snap = cloneState(table.initialState) - table._reactivity.batch(() => { + table.reactivity.batch(() => { for (const key of Object.keys(snap) as Array) { ;(table.baseAtoms as any)[key].set(snap[key] as any) } diff --git a/packages/table-core/src/features/table-reactivity/table-reactivity.ts b/packages/table-core/src/features/table-reactivity/table-reactivity.ts index 21fc33b65e..a0432af753 100644 --- a/packages/table-core/src/features/table-reactivity/table-reactivity.ts +++ b/packages/table-core/src/features/table-reactivity/table-reactivity.ts @@ -7,24 +7,57 @@ import type { } from '@tanstack/store' export interface TableAtomOptions extends AtomOptions { + /** + * A debug name for the atom, useful for debugging. + */ debugName: string } +/** + * Framework reactivity bindings used by table-core. + * + * Adapters (React, Solid, Vue, etc.) provide concrete implementations so + * core features can create derived/writable atoms and integrate with their + * scheduling primitives. + */ export interface TableReactivityBindings { + /** + * Creates a writable atom with an initial value. + */ createWritableAtom: ( initialValue: T, options?: TableAtomOptions, ) => Atom + /** + * Creates a readonly/derived atom from a compute function. + */ createReadonlyAtom: ( fn: () => T, options?: TableAtomOptions, ) => ReadonlyAtom + /** + * Evaluates a function without tracking reactive dependencies. + */ untrack: (fn: () => T) => T + /** + * Batches reactive updates to avoid intermediate recomputation. + */ batch: (fn: () => void) => void } +/** + * Converts a writable atom to the store-compatible shape expected by core. + */ export function atomToStore(atom: Atom): Store +/** + * Converts a readonly atom to a readonly store-compatible shape. + */ export function atomToStore(atom: ReadonlyAtom): ReadonlyStore +/** + * Bridges atom instances to the `Store`/`ReadonlyStore` API by exposing + * a `state` getter backed by `atom.get()`, and wiring `setState` for + * writable atoms. + */ export function atomToStore( atom: Atom | ReadonlyAtom, ): Store | ReadonlyStore { From 4984c5c891414401db8eaaf2858e5b44d137ca58 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Fri, 1 May 2026 21:17:17 -0500 Subject: [PATCH 08/14] package upgrade sync --- .../react/mantine-react-table/package.json | 2 +- .../react/with-tanstack-form/package.json | 2 +- .../react/with-tanstack-router/package.json | 2 +- .../solid/with-tanstack-form/package.json | 2 +- .../solid/with-tanstack-router/package.json | 2 +- .../svelte/with-tanstack-form/package.json | 2 +- examples/vue/with-tanstack-form/package.json | 2 +- packages/preact-table/package.json | 2 +- packages/react-table-devtools/package.json | 2 +- packages/react-table/package.json | 2 +- pnpm-lock.yaml | 242 +++++++++--------- 11 files changed, 131 insertions(+), 131 deletions(-) diff --git a/examples/react/mantine-react-table/package.json b/examples/react/mantine-react-table/package.json index d1bf583467..575ff33e08 100644 --- a/examples/react/mantine-react-table/package.json +++ b/examples/react/mantine-react-table/package.json @@ -20,7 +20,7 @@ "@tanstack/react-table": "^9.0.0-alpha.41", "@tanstack/react-virtual": "^3.13.24", "clsx": "^2.1.1", - "dayjs": "^1.11.13", + "dayjs": "^1.11.20", "react": "^19.2.5", "react-dom": "^19.2.5" }, diff --git a/examples/react/with-tanstack-form/package.json b/examples/react/with-tanstack-form/package.json index 0061549869..2f8059b016 100644 --- a/examples/react/with-tanstack-form/package.json +++ b/examples/react/with-tanstack-form/package.json @@ -15,7 +15,7 @@ "@tanstack/react-table": "^9.0.0-alpha.41", "react": "^19.2.5", "react-dom": "^19.2.5", - "zod": "^4.4.1" + "zod": "^4.4.2" }, "devDependencies": { "@rolldown/plugin-babel": "^0.2.3", diff --git a/examples/react/with-tanstack-router/package.json b/examples/react/with-tanstack-router/package.json index 0f3faa70cb..b921d08b19 100644 --- a/examples/react/with-tanstack-router/package.json +++ b/examples/react/with-tanstack-router/package.json @@ -20,7 +20,7 @@ "@faker-js/faker": "^10.4.0", "@rolldown/plugin-babel": "^0.2.3", "@rollup/plugin-replace": "^6.0.3", - "@tanstack/router-vite-plugin": "^1.166.46", + "@tanstack/router-vite-plugin": "^1.166.47", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", diff --git a/examples/solid/with-tanstack-form/package.json b/examples/solid/with-tanstack-form/package.json index 52df05c4a4..70894aed0f 100644 --- a/examples/solid/with-tanstack-form/package.json +++ b/examples/solid/with-tanstack-form/package.json @@ -19,6 +19,6 @@ "@tanstack/solid-form": "^1.29.1", "@tanstack/solid-table": "^9.0.0-alpha.41", "solid-js": "^1.9.12", - "zod": "^4.4.1" + "zod": "^4.4.2" } } diff --git a/examples/solid/with-tanstack-router/package.json b/examples/solid/with-tanstack-router/package.json index 65da14580d..bd61c17805 100644 --- a/examples/solid/with-tanstack-router/package.json +++ b/examples/solid/with-tanstack-router/package.json @@ -11,7 +11,7 @@ "license": "MIT", "devDependencies": { "@faker-js/faker": "^10.4.0", - "@tanstack/router-vite-plugin": "^1.166.46", + "@tanstack/router-vite-plugin": "^1.166.47", "typescript": "6.0.3", "vite": "^8.0.10", "vite-plugin-solid": "^2.11.12" diff --git a/examples/svelte/with-tanstack-form/package.json b/examples/svelte/with-tanstack-form/package.json index 83a8e9aa38..bf5828ee6c 100644 --- a/examples/svelte/with-tanstack-form/package.json +++ b/examples/svelte/with-tanstack-form/package.json @@ -23,6 +23,6 @@ "@tanstack/form-core": "^1.29.1", "@tanstack/svelte-form": "^1.29.1", "@tanstack/svelte-table": "^9.0.0-alpha.41", - "zod": "^4.4.1" + "zod": "^4.4.2" } } diff --git a/examples/vue/with-tanstack-form/package.json b/examples/vue/with-tanstack-form/package.json index 2864758c5f..ca707f2b3e 100644 --- a/examples/vue/with-tanstack-form/package.json +++ b/examples/vue/with-tanstack-form/package.json @@ -13,7 +13,7 @@ "@tanstack/vue-form": "^1.29.1", "@tanstack/vue-table": "^9.0.0-alpha.41", "vue": "^3.5.33", - "zod": "^4.4.1" + "zod": "^4.4.2" }, "devDependencies": { "@types/node": "^25.6.0", diff --git a/packages/preact-table/package.json b/packages/preact-table/package.json index 5f38c8c499..3379439cf6 100644 --- a/packages/preact-table/package.json +++ b/packages/preact-table/package.json @@ -57,7 +57,7 @@ "build": "tsdown" }, "dependencies": { - "@preact/signals": "^2.5.1", + "@preact/signals": "^2.9.0", "@tanstack/preact-store": "^0.13.0", "@tanstack/table-core": "workspace:*" }, diff --git a/packages/react-table-devtools/package.json b/packages/react-table-devtools/package.json index 4b002c7dc5..331ca66a17 100644 --- a/packages/react-table-devtools/package.json +++ b/packages/react-table-devtools/package.json @@ -51,7 +51,7 @@ "@tanstack/table-devtools": "workspace:*" }, "devDependencies": { - "@eslint-react/eslint-plugin": "^5.6.6", + "@eslint-react/eslint-plugin": "^5.7.0", "@tanstack/table-core": "workspace:*", "@types/react": "^19.2.14", "@vitejs/plugin-react": "^6.0.1", diff --git a/packages/react-table/package.json b/packages/react-table/package.json index c6045c4f50..e83de21f77 100644 --- a/packages/react-table/package.json +++ b/packages/react-table/package.json @@ -65,7 +65,7 @@ "@tanstack/table-core": "workspace:*" }, "devDependencies": { - "@eslint-react/eslint-plugin": "^5.6.6", + "@eslint-react/eslint-plugin": "^5.7.0", "@types/react": "^19.2.14", "@vitejs/plugin-react": "^6.0.1", "eslint-plugin-react-compiler": "19.1.0-rc.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 693bb35ca1..fa78e6b54e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3715,7 +3715,7 @@ importers: specifier: ^2.1.1 version: 2.1.1 dayjs: - specifier: ^1.11.13 + specifier: ^1.11.20 version: 1.11.20 react: specifier: ^19.2.5 @@ -4325,8 +4325,8 @@ importers: specifier: ^19.2.5 version: 19.2.5(react@19.2.5) zod: - specifier: ^4.4.1 - version: 4.4.1 + specifier: ^4.4.2 + version: 4.4.2 devDependencies: '@rolldown/plugin-babel': specifier: ^0.2.3 @@ -4427,8 +4427,8 @@ importers: specifier: ^6.0.3 version: 6.0.3(rollup@4.60.2) '@tanstack/router-vite-plugin': - specifier: ^1.166.46 - version: 1.166.46(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0)) + specifier: ^1.166.47 + version: 1.166.47(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0)) '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -5081,8 +5081,8 @@ importers: specifier: ^1.9.12 version: 1.9.12 zod: - specifier: ^4.4.1 - version: 4.4.1 + specifier: ^4.4.2 + version: 4.4.2 devDependencies: '@faker-js/faker': specifier: ^10.4.0 @@ -5144,8 +5144,8 @@ importers: specifier: ^10.4.0 version: 10.4.0 '@tanstack/router-vite-plugin': - specifier: ^1.166.46 - version: 1.166.46(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0)) + specifier: ^1.166.47 + version: 1.166.47(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0)) typescript: specifier: 6.0.3 version: 6.0.3 @@ -6027,8 +6027,8 @@ importers: specifier: ^9.0.0-alpha.41 version: link:../../../packages/svelte-table zod: - specifier: ^4.4.1 - version: 4.4.1 + specifier: ^4.4.2 + version: 4.4.2 devDependencies: '@faker-js/faker': specifier: ^10.4.0 @@ -7025,8 +7025,8 @@ importers: specifier: ^3.5.33 version: 3.5.33(typescript@6.0.3) zod: - specifier: ^4.4.1 - version: 4.4.1 + specifier: ^4.4.2 + version: 4.4.2 devDependencies: '@types/node': specifier: ^25.6.0 @@ -7140,7 +7140,7 @@ importers: packages/preact-table: dependencies: '@preact/signals': - specifier: ^2.5.1 + specifier: ^2.9.0 version: 2.9.0(preact@10.29.1) '@tanstack/preact-store': specifier: ^0.13.0 @@ -7185,8 +7185,8 @@ importers: version: link:../table-core devDependencies: '@eslint-react/eslint-plugin': - specifier: ^5.6.6 - version: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + specifier: ^5.7.0 + version: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -7222,8 +7222,8 @@ importers: version: 19.2.5(react@19.2.5) devDependencies: '@eslint-react/eslint-plugin': - specifier: ^5.6.6 - version: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + specifier: ^5.7.0 + version: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@tanstack/table-core': specifier: workspace:* version: link:../table-core @@ -8942,50 +8942,50 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-react/ast@5.6.6': - resolution: {integrity: sha512-/JQtRvhfYXs3mUbwXROezatFNR6icnU0nLOagNMseg+zkXsUvaITJyWqcRMz4xXXzvoAGwUjCwiuT+iWAVg5zg==} + '@eslint-react/ast@5.7.0': + resolution: {integrity: sha512-axxvjF/ExVyhXcyS5IUWtz/jP3mXzDt416tr+rWmlb6RPPqdG8tnyNgPXX0rLXHsC2yjfc7LmRuDwUO61hbG2w==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - '@eslint-react/core@5.6.6': - resolution: {integrity: sha512-bSswvxQlwXJ2vnPm/OkgNS3gVbRC4nL/ZpuLi67qGFb1/ORk1OilzO0bSMVVdp9j1v0H2cNxq6pOJVPGbS7xsw==} + '@eslint-react/core@5.7.0': + resolution: {integrity: sha512-PoEXxwa5ob/uuE8y/rXZX047ysd0bsdNwsumqU0fLZmdWjOrV+ZS7tmu83HmqMIRmkHKrKTEisMqFCaNZujKIQ==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - '@eslint-react/eslint-plugin@5.6.6': - resolution: {integrity: sha512-xxeB32eATjdyBb6WMBQlQc1w5NftahLBhz0jWdbyPKHIpYJZXtJ997tNuOTFnvAayRMjWlzl6tCTfhyInu2Uaw==} + '@eslint-react/eslint-plugin@5.7.0': + resolution: {integrity: sha512-3JgMd9TudbaUF5AyAY+4xnL5lw3+Nadfh6fIEENc6IXJEQkYKegudfJQtWnUke4Ux2prl34GcVtlJfUP63Uurg==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - '@eslint-react/eslint@5.6.6': - resolution: {integrity: sha512-LhOKn0QsHukmfwsKEVS/bdnpzT3/XW1Tb8Yx22IiwuK56evx+5szFSCBy7qXSEvjKvgDl1CznujS70IcdpsJvA==} + '@eslint-react/eslint@5.7.0': + resolution: {integrity: sha512-U0DJvQqTAHok4yjIzSHp2Eg4fTSAH32pAzOYJTpfe2K0dV7XryXgQxYaHlrPRr9nzTeTTdQNTk7JywhsOQRnvQ==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - '@eslint-react/jsx@5.6.6': - resolution: {integrity: sha512-sJTUmCXEnJ6Fm1Cdu+a/zVuTXQys+9HVSVLKrgxgTp9XMBIl1v5dz+axiIeQ5VxfWxvFQJVdUS7CKGoivu/CRg==} + '@eslint-react/jsx@5.7.0': + resolution: {integrity: sha512-dUqohdmq8Jvzz7I4DN8uPfMyKNRRaRs2s0EluSq9WiRu8BT64VH11wwLj9HJTTtx4PJDmRuUZClnd7jENz0N6g==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - '@eslint-react/shared@5.6.6': - resolution: {integrity: sha512-gh3xE3rusNuxUxv0OIVzpPp1mQy5KvKGGZ0oQ44mtZwKiGN25MTaY/HzLqN5SsSudxF/bDr6uKDChPct59s+qQ==} + '@eslint-react/shared@5.7.0': + resolution: {integrity: sha512-YNI7Mqo4aGbwXOR3mvIL4+RVmiHilQa8btvAu9uHgac8KPqPWng5dtoWVEy2NsaA3lUDq6j0W8AItYb4YsjgIA==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - '@eslint-react/var@5.6.6': - resolution: {integrity: sha512-i42rRnDCQ2SrNAo0fMRj/X5ycvpsOZ860mhVbj64sqAOSfrgbh0IdMXMazz1bv1wwfR8Q2QD1K+GM08WzvvmpA==} + '@eslint-react/var@5.7.0': + resolution: {integrity: sha512-aEOcxuXojdKXRKRo2GIko5Je5AAKjwPkNy1czX4IbL3c+HRoQ2dK6UPgRiCXR0lfq3HBv0ArUTN4z4E23ZF+JQ==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 @@ -11943,8 +11943,8 @@ packages: resolution: {integrity: sha512-j2OW/UvpjM/DT9tHVmuhWW1k6UOezTRrPqBPZFFmIth0fY7iTPqK+Erqpo8r5yGTRGCbMvOS4sL3H2MldnIZew==} engines: {node: '>=20.19'} - '@tanstack/router-plugin@1.167.31': - resolution: {integrity: sha512-WP+joQGNKXIKDvitX7KydHtTVQyzrnPwqSo2e/IwF5arD31Rwg1arlbze7ZSdEJCOrUhHrBP1/WmqbmeENFTTA==} + '@tanstack/router-plugin@1.167.32': + resolution: {integrity: sha512-i9BA6GzUCoM20UYZ77orXzHwD5zM0OQTtLuPNbqTTSG38CvR6viRFP/d+QFo2aRNyCvun8PR7zSa49bslSggEQ==} engines: {node: '>=20.19'} hasBin: true peerDependencies: @@ -11969,8 +11969,8 @@ packages: resolution: {integrity: sha512-VkY0u7ax/GD0qU6ZLLnfPC+UMxVzxRbvZp4yV4iUSXjgJZ/siAT5/QlLm9FEDJ9QDoC0VD9W7f00tKKreUI7Ng==} engines: {node: '>=20.19'} - '@tanstack/router-vite-plugin@1.166.46': - resolution: {integrity: sha512-m6G5VTzlOvXuVb8p1EEOHs9pZ714Q8UeveVHQ51PX0XiOh4M54i0cRUYTtwa+mRSIComVHOk997SrfFibPNJcw==} + '@tanstack/router-vite-plugin@1.166.47': + resolution: {integrity: sha512-9/SElKRWMP8qVR7+cU+LZpPu9P0bp+Ph+xf/idde3D6Uid+EieFWjH6WrMx+I3k7fsbsGP/bxFF89Xmi5D0IGg==} engines: {node: '>=20.19'} '@tanstack/solid-devtools@0.8.2': @@ -13572,8 +13572,8 @@ packages: peerDependencies: eslint: '>=7' - eslint-plugin-react-dom@5.6.6: - resolution: {integrity: sha512-2feT8VFFVUFiW6XGi43+YuF70+sEljA4jdqhOt30urmEfLQzxjlaV1EV2AeQSmIYd8Xp39GnHrTDvePbt79YDg==} + eslint-plugin-react-dom@5.7.0: + resolution: {integrity: sha512-aA3d27vFUqhfNnmBB9/b8INvBcJcVfIwrpdagBtzr5baHsznw8+VM02lCJeRW2l/0fHBKRctWQn3lRo1fPNCgQ==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 @@ -13585,36 +13585,36 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 - eslint-plugin-react-jsx@5.6.6: - resolution: {integrity: sha512-iCn5FMzerdCERMv4+Orpn7/E3WJnnu10PmAtyqgTrLQgy+UvWIPvEC2qRpnez/9cZsu1AtLL/xtYa+R55jGozg==} + eslint-plugin-react-jsx@5.7.0: + resolution: {integrity: sha512-tTeXNK1QP6YvfMyTCWg8i1ZHHFM67KKBWBNsayI/WAQ9micZ9m2vprbk/EPFJ/RgWqUgT7swIzt4powxiGqTqw==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - eslint-plugin-react-naming-convention@5.6.6: - resolution: {integrity: sha512-T9ys5uzp0a4Rg5oh71L52uWyGPzv21adNXb2rB+RO/o+2we7oCv2sxW5hUMu8F3d2zZmDPJtz42ASghaxGOo4w==} + eslint-plugin-react-naming-convention@5.7.0: + resolution: {integrity: sha512-s71ZdDG8sPlsP0H64Dy4FHgD/11FpsXWhKjkSo1rcTs5i9bONJwxRzUQV/99cTtigYSojaEb/fi/r7plo3ezmw==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - eslint-plugin-react-rsc@5.6.6: - resolution: {integrity: sha512-tBm5dtr8b755kLP5W/giV/0t4j9SVTEMxvZvVwtY8uD45Jn6TynNIH02jv9fRfuCsNsWehTT4ZEjbopiRPtgmw==} + eslint-plugin-react-rsc@5.7.0: + resolution: {integrity: sha512-fMZyrb8mW5EiQzrJRjM083+3EwAGgsTBXo7B7LFEUDcjV+CwH98hwjNRqLKN0GYPiGK5XVmMXg19P2sv5kI9MA==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - eslint-plugin-react-web-api@5.6.6: - resolution: {integrity: sha512-3SRULmUUi/Gt9I655pkNJeB+YVGw+Bc25dWoLPgis+Sfw+H4fjyXyrofcN6SdumcxU5RMiSO2yuw6v8O6Sj3RA==} + eslint-plugin-react-web-api@5.7.0: + resolution: {integrity: sha512-fiPrh9V/WuRGM+ie0IaLMj9mE/dLhkqZ9PLBJ9MWVH/NMZLigGX5uSDshrR7Hafm/b58XkM9Af3tt0wH99RC5w==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 typescript: '*' - eslint-plugin-react-x@5.6.6: - resolution: {integrity: sha512-Szx1VkEEell+b3j99dNHY0OJqCtDldfFaQLS0BmvlUOfV27cn/4yFqal9/0Tq8lU0T4Gvi0iiDhSmxib6KsyPA==} + eslint-plugin-react-x@5.7.0: + resolution: {integrity: sha512-bt0q1X1eK8fz0wJsBPWQvuNexDzM6jWjADM7qpdVEHm6uYnIiJaW0GIgrLecwt1YCmNyAcdh3kEeNFHEmeXfdw==} engines: {node: '>=22.0.0'} peerDependencies: eslint: ^10.2.1 @@ -16786,8 +16786,8 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - zod@4.4.1: - resolution: {integrity: sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==} + zod@4.4.2: + resolution: {integrity: sha512-IynmDyxsEsb9RKzO3J9+4SxXnl2FTFSzNBaKKaMV6tsSk0rw9gYw9gs+JFCq/qk2LCZ78KDwyj+Z289TijSkUw==} snapshots: @@ -18699,7 +18699,7 @@ snapshots: '@eslint-community/regexpp@4.12.2': {} - '@eslint-react/ast@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': + '@eslint-react/ast@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.59.1 '@typescript-eslint/typescript-estree': 8.59.1(typescript@6.0.3) @@ -18710,13 +18710,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint-react/core@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': + '@eslint-react/core@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/jsx': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/jsx': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/var': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.59.1 '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) @@ -18726,21 +18726,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint-react/eslint-plugin@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': + '@eslint-react/eslint-plugin@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) eslint: 10.3.0(jiti@2.6.1) - eslint-plugin-react-dom: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - eslint-plugin-react-jsx: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - eslint-plugin-react-naming-convention: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - eslint-plugin-react-rsc: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - eslint-plugin-react-web-api: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - eslint-plugin-react-x: 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + eslint-plugin-react-dom: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + eslint-plugin-react-jsx: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + eslint-plugin-react-naming-convention: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + eslint-plugin-react-rsc: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + eslint-plugin-react-web-api: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + eslint-plugin-react-x: 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@eslint-react/eslint@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': + '@eslint-react/eslint@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) eslint: 10.3.0(jiti@2.6.1) @@ -18748,12 +18748,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint-react/jsx@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': + '@eslint-react/jsx@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/var': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) eslint: 10.3.0(jiti@2.6.1) @@ -18762,21 +18762,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint-react/shared@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': + '@eslint-react/shared@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) eslint: 10.3.0(jiti@2.6.1) ts-pattern: 5.9.0 typescript: 6.0.3 - zod: 4.4.1 + zod: 4.4.2 transitivePeerDependencies: - supports-color - '@eslint-react/var@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': + '@eslint-react/var@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.59.1 '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) @@ -21475,7 +21475,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.167.31(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0))': + '@tanstack/router-plugin@1.167.32(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) @@ -21512,9 +21512,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-vite-plugin@1.166.46(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0))': + '@tanstack/router-vite-plugin@1.166.47(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0))': dependencies: - '@tanstack/router-plugin': 1.167.31(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0)) + '@tanstack/router-plugin': 1.167.32(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3)))(vite@8.0.10(@types/node@25.6.0)(esbuild@0.28.0)(jiti@2.6.1)(less@4.6.4)(sass@1.99.0)(sugarss@5.0.1(postcss@8.5.13))(terser@5.46.0)(yaml@2.8.3))(webpack@5.105.2(esbuild@0.28.0)) transitivePeerDependencies: - '@rsbuild/core' - '@tanstack/react-router' @@ -23380,12 +23380,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-dom@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): + eslint-plugin-react-dom@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/jsx': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/jsx': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) compare-versions: 6.1.1 @@ -23400,18 +23400,18 @@ snapshots: '@babel/parser': 7.29.2 eslint: 10.3.0(jiti@2.6.1) hermes-parser: 0.25.1 - zod: 4.4.1 - zod-validation-error: 4.0.2(zod@4.4.1) + zod: 4.4.2 + zod-validation-error: 4.0.2(zod@4.4.2) transitivePeerDependencies: - supports-color - eslint-plugin-react-jsx@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): + eslint-plugin-react-jsx@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/core': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/jsx': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/core': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/jsx': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) eslint: 10.3.0(jiti@2.6.1) @@ -23419,12 +23419,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-naming-convention@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): + eslint-plugin-react-naming-convention@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/core': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/core': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/var': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) eslint: 10.3.0(jiti@2.6.1) @@ -23433,13 +23433,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-rsc@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): + eslint-plugin-react-rsc@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/core': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/core': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/var': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) eslint: 10.3.0(jiti@2.6.1) @@ -23447,13 +23447,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-web-api@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): + eslint-plugin-react-web-api@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/core': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/core': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/var': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.59.1 '@typescript-eslint/utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) birecord: 0.1.1 @@ -23463,14 +23463,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-x@5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): + eslint-plugin-react-x@5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3): dependencies: - '@eslint-react/ast': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/core': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/eslint': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/jsx': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 5.6.6(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/ast': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/core': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/eslint': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/jsx': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/shared': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) + '@eslint-react/var': 5.7.0(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.59.1 '@typescript-eslint/type-utils': 8.59.1(eslint@10.3.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.59.1 @@ -24288,7 +24288,7 @@ snapshots: tinyglobby: 0.2.16 unbash: 3.0.0 yaml: 2.8.3 - zod: 4.4.1 + zod: 4.4.2 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -27118,12 +27118,12 @@ snapshots: dependencies: zod: 3.25.76 - zod-validation-error@4.0.2(zod@4.4.1): + zod-validation-error@4.0.2(zod@4.4.2): dependencies: - zod: 4.4.1 + zod: 4.4.2 zod@3.25.76: {} zod@4.3.6: {} - zod@4.4.1: {} + zod@4.4.2: {} From 75469b27d3ac5a464a4949cde347e12c4975218e Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 2 May 2026 20:25:11 -0500 Subject: [PATCH 09/14] rename reactivity stuff as coreReactivityFeature --- .../preact/basic-external-atoms/index.html | 1 - .../preact/basic-external-state/index.html | 1 - .../preact/basic-use-app-table/index.html | 1 - examples/preact/basic-use-table/index.html | 1 - examples/preact/column-groups/index.html | 1 - examples/preact/column-ordering/index.html | 1 - .../preact/column-pinning-split/index.html | 1 - .../preact/column-pinning-sticky/index.html | 1 - examples/preact/column-pinning/index.html | 1 - .../column-resizing-performant/index.html | 1 - examples/preact/column-resizing/index.html | 1 - examples/preact/column-sizing/index.html | 1 - examples/preact/column-visibility/index.html | 1 - examples/preact/composable-tables/index.html | 1 - examples/preact/custom-plugin/index.html | 1 - examples/preact/expanding/index.html | 1 - examples/preact/filters-faceted/index.html | 1 - examples/preact/filters-fuzzy/index.html | 1 - examples/preact/filters/index.html | 1 - examples/preact/grouping/index.html | 1 - examples/preact/pagination/index.html | 1 - examples/preact/row-pinning/index.html | 1 - examples/preact/row-selection/index.html | 1 - examples/preact/sorting/index.html | 1 - examples/preact/sub-components/index.html | 1 - .../preact/with-tanstack-query/index.html | 1 - .../src/helpers/createTableHook.ts | 5 +- packages/angular-table/src/injectTable.ts | 7 +- .../src/lazySignalInitializer.ts | 2 +- .../src/{signals.ts => reactivity.ts} | 3 +- packages/lit-table/src/TableController.ts | 12 ++-- .../src/{signals.ts => reactivity.ts} | 3 +- packages/preact-table/src/useTable.ts | 33 ++++----- .../preact-table/tests/unit/signals.test.ts | 2 +- packages/react-table/src/useTable.ts | 7 +- packages/solid-table/src/createTable.ts | 10 +-- .../src/{signals.ts => reactivity.ts} | 2 +- .../svelte-table/src/createTable.svelte.ts | 12 ++-- ...signals.svelte.ts => reactivity.svelte.ts} | 2 +- packages/table-core/package.json | 8 +-- packages/table-core/src/core/coreFeatures.ts | 2 + .../constructReactivityBindings.ts} | 4 +- .../coreReactivityFeature.types.ts} | 36 +--------- .../reactivity/coreReactivityFeature.utils.ts | 29 ++++++++ .../src/core/table/constructTable.ts | 32 ++++----- .../src/core/table/coreTablesFeature.types.ts | 70 +++++++++---------- packages/table-core/src/index.ts | 3 - packages/table-core/src/reactivity.ts | 3 + .../tests/helpers/generateTestTable.ts | 6 +- .../tests/helpers/rowPinningHelpers.ts | 4 +- .../row-pinning/rowPinningFeature.test.ts | 6 +- .../row-selection/rowSelectionFeature.test.ts | 28 ++++---- .../columnGroupingFeature.test.ts | 4 +- .../unit/core/columns/constructColumn.test.ts | 4 +- .../unit/core/table/constructTable.test.ts | 4 +- .../table/stockFeaturesInitialState.test.ts | 4 +- .../tests/unit/core/tableAtoms.test.ts | 4 +- packages/table-core/tsdown.config.ts | 2 +- .../src/{signals.ts => reactivity.ts} | 2 +- packages/vue-table/src/useTable.ts | 10 +-- 60 files changed, 185 insertions(+), 206 deletions(-) rename packages/angular-table/src/{signals.ts => reactivity.ts} (95%) rename packages/preact-table/src/{signals.ts => reactivity.ts} (95%) rename packages/solid-table/src/{signals.ts => reactivity.ts} (97%) rename packages/svelte-table/src/{signals.svelte.ts => reactivity.svelte.ts} (97%) rename packages/table-core/src/{features/table-reactivity/tanstack-signals.ts => core/reactivity/constructReactivityBindings.ts} (71%) rename packages/table-core/src/{features/table-reactivity/table-reactivity.ts => core/reactivity/coreReactivityFeature.types.ts} (53%) create mode 100644 packages/table-core/src/core/reactivity/coreReactivityFeature.utils.ts create mode 100644 packages/table-core/src/reactivity.ts rename packages/vue-table/src/{signals.ts => reactivity.ts} (97%) diff --git a/examples/preact/basic-external-atoms/index.html b/examples/preact/basic-external-atoms/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/basic-external-atoms/index.html +++ b/examples/preact/basic-external-atoms/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/basic-external-state/index.html b/examples/preact/basic-external-state/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/basic-external-state/index.html +++ b/examples/preact/basic-external-state/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/basic-use-app-table/index.html b/examples/preact/basic-use-app-table/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/basic-use-app-table/index.html +++ b/examples/preact/basic-use-app-table/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/basic-use-table/index.html b/examples/preact/basic-use-table/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/basic-use-table/index.html +++ b/examples/preact/basic-use-table/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-groups/index.html b/examples/preact/column-groups/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-groups/index.html +++ b/examples/preact/column-groups/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-ordering/index.html b/examples/preact/column-ordering/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-ordering/index.html +++ b/examples/preact/column-ordering/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-pinning-split/index.html b/examples/preact/column-pinning-split/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-pinning-split/index.html +++ b/examples/preact/column-pinning-split/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-pinning-sticky/index.html b/examples/preact/column-pinning-sticky/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-pinning-sticky/index.html +++ b/examples/preact/column-pinning-sticky/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-pinning/index.html b/examples/preact/column-pinning/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-pinning/index.html +++ b/examples/preact/column-pinning/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-resizing-performant/index.html b/examples/preact/column-resizing-performant/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-resizing-performant/index.html +++ b/examples/preact/column-resizing-performant/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-resizing/index.html b/examples/preact/column-resizing/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-resizing/index.html +++ b/examples/preact/column-resizing/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-sizing/index.html b/examples/preact/column-sizing/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-sizing/index.html +++ b/examples/preact/column-sizing/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/column-visibility/index.html b/examples/preact/column-visibility/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/column-visibility/index.html +++ b/examples/preact/column-visibility/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/composable-tables/index.html b/examples/preact/composable-tables/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/composable-tables/index.html +++ b/examples/preact/composable-tables/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/custom-plugin/index.html b/examples/preact/custom-plugin/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/custom-plugin/index.html +++ b/examples/preact/custom-plugin/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/expanding/index.html b/examples/preact/expanding/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/expanding/index.html +++ b/examples/preact/expanding/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/filters-faceted/index.html b/examples/preact/filters-faceted/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/filters-faceted/index.html +++ b/examples/preact/filters-faceted/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/filters-fuzzy/index.html b/examples/preact/filters-fuzzy/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/filters-fuzzy/index.html +++ b/examples/preact/filters-fuzzy/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/filters/index.html b/examples/preact/filters/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/filters/index.html +++ b/examples/preact/filters/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/grouping/index.html b/examples/preact/grouping/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/grouping/index.html +++ b/examples/preact/grouping/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/pagination/index.html b/examples/preact/pagination/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/pagination/index.html +++ b/examples/preact/pagination/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/row-pinning/index.html b/examples/preact/row-pinning/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/row-pinning/index.html +++ b/examples/preact/row-pinning/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/row-selection/index.html b/examples/preact/row-selection/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/row-selection/index.html +++ b/examples/preact/row-selection/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/sorting/index.html b/examples/preact/sorting/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/sorting/index.html +++ b/examples/preact/sorting/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/sub-components/index.html b/examples/preact/sub-components/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/sub-components/index.html +++ b/examples/preact/sub-components/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/examples/preact/with-tanstack-query/index.html b/examples/preact/with-tanstack-query/index.html index 88776ec7ae..fff0f71352 100644 --- a/examples/preact/with-tanstack-query/index.html +++ b/examples/preact/with-tanstack-query/index.html @@ -4,7 +4,6 @@ - Vite + Preact diff --git a/packages/angular-table/src/helpers/createTableHook.ts b/packages/angular-table/src/helpers/createTableHook.ts index f6c278655c..0d1ab14097 100644 --- a/packages/angular-table/src/helpers/createTableHook.ts +++ b/packages/angular-table/src/helpers/createTableHook.ts @@ -446,8 +446,11 @@ export function createTableHook< const options = { ...defaultTableOptions, ...tableOptions(), + _features: { + ...defaultTableOptions._features, + appTableFeatures, + }, } as TableOptions - options._features = { ...options._features, appTableFeatures } return options }, selector) as AngularTable } diff --git a/packages/angular-table/src/injectTable.ts b/packages/angular-table/src/injectTable.ts index 811a262106..4a12ee11fe 100644 --- a/packages/angular-table/src/injectTable.ts +++ b/packages/angular-table/src/injectTable.ts @@ -8,7 +8,7 @@ import { } from '@angular/core' import { constructTable } from '@tanstack/table-core' import { lazyInit } from './lazySignalInitializer' -import { angularReactivity } from './signals' +import { angularReactivity } from './reactivity' import type { Atom, ReadonlyAtom } from '@tanstack/angular-store' import type { RowData, @@ -136,7 +136,10 @@ export function injectTable< return lazyInit(() => { const table = constructTable({ ...options(), - reactivity: angularReactivity(injector), + _features: { + coreReativityFeature: angularReactivity(injector), + ...options()._features, + }, }) as AngularTable let isMount = true diff --git a/packages/angular-table/src/lazySignalInitializer.ts b/packages/angular-table/src/lazySignalInitializer.ts index 45c1fc0e9a..92f8dcc901 100644 --- a/packages/angular-table/src/lazySignalInitializer.ts +++ b/packages/angular-table/src/lazySignalInitializer.ts @@ -1,4 +1,4 @@ -import { effect, untracked } from '@angular/core' +import { untracked } from '@angular/core' /** * Implementation from @tanstack/angular-query diff --git a/packages/angular-table/src/signals.ts b/packages/angular-table/src/reactivity.ts similarity index 95% rename from packages/angular-table/src/signals.ts rename to packages/angular-table/src/reactivity.ts index 1b074434e2..71ed087f62 100644 --- a/packages/angular-table/src/signals.ts +++ b/packages/angular-table/src/reactivity.ts @@ -1,11 +1,10 @@ import { computed, signal, untracked } from '@angular/core' import { toObservable } from '@angular/core/rxjs-interop' -import { batch } from '@tanstack/angular-store' import type { Atom, Observer, ReadonlyAtom } from '@tanstack/angular-store' import type { TableAtomOptions, TableReactivityBindings, -} from '@tanstack/table-core' +} from '@tanstack/table-core/reactivity' import type { Injector, Signal, WritableSignal } from '@angular/core' function signalToReadonlyAtom( diff --git a/packages/lit-table/src/TableController.ts b/packages/lit-table/src/TableController.ts index 9941866cc0..b759d572db 100644 --- a/packages/lit-table/src/TableController.ts +++ b/packages/lit-table/src/TableController.ts @@ -1,5 +1,5 @@ import { constructTable } from '@tanstack/table-core' -import { tanstackSignals } from '@tanstack/table-core/features/table-reactivity/tanstack-signals' +import { constructReactivityBindings } from '@tanstack/table-core/reactivity' import { FlexRender } from './flexRender' import type { Atom, ReadonlyAtom } from '@tanstack/store' import type { @@ -8,7 +8,6 @@ import type { Table, TableFeatures, TableOptions, - TableReactivityBindings, TableState, } from '@tanstack/table-core' import type { @@ -145,11 +144,12 @@ export class TableController< ({}) as TSelected, ): LitTable { if (!this._table) { - const mergedOptions: TableOptions & { - reactivity: TableReactivityBindings - } = { + const mergedOptions: TableOptions = { ...tableOptions, - reactivity: tableOptions.reactivity ?? tanstackSignals(), + _features: { + coreReativityFeature: constructReactivityBindings(), + ...tableOptions._features, + }, mergeOptions: ( defaultOptions: TableOptions, newOptions: Partial>, diff --git a/packages/preact-table/src/signals.ts b/packages/preact-table/src/reactivity.ts similarity index 95% rename from packages/preact-table/src/signals.ts rename to packages/preact-table/src/reactivity.ts index eac4dcf904..08ac3488ea 100644 --- a/packages/preact-table/src/signals.ts +++ b/packages/preact-table/src/reactivity.ts @@ -1,8 +1,9 @@ +// TOTO - re-explore preact signals for reactivity import { batch, computed, signal, untracked } from '@preact/signals' import type { TableAtomOptions, TableReactivityBindings, -} from '@tanstack/table-core' +} from '@tanstack/table-core/reactivity' import type { Atom, Observer, ReadonlyAtom } from '@tanstack/preact-store' function observerToCallback( diff --git a/packages/preact-table/src/useTable.ts b/packages/preact-table/src/useTable.ts index 13fef1430f..977d7a7431 100644 --- a/packages/preact-table/src/useTable.ts +++ b/packages/preact-table/src/useTable.ts @@ -1,9 +1,10 @@ -import { useMemo, useState } from 'preact/hooks' +import { useEffect, useMemo, useState } from 'preact/hooks' import { constructTable } from '@tanstack/table-core' import { shallow, useSelector } from '@tanstack/preact-store' +import { constructReactivityBindings } from '@tanstack/table-core/reactivity' import { FlexRender } from './FlexRender' import { Subscribe } from './Subscribe' -import { preactReactivity } from './signals' +// import { preactReactivity } from './reactivity' import type { CellData, RowData, @@ -95,7 +96,10 @@ export function useTable< const [table] = useState(() => { const tableInstance = constructTable({ ...tableOptions, - reactivity: tableOptions.reactivity ?? preactReactivity(), + _features: { + coreReativityFeature: constructReactivityBindings(), // preactReactivity() currently causes infinite re-renders + ...tableOptions._features, + }, }) as PreactTable tableInstance.Subscribe = ((props: any) => { @@ -110,27 +114,24 @@ export function useTable< return tableInstance }) - // sync table options on every render - table.setOptions((prev) => ({ - ...prev, - ...tableOptions, - })) - - const selectorWithDataAndColumns = (state: TableState) => ({ - columns: tableOptions.columns, - data: tableOptions.data, - ...selector(state), - }) + useEffect(() => { + table.setOptions((prev) => ({ + ...prev, + ...tableOptions, + })) + }, [table, tableOptions]) - const state = useSelector(table.store, selectorWithDataAndColumns, { + const state = useSelector(table.store, selector, { compare: shallow }) + const options = useSelector(table.optionsStore, (options) => options, { compare: shallow, }) return useMemo( () => ({ ...table, + options, state, }), - [state, table], + [table, options, state], ) as PreactTable } diff --git a/packages/preact-table/tests/unit/signals.test.ts b/packages/preact-table/tests/unit/signals.test.ts index 3f4efa04e7..6706b4b07c 100644 --- a/packages/preact-table/tests/unit/signals.test.ts +++ b/packages/preact-table/tests/unit/signals.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'vitest' -import { preactReactivity } from '../../src/signals' +import { preactReactivity } from '../../src/reactivity' describe('preactReactivity', () => { test('creates writable and readonly atoms from Preact signals', () => { diff --git a/packages/react-table/src/useTable.ts b/packages/react-table/src/useTable.ts index 06064f03c0..aea2b679a7 100644 --- a/packages/react-table/src/useTable.ts +++ b/packages/react-table/src/useTable.ts @@ -2,8 +2,8 @@ import { useEffect, useMemo, useState } from 'react' import { constructTable } from '@tanstack/table-core' -import { tanstackSignals } from '@tanstack/table-core/features/table-reactivity/tanstack-signals' import { shallow, useSelector } from '@tanstack/react-store' +import { constructReactivityBindings } from '@tanstack/table-core/reactivity' import { FlexRender } from './FlexRender' import { Subscribe } from './Subscribe' import type { Atom, ReadonlyAtom } from '@tanstack/react-store' @@ -124,7 +124,10 @@ export function useTable< const [table] = useState(() => { const tableInstance = constructTable({ ...tableOptions, - reactivity: tableOptions.reactivity ?? tanstackSignals(), + _features: { + coreReativityFeature: constructReactivityBindings(), + ...tableOptions._features, + }, }) as ReactTable tableInstance.Subscribe = ((props: any) => { diff --git a/packages/solid-table/src/createTable.ts b/packages/solid-table/src/createTable.ts index fe62fde300..573bd34fa5 100644 --- a/packages/solid-table/src/createTable.ts +++ b/packages/solid-table/src/createTable.ts @@ -2,7 +2,7 @@ import { constructTable } from '@tanstack/table-core' import { createComputed, getOwner, mergeProps, untrack } from 'solid-js' import { shallow, useSelector } from '@tanstack/solid-store' import { FlexRender } from './FlexRender' -import { solidReactivity } from './signals' +import { solidReactivity } from './reactivity' import type { Atom, ReadonlyAtom } from '@tanstack/solid-store' import type { Accessor, JSX } from 'solid-js' import type { @@ -10,7 +10,6 @@ import type { Table, TableFeatures, TableOptions, - TableReactivityBindings, TableState, } from '@tanstack/table-core' @@ -75,7 +74,10 @@ export function createTable< const owner = getOwner()! const mergedOptions = mergeProps(tableOptions, { - reactivity: solidReactivity(owner), + _features: { + coreReativityFeature: solidReactivity(owner), + ...tableOptions._features, + }, }) as any const resolvedOptions = mergeProps( @@ -88,7 +90,7 @@ export function createTable< }, }, mergedOptions, - ) as TableOptions & { reactivity: TableReactivityBindings } + ) as TableOptions const table = constructTable(resolvedOptions) as SolidTable< TFeatures, diff --git a/packages/solid-table/src/signals.ts b/packages/solid-table/src/reactivity.ts similarity index 97% rename from packages/solid-table/src/signals.ts rename to packages/solid-table/src/reactivity.ts index 473bef5842..1b3cb44366 100644 --- a/packages/solid-table/src/signals.ts +++ b/packages/solid-table/src/reactivity.ts @@ -10,7 +10,7 @@ import type { Accessor, Owner, Setter } from 'solid-js' import type { TableAtomOptions, TableReactivityBindings, -} from '@tanstack/table-core' +} from '@tanstack/table-core/reactivity' import type { Atom, Observer, ReadonlyAtom } from '@tanstack/solid-store' function signalToReadonlyAtom( diff --git a/packages/svelte-table/src/createTable.svelte.ts b/packages/svelte-table/src/createTable.svelte.ts index 81a3be43fc..9c24a4a1de 100644 --- a/packages/svelte-table/src/createTable.svelte.ts +++ b/packages/svelte-table/src/createTable.svelte.ts @@ -2,13 +2,12 @@ import { constructTable } from '@tanstack/table-core' import { useSelector } from '@tanstack/svelte-store' import { untrack } from 'svelte' import { mergeObjects } from './merge-objects' -import { svelteReactivity } from './signals.svelte' +import { svelteReactivity } from './reactivity.svelte' import type { RowData, Table, TableFeatures, TableOptions, - TableReactivityBindings, TableState, } from '@tanstack/table-core' @@ -39,8 +38,11 @@ export function createTable< ): SvelteTable { // 1. Merge reactivity into options using mergeObjects (preserves getters) const mergedOptions = mergeObjects(tableOptions, { - reactivity: tableOptions.reactivity ?? svelteReactivity(), - }) as TableOptions & { reactivity: TableReactivityBindings } + _features: { + coreReativityFeature: svelteReactivity(), + ...tableOptions._features, + }, + }) as TableOptions // 2. Set up resolved options with mergeOptions handler const resolvedOptions = mergeObjects( @@ -53,7 +55,7 @@ export function createTable< }, }, mergedOptions, - ) as TableOptions & { reactivity: TableReactivityBindings } + ) as TableOptions // 3. Construct table const table = constructTable(resolvedOptions) as SvelteTable< diff --git a/packages/svelte-table/src/signals.svelte.ts b/packages/svelte-table/src/reactivity.svelte.ts similarity index 97% rename from packages/svelte-table/src/signals.svelte.ts rename to packages/svelte-table/src/reactivity.svelte.ts index de06ef472a..0238b914c2 100644 --- a/packages/svelte-table/src/signals.svelte.ts +++ b/packages/svelte-table/src/reactivity.svelte.ts @@ -2,7 +2,7 @@ import { flushSync, untrack } from 'svelte' import type { TableAtomOptions, TableReactivityBindings, -} from '@tanstack/table-core' +} from '@tanstack/table-core/reactivity' import type { Atom, Observer, ReadonlyAtom } from '@tanstack/svelte-store' function observerToCallback( diff --git a/packages/table-core/package.json b/packages/table-core/package.json index b2e35ebccd..0425f26c60 100644 --- a/packages/table-core/package.json +++ b/packages/table-core/package.json @@ -34,14 +34,14 @@ "import": "./dist/index.js", "require": "./dist/index.cjs" }, - "./features/table-reactivity/tanstack-signals": { - "import": "./dist/features/table-reactivity/tanstack-signals.js", - "require": "./dist/features/table-reactivity/tanstack-signals.cjs" - }, "./flex-render": { "import": "./dist/flex-render.js", "require": "./dist/flex-render.cjs" }, + "./reactivity": { + "import": "./dist/reactivity.js", + "require": "./dist/reactivity.cjs" + }, "./static-functions": { "import": "./dist/static-functions.js", "require": "./dist/static-functions.cjs" diff --git a/packages/table-core/src/core/coreFeatures.ts b/packages/table-core/src/core/coreFeatures.ts index 22d11f5762..656ff949de 100644 --- a/packages/table-core/src/core/coreFeatures.ts +++ b/packages/table-core/src/core/coreFeatures.ts @@ -4,8 +4,10 @@ import { coreHeadersFeature } from './headers/coreHeadersFeature' import { coreRowModelsFeature } from './row-models/coreRowModelsFeature' import { coreRowsFeature } from './rows/coreRowsFeature' import { coreTablesFeature } from './table/coreTablesFeature' +import type { TableReactivityBindings } from '../reactivity' export interface CoreFeatures { + coreReativityFeature?: TableReactivityBindings coreCellsFeature: typeof coreCellsFeature coreColumnsFeature: typeof coreColumnsFeature coreHeadersFeature: typeof coreHeadersFeature diff --git a/packages/table-core/src/features/table-reactivity/tanstack-signals.ts b/packages/table-core/src/core/reactivity/constructReactivityBindings.ts similarity index 71% rename from packages/table-core/src/features/table-reactivity/tanstack-signals.ts rename to packages/table-core/src/core/reactivity/constructReactivityBindings.ts index d043568068..2c94853904 100644 --- a/packages/table-core/src/features/table-reactivity/tanstack-signals.ts +++ b/packages/table-core/src/core/reactivity/constructReactivityBindings.ts @@ -1,7 +1,7 @@ import { batch, createAtom } from '@tanstack/store' -import type { TableReactivityBindings } from './table-reactivity' +import type { TableReactivityBindings } from './coreReactivityFeature.types' -export function tanstackSignals(): TableReactivityBindings { +export function constructReactivityBindings(): TableReactivityBindings { return { batch: batch, untrack: (fn) => fn(), diff --git a/packages/table-core/src/features/table-reactivity/table-reactivity.ts b/packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts similarity index 53% rename from packages/table-core/src/features/table-reactivity/table-reactivity.ts rename to packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts index a0432af753..8e323c1c4c 100644 --- a/packages/table-core/src/features/table-reactivity/table-reactivity.ts +++ b/packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts @@ -1,10 +1,4 @@ -import type { - Atom, - AtomOptions, - ReadonlyAtom, - ReadonlyStore, - Store, -} from '@tanstack/store' +import type { Atom, AtomOptions, ReadonlyAtom } from '@tanstack/store' export interface TableAtomOptions extends AtomOptions { /** @@ -44,31 +38,3 @@ export interface TableReactivityBindings { */ batch: (fn: () => void) => void } - -/** - * Converts a writable atom to the store-compatible shape expected by core. - */ -export function atomToStore(atom: Atom): Store -/** - * Converts a readonly atom to a readonly store-compatible shape. - */ -export function atomToStore(atom: ReadonlyAtom): ReadonlyStore -/** - * Bridges atom instances to the `Store`/`ReadonlyStore` API by exposing - * a `state` getter backed by `atom.get()`, and wiring `setState` for - * writable atoms. - */ -export function atomToStore( - atom: Atom | ReadonlyAtom, -): Store | ReadonlyStore { - const store: Store = atom as Store - Object.defineProperty(atom, 'state', { - get() { - return atom.get() - }, - }) - if ('set' in atom) { - store.setState = atom.set.bind(atom) - } - return store -} diff --git a/packages/table-core/src/core/reactivity/coreReactivityFeature.utils.ts b/packages/table-core/src/core/reactivity/coreReactivityFeature.utils.ts new file mode 100644 index 0000000000..54ef1f90dc --- /dev/null +++ b/packages/table-core/src/core/reactivity/coreReactivityFeature.utils.ts @@ -0,0 +1,29 @@ +import type { Atom, ReadonlyAtom, ReadonlyStore, Store } from '@tanstack/store' + +/** + * Converts a writable atom to the store-compatible shape expected by core. + */ +export function atomToStore(atom: Atom): Store +/** + * Converts a readonly atom to a readonly store-compatible shape. + */ +export function atomToStore(atom: ReadonlyAtom): ReadonlyStore +/** + * Bridges atom instances to the `Store`/`ReadonlyStore` API by exposing + * a `state` getter backed by `atom.get()`, and wiring `setState` for + * writable atoms. + */ +export function atomToStore( + atom: Atom | ReadonlyAtom, +): Store | ReadonlyStore { + const store: Store = atom as Store + Object.defineProperty(atom, 'state', { + get() { + return atom.get() + }, + }) + if ('set' in atom) { + store.setState = atom.set.bind(atom) + } + return store +} diff --git a/packages/table-core/src/core/table/constructTable.ts b/packages/table-core/src/core/table/constructTable.ts index 07eee93214..be14a21389 100644 --- a/packages/table-core/src/core/table/constructTable.ts +++ b/packages/table-core/src/core/table/constructTable.ts @@ -1,7 +1,6 @@ import { coreFeatures } from '../coreFeatures' import { cloneState } from '../../utils' -import { atomToStore } from '../../features/table-reactivity/table-reactivity' -import type { TableReactivityBindings } from '../../features/table-reactivity/table-reactivity' +import { atomToStore } from '../reactivity/coreReactivityFeature.utils' import type { RowData } from '../../types/type-utils' import type { TableFeature, TableFeatures } from '../../types/TableFeatures' import type { Table, Table_Internal } from '../../types/Table' @@ -18,21 +17,14 @@ export function getInitialTableState( return cloneState(initialState) as TableState } -export type ConstructTable< - TFeatures extends TableFeatures, - TData extends RowData, -> = Omit, 'reactivity'> & { - reactivity: TableReactivityBindings -} - export function constructTable< TFeatures extends TableFeatures, TData extends RowData, ->(tableOptions: ConstructTable): Table { - const signals = tableOptions.reactivity +>(tableOptions: TableOptions): Table { + const _reactivity = tableOptions._features.coreReativityFeature! const table = { - reactivity: signals, + _reactivity, _features: { ...coreFeatures, ...tableOptions._features }, _rowModels: {}, _rowModelFns: {}, @@ -52,7 +44,8 @@ export function constructTable< return Object.assign(obj, feature.getDefaultTableOptions?.(table)) }, {}) as TableOptions - table.optionsStore = signals.createWritableAtom< + // @ts-ignore - direct set + table.optionsStore = _reactivity.createWritableAtom< TableOptions >({ ...defaultOptions, ...tableOptions }, { debugName: 'table/optionsStore' }) @@ -67,12 +60,15 @@ export function constructTable< for (const key of stateKeys) { // create writable base atom - table.baseAtoms[key] = signals.createWritableAtom(table.initialState[key], { - debugName: `table/baseAtoms/${key}`, - }) as any + table.baseAtoms[key] = _reactivity.createWritableAtom( + table.initialState[key], + { + debugName: `table/baseAtoms/${key}`, + }, + ) as any // create readonly derived atom: on each get(), read current options (state, then external atom, then base) - ;(table.atoms as any)[key] = signals.createReadonlyAtom( + ;(table.atoms as any)[key] = _reactivity.createReadonlyAtom( () => { // Reading optionsStore.get() keeps this reactive to setOptions const opts = table.optionsStore.get() @@ -91,7 +87,7 @@ export function constructTable< } table.store = atomToStore( - signals.createReadonlyAtom( + _reactivity.createReadonlyAtom( () => { const snapshot = {} as TableState for (const key of stateKeys) { diff --git a/packages/table-core/src/core/table/coreTablesFeature.types.ts b/packages/table-core/src/core/table/coreTablesFeature.types.ts index ab598ee296..2d831838e5 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.types.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.types.ts @@ -1,11 +1,11 @@ -import type { Atom, ReadonlyAtom, ReadonlyStore, Store } from '@tanstack/store' +import type { TableReactivityBindings } from '../reactivity/coreReactivityFeature.types' +import type { Atom, ReadonlyAtom, ReadonlyStore } from '@tanstack/store' import type { CoreFeatures } from '../coreFeatures' import type { RowModelFns } from '../../types/RowModelFns' import type { RowData, Updater } from '../../types/type-utils' import type { TableFeatures } from '../../types/TableFeatures' import type { CachedRowModels, CreateRowModels_All } from '../../types/RowModel' import type { TableOptions } from '../../types/TableOptions' -import type { TableReactivityBindings } from '../../features/table-reactivity/table-reactivity' import type { TableState, TableState_All } from '../../types/TableState' export interface TableMeta< @@ -68,11 +68,11 @@ export interface TableOptions_Table< /** * The features that you want to enable for the table. */ - _features: TFeatures + readonly _features: TFeatures /** * The row model options that you want to enable for the table. */ - _rowModels?: CreateRowModels_All + readonly _rowModels?: CreateRowModels_All /** * Optionally, provide your own external writable atoms for individual state slices. * When an atom is provided for a given slice, it takes precedence over `options.state[key]` @@ -80,39 +80,35 @@ export interface TableOptions_Table< * still routed through the internal base atom; consumers are responsible for * mirroring changes back to their external atom via the corresponding `onXChange` callback. */ - atoms?: ExternalAtoms + readonly atoms?: ExternalAtoms /** * Set this option to override any of the `autoReset...` feature options. */ - autoResetAll?: boolean + readonly autoResetAll?: boolean /** * The data for the table to display. When the `data` option changes reference, the table will reprocess the data. */ - data: ReadonlyArray + readonly data: ReadonlyArray /** * Use this option to optionally pass initial state to the table. This state will be used when resetting various table states either automatically by the table (eg. `options.autoResetPageIndex`) or via functions like `table.resetRowSelection()`. Most reset function allow you optionally pass a flag to reset to a blank/default state instead of the initial state. * Table state will not be reset when this object changes, which also means that the initial state object does not need to be stable. */ - initialState?: Partial> + readonly initialState?: Partial> /** * This option is used to optionally implement the merging of table options. */ - mergeOptions?: ( + readonly mergeOptions?: ( defaultOptions: TableOptions, options: Partial>, ) => TableOptions /** * You can pass any object to `options.meta` and access it anywhere the `table` is available via `table.options.meta`. */ - meta?: TableMeta + readonly meta?: TableMeta /** * Pass in individual self-managed state to the table. */ - state?: Partial> - /** - * Table custom reactibity bindings. - */ - readonly reactivity?: TableReactivityBindings + readonly state?: Partial> } export interface Table_CoreProperties< @@ -120,65 +116,65 @@ export interface Table_CoreProperties< TData extends RowData, > { /** - * Table custom reactivity bindings. - */ - reactivity: TableReactivityBindings - /** - * The features that are enabled for the table. + * Table reactivity bindings for interacting with TanStack Store. */ - _features: Partial & TFeatures + readonly _reactivity: TableReactivityBindings /** * Prototype cache for Cell objects - shared by all cells in this table */ - _cellPrototype?: object + readonly _cellPrototype?: object /** * Prototype cache for Column objects - shared by all columns in this table */ - _columnPrototype?: object + readonly _columnPrototype?: object + /** + * The features that are enabled for the table. + */ + readonly _features: Partial & TFeatures /** * Prototype cache for Header objects - shared by all headers in this table */ - _headerPrototype?: object + readonly _headerPrototype?: object /** * The row model processing functions that are used to process the data by features. */ - _rowModelFns: RowModelFns + readonly _rowModelFns: RowModelFns /** * The row models that are enabled for the table. */ - _rowModels: CachedRowModels + readonly _rowModels: CachedRowModels /** * Prototype cache for Row objects - shared by all rows in this table */ - _rowPrototype?: object - /** - * The internal writable atoms for each `TableState` slice. This is the library's - * single write surface — all state mutations from features land here. - */ - baseAtoms: BaseAtoms + readonly _rowPrototype?: object /** * The readonly derived atoms for each `TableState` slice. Each derives from * its corresponding `baseAtom` plus, optionally, a per-slice external atom or * external state value (precedence: external atom > external state > base atom). */ - atoms: Atoms + readonly atoms: Atoms /** - * The base store for the table options. + * The internal writable atoms for each `TableState` slice. This is the library's + * single write surface — all state mutations from features land here. */ - optionsStore: Atom> + readonly baseAtoms: BaseAtoms /** * This is the resolved initial state of the table. */ - initialState: TableState + readonly initialState: TableState /** * A read-only reference to the table's current options. */ readonly options: TableOptions + /** + * The base store for the table options. + */ + readonly optionsStore: Atom> /** * The readonly flat store for the table state. Derives from `table.atoms` * only; never reads external state directly. */ - store: ReadonlyStore> + readonly store: ReadonlyStore> } export interface Table_Table< diff --git a/packages/table-core/src/index.ts b/packages/table-core/src/index.ts index 5b3d05ccc0..06805fe7ff 100755 --- a/packages/table-core/src/index.ts +++ b/packages/table-core/src/index.ts @@ -71,9 +71,6 @@ export * from './fns/sortFns' export * from './features/stockFeatures' -// tableReactivityFeature -export * from './features/table-reactivity/table-reactivity' - // columnFacetingFeature export * from './features/column-faceting/columnFacetingFeature' export * from './features/column-faceting/columnFacetingFeature.types' diff --git a/packages/table-core/src/reactivity.ts b/packages/table-core/src/reactivity.ts new file mode 100644 index 0000000000..86ad4e0347 --- /dev/null +++ b/packages/table-core/src/reactivity.ts @@ -0,0 +1,3 @@ +export * from './core/reactivity/coreReactivityFeature.types' +export * from './core/reactivity/constructReactivityBindings' +export * from './core/reactivity/coreReactivityFeature.utils' diff --git a/packages/table-core/tests/helpers/generateTestTable.ts b/packages/table-core/tests/helpers/generateTestTable.ts index 3ebb3860b4..d0b01c0ffa 100644 --- a/packages/table-core/tests/helpers/generateTestTable.ts +++ b/packages/table-core/tests/helpers/generateTestTable.ts @@ -1,7 +1,7 @@ import { constructTable, coreFeatures } from '../../src' import { generateTestColumnDefs } from '../fixtures/data/generateTestColumnDefs' import { generateTestData } from '../fixtures/data/generateTestData' -import { tanstackSignals } from '../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../src/core/reactivity/defaultReactivityBindings' import type { Row, Table, @@ -27,7 +27,7 @@ export function generateTestTableWithData( return constructTable({ data, columns, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), getSubRows: (row: Row) => row.subRows, ...options, _features: { @@ -45,7 +45,7 @@ export function generateTestTableFromData( return constructTable({ data, columns, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), ...options, _features: { ...coreFeatures, diff --git a/packages/table-core/tests/helpers/rowPinningHelpers.ts b/packages/table-core/tests/helpers/rowPinningHelpers.ts index 4f13ed7660..7787b068f1 100644 --- a/packages/table-core/tests/helpers/rowPinningHelpers.ts +++ b/packages/table-core/tests/helpers/rowPinningHelpers.ts @@ -7,7 +7,7 @@ import { rowPinningFeature, } from '../../src' import { generateTestData } from '../fixtures/data/generateTestData' -import { tanstackSignals } from '../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../src/core/reactivity/defaultReactivityBindings' import { generateTestTableWithData } from './generateTestTable' import type { ColumnDef, RowPinningState, TableOptions } from '../../src' import type { Person } from '../fixtures/data/types' @@ -78,7 +78,7 @@ export function createRowPinningTable( const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), data, columns, getSubRows: (row: any) => row.subRows, diff --git a/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts b/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts index b688e272d7..5a0aff5efa 100644 --- a/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts +++ b/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts @@ -12,7 +12,7 @@ import { createTableWithMockOnPinningChange, } from '../../../helpers/rowPinningHelpers' import { generateTestData } from '../../../fixtures/data/generateTestData' -import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' import type { ColumnDef, Row } from '../../../../src' import type { Person } from '../../../fixtures/data/types' @@ -177,7 +177,7 @@ describe('table methods', () => { _rowModels: { paginatedRowModel: createPaginatedRowModel(), }, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), data, columns, getSubRows: (row) => row.subRows, @@ -217,7 +217,7 @@ describe('table methods', () => { _rowModels: { paginatedRowModel: createPaginatedRowModel(), }, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), data, columns, getSubRows: (row) => row.subRows, diff --git a/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts b/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts index 6634941df8..2cab56cd89 100644 --- a/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts +++ b/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts @@ -6,7 +6,7 @@ import { } from '../../../../src' import * as RowSelectionUtils from '../../../../src/features/row-selection/rowSelectionFeature.utils' import { generateTestData } from '../../../fixtures/data/generateTestData' -import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' import type { Person } from '../../../fixtures/data/types' import type { ColumnDef } from '../../../../src' @@ -42,7 +42,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -72,7 +72,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -102,7 +102,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -129,7 +129,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -155,7 +155,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -181,7 +181,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -207,7 +207,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -229,7 +229,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -251,7 +251,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -276,7 +276,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -303,7 +303,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -330,7 +330,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: (row) => row.index === 0, // only first row is selectable (of 2 sub-rows) renderFallbackValue: '', data, @@ -356,7 +356,7 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, diff --git a/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts b/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts index 1f374786f8..2fce5b2332 100644 --- a/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts +++ b/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts @@ -8,7 +8,7 @@ import { } from '../../../../src' import { createColumnHelper } from '../../../../src/helpers/columnHelper' import { generateTestData } from '../../../fixtures/data/generateTestData' -import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' import type { Person } from '../../../fixtures/data/types' import type { ColumnDef } from '../../../../src' @@ -47,7 +47,7 @@ describe('#getGroupedRowModel', () => { _rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns), }, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), onStateChange() {}, renderFallbackValue: '', data, diff --git a/packages/table-core/tests/unit/core/columns/constructColumn.test.ts b/packages/table-core/tests/unit/core/columns/constructColumn.test.ts index 063f192bff..873de5069d 100644 --- a/packages/table-core/tests/unit/core/columns/constructColumn.test.ts +++ b/packages/table-core/tests/unit/core/columns/constructColumn.test.ts @@ -2,14 +2,14 @@ import { describe, expect, it } from 'vitest' import { coreColumnsFeature } from '../../../../src/core/columns/coreColumnsFeature' import { constructColumn } from '../../../../src/core/columns/constructColumn' import { constructTable } from '../../../../src' -import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' import type { ColumnDef } from '../../../../src/types/ColumnDef' describe('constructColumn', () => { it('should create a column with all core column APIs and properties', () => { const table = constructTable({ _features: { coreColumnsFeature }, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), columns: [] as Array, data: [] as Array, }) diff --git a/packages/table-core/tests/unit/core/table/constructTable.test.ts b/packages/table-core/tests/unit/core/table/constructTable.test.ts index da10f2048e..7b8a396600 100644 --- a/packages/table-core/tests/unit/core/table/constructTable.test.ts +++ b/packages/table-core/tests/unit/core/table/constructTable.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import { constructTable, coreFeatures } from '../../../../src' -import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' describe('constructTable', () => { it('should create a table with all core table APIs and properties', () => { @@ -8,7 +8,7 @@ describe('constructTable', () => { _features: { ...coreFeatures, }, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), columns: [], data: [], }) diff --git a/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts b/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts index 3af707bdb9..42e0270065 100644 --- a/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts +++ b/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts @@ -1,12 +1,12 @@ import { describe, expect, it } from 'vitest' import { constructTable, stockFeatures } from '../../../../src' -import { tanstackSignals } from '../../../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' describe('constructTable with stockFeatures', () => { it('should include all feature states in initial state', () => { const table = constructTable({ _features: stockFeatures, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), columns: [], data: [], }) diff --git a/packages/table-core/tests/unit/core/tableAtoms.test.ts b/packages/table-core/tests/unit/core/tableAtoms.test.ts index 77699be9d2..f52cba66f7 100644 --- a/packages/table-core/tests/unit/core/tableAtoms.test.ts +++ b/packages/table-core/tests/unit/core/tableAtoms.test.ts @@ -6,7 +6,7 @@ import { rowSelectionFeature, rowSortingFeature, } from '../../../src' -import { tanstackSignals } from '../../../src/features/table-reactivity/tanstack-signals' +import { defaultReactivityBindings } from '../../../src/core/reactivity/defaultReactivityBindings' import type { PaginationState, SortingState, @@ -23,7 +23,7 @@ function makeTable(options: any = {}) { return constructTable({ _features, _rowModels: {}, - reactivity: tanstackSignals(), + reactivity: defaultReactivityBindings(), columns: [], data: [], ...options, diff --git a/packages/table-core/tsdown.config.ts b/packages/table-core/tsdown.config.ts index 533e215e93..ff0c1b884d 100644 --- a/packages/table-core/tsdown.config.ts +++ b/packages/table-core/tsdown.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ './src/index.ts', './src/static-functions.ts', './src/flex-render.ts', - './src/features/table-reactivity/tanstack-signals', + './src/reactivity.ts', ], format: ['esm', 'cjs'], unbundle: true, diff --git a/packages/vue-table/src/signals.ts b/packages/vue-table/src/reactivity.ts similarity index 97% rename from packages/vue-table/src/signals.ts rename to packages/vue-table/src/reactivity.ts index af3b09cfaa..7c1ba3788c 100644 --- a/packages/vue-table/src/signals.ts +++ b/packages/vue-table/src/reactivity.ts @@ -2,7 +2,7 @@ import { computed, shallowRef, watch } from 'vue' import type { TableAtomOptions, TableReactivityBindings, -} from '@tanstack/table-core' +} from '@tanstack/table-core/reactivity' import type { Atom, Observer, ReadonlyAtom } from '@tanstack/vue-store' import type { ComputedRef, ShallowRef } from 'vue' diff --git a/packages/vue-table/src/useTable.ts b/packages/vue-table/src/useTable.ts index 3ff7822c10..f7ddef6c8b 100644 --- a/packages/vue-table/src/useTable.ts +++ b/packages/vue-table/src/useTable.ts @@ -2,7 +2,7 @@ import { unref, watch } from 'vue' import { constructTable } from '@tanstack/table-core' import { shallow, useSelector } from '@tanstack/vue-store' import { mergeProxy } from './merge-proxy' -import { vueReactivity } from './signals' +import { vueReactivity } from './reactivity' import type { Atom, ReadonlyAtom } from '@tanstack/vue-store' import type { NoInfer, @@ -10,7 +10,6 @@ import type { Table, TableFeatures, TableOptions, - TableReactivityBindings, TableState, } from '@tanstack/table-core' import type { MaybeRef, VNode } from 'vue' @@ -117,7 +116,10 @@ export function useTable< const mergedOptions = { ...tableOptions, - reactivity: tableOptions.reactivity ?? vueReactivity(), + _features: { + coreReativityFeature: vueReactivity(), + ...tableOptions._features, + }, } const resolvedOptions = mergeProxy( @@ -133,7 +135,7 @@ export function useTable< return mergeProxy(defaultOptions, newOptions) }, }, - ) as TableOptions & { reactivity: TableReactivityBindings } + ) as TableOptions const table = constructTable(resolvedOptions) as VueTable< TFeatures, From bece51373f0b057280b00eab2a70e0d858bfb6a1 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 2 May 2026 21:29:29 -0500 Subject: [PATCH 10/14] fix preact filter components --- .../src/components/header-components.tsx | 2 +- examples/preact/custom-plugin/src/main.tsx | 6 +++--- examples/preact/expanding/src/main.tsx | 6 +++--- examples/preact/filters-faceted/src/main.tsx | 2 +- examples/preact/filters-fuzzy/src/main.tsx | 2 +- examples/preact/filters/src/main.tsx | 2 +- examples/preact/pagination/src/main.tsx | 6 +++--- examples/preact/row-pinning/src/main.tsx | 6 +++--- examples/preact/row-selection/src/main.tsx | 8 ++++---- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/preact/composable-tables/src/components/header-components.tsx b/examples/preact/composable-tables/src/components/header-components.tsx index c86bc2842d..d3e7309f90 100644 --- a/examples/preact/composable-tables/src/components/header-components.tsx +++ b/examples/preact/composable-tables/src/components/header-components.tsx @@ -35,7 +35,7 @@ export function ColumnFilter() { + onInput={(e) => header.column.setFilterValue((e.target as HTMLInputElement).value) } placeholder={`Filter ${header.column.id}...`} diff --git a/examples/preact/custom-plugin/src/main.tsx b/examples/preact/custom-plugin/src/main.tsx index e3e984b5ad..41fb2d075b 100644 --- a/examples/preact/custom-plugin/src/main.tsx +++ b/examples/preact/custom-plugin/src/main.tsx @@ -345,7 +345,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: [number, number]) => [ (e.target as HTMLInputElement).value, old[1], @@ -357,7 +357,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: [number, number]) => [ old[0], (e.target as HTMLInputElement).value, @@ -371,7 +371,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((e.target as HTMLInputElement).value) } placeholder={`Search...`} diff --git a/examples/preact/expanding/src/main.tsx b/examples/preact/expanding/src/main.tsx index bf2fd26a15..16ac1f9d7f 100644 --- a/examples/preact/expanding/src/main.tsx +++ b/examples/preact/expanding/src/main.tsx @@ -284,7 +284,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: [number, number] | undefined) => [ (e.target as HTMLInputElement).value, old?.[1], @@ -296,7 +296,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: [number, number] | undefined) => [ old?.[0], (e.target as HTMLInputElement).value, @@ -310,7 +310,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((e.target as HTMLInputElement).value) } placeholder={`Search...`} diff --git a/examples/preact/filters-faceted/src/main.tsx b/examples/preact/filters-faceted/src/main.tsx index e05dd41964..483928948d 100644 --- a/examples/preact/filters-faceted/src/main.tsx +++ b/examples/preact/filters-faceted/src/main.tsx @@ -375,7 +375,7 @@ function DebouncedInput({ setValue((e.target as HTMLInputElement).value)} + onInput={(e) => setValue((e.target as HTMLInputElement).value)} /> ) } diff --git a/examples/preact/filters-fuzzy/src/main.tsx b/examples/preact/filters-fuzzy/src/main.tsx index 9ac6453d46..c8d31d641e 100644 --- a/examples/preact/filters-fuzzy/src/main.tsx +++ b/examples/preact/filters-fuzzy/src/main.tsx @@ -341,7 +341,7 @@ function DebouncedInput({ setValue((e.target as HTMLInputElement).value)} + onInput={(e) => setValue((e.target as HTMLInputElement).value)} /> ) } diff --git a/examples/preact/filters/src/main.tsx b/examples/preact/filters/src/main.tsx index d7c070c810..dc1fecac8f 100644 --- a/examples/preact/filters/src/main.tsx +++ b/examples/preact/filters/src/main.tsx @@ -349,7 +349,7 @@ function DebouncedInput({ setValue((e.target as HTMLInputElement).value)} + onInput={(e) => setValue((e.target as HTMLInputElement).value)} /> ) } diff --git a/examples/preact/pagination/src/main.tsx b/examples/preact/pagination/src/main.tsx index ecf586f25d..5f738d6bc0 100644 --- a/examples/preact/pagination/src/main.tsx +++ b/examples/preact/pagination/src/main.tsx @@ -256,7 +256,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: [number, number]) => [ (e.target as HTMLInputElement).value, old[1], @@ -268,7 +268,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: [number, number]) => [ old[0], (e.target as HTMLInputElement).value, @@ -281,7 +281,7 @@ function Filter({ ) : ( + onInput={(e) => column.setFilterValue((e.target as HTMLInputElement).value) } onClick={(e) => e.stopPropagation()} diff --git a/examples/preact/row-pinning/src/main.tsx b/examples/preact/row-pinning/src/main.tsx index 3afa2cbab6..1b9b139bff 100644 --- a/examples/preact/row-pinning/src/main.tsx +++ b/examples/preact/row-pinning/src/main.tsx @@ -406,7 +406,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: any) => [ (e.target as HTMLInputElement).value, old?.[1], @@ -418,7 +418,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: any) => [ old?.[0], (e.target as HTMLInputElement).value, @@ -432,7 +432,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((e.target as HTMLInputElement).value) } placeholder={`Search...`} diff --git a/examples/preact/row-selection/src/main.tsx b/examples/preact/row-selection/src/main.tsx index 69c988f510..fa79a39781 100644 --- a/examples/preact/row-selection/src/main.tsx +++ b/examples/preact/row-selection/src/main.tsx @@ -152,7 +152,7 @@ function App() {
+ onInput={(e) => table.setGlobalFilter((e.target as HTMLInputElement).value) } className="summary-panel" @@ -363,7 +363,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: any) => [ (e.target as HTMLInputElement).value, old?.[1], @@ -375,7 +375,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((old: any) => [ old?.[0], (e.target as HTMLInputElement).value, @@ -389,7 +389,7 @@ function Filter({ + onInput={(e) => column.setFilterValue((e.target as HTMLInputElement).value) } placeholder={`Search...`} From 166bf9f9344a151f849b7e35e3c7117e7db3960c Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 2 May 2026 21:50:46 -0500 Subject: [PATCH 11/14] fix tests --- .../tests/unit/defaultReactivity.test.ts | 2 +- .../src/core/table/coreTablesFeature.types.ts | 8 ++-- .../src/core/table/coreTablesFeature.utils.ts | 2 +- .../table-core/src/types/TableFeatures.ts | 14 +++---- .../tests/helpers/generateTestTable.ts | 6 +-- .../tests/helpers/rowPinningHelpers.ts | 4 +- .../row-pinning/rowPinningFeature.test.ts | 11 +++--- .../row-selection/rowSelectionFeature.test.ts | 39 +++++++------------ .../columnGroupingFeature.test.ts | 9 +++-- .../unit/core/columns/constructColumn.test.ts | 8 ++-- .../unit/core/table/constructTable.test.ts | 4 +- .../table/stockFeaturesInitialState.test.ts | 8 ++-- .../tests/unit/core/tableAtoms.test.ts | 10 +++-- packages/vue-table/tests/unit/signals.test.ts | 2 +- 14 files changed, 65 insertions(+), 62 deletions(-) diff --git a/packages/lit-table/tests/unit/defaultReactivity.test.ts b/packages/lit-table/tests/unit/defaultReactivity.test.ts index 8ae0bdcffe..9d8e572681 100644 --- a/packages/lit-table/tests/unit/defaultReactivity.test.ts +++ b/packages/lit-table/tests/unit/defaultReactivity.test.ts @@ -16,7 +16,7 @@ describe('TableController', () => { data: [], }) - expect(table.reactivity).toBeDefined() + expect(table._reactivity).toBeDefined() expect(table.store.get()).toEqual({}) }) }) diff --git a/packages/table-core/src/core/table/coreTablesFeature.types.ts b/packages/table-core/src/core/table/coreTablesFeature.types.ts index 2d831838e5..5e0bcab3e3 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.types.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.types.ts @@ -122,11 +122,11 @@ export interface Table_CoreProperties< /** * Prototype cache for Cell objects - shared by all cells in this table */ - readonly _cellPrototype?: object + _cellPrototype?: object /** * Prototype cache for Column objects - shared by all columns in this table */ - readonly _columnPrototype?: object + _columnPrototype?: object /** * The features that are enabled for the table. */ @@ -134,7 +134,7 @@ export interface Table_CoreProperties< /** * Prototype cache for Header objects - shared by all headers in this table */ - readonly _headerPrototype?: object + _headerPrototype?: object /** * The row model processing functions that are used to process the data by features. */ @@ -146,7 +146,7 @@ export interface Table_CoreProperties< /** * Prototype cache for Row objects - shared by all rows in this table */ - readonly _rowPrototype?: object + _rowPrototype?: object /** * The readonly derived atoms for each `TableState` slice. Each derives from * its corresponding `baseAtom` plus, optionally, a per-slice external atom or diff --git a/packages/table-core/src/core/table/coreTablesFeature.utils.ts b/packages/table-core/src/core/table/coreTablesFeature.utils.ts index 2d93b16d9d..01872f17ed 100644 --- a/packages/table-core/src/core/table/coreTablesFeature.utils.ts +++ b/packages/table-core/src/core/table/coreTablesFeature.utils.ts @@ -9,7 +9,7 @@ export function table_reset< TData extends RowData, >(table: Table_Internal): void { const snap = cloneState(table.initialState) - table.reactivity.batch(() => { + table._reactivity.batch(() => { for (const key of Object.keys(snap) as Array) { ;(table.baseAtoms as any)[key].set(snap[key] as any) } diff --git a/packages/table-core/src/types/TableFeatures.ts b/packages/table-core/src/types/TableFeatures.ts index 85f23d3082..d55702f6e4 100644 --- a/packages/table-core/src/types/TableFeatures.ts +++ b/packages/table-core/src/types/TableFeatures.ts @@ -12,13 +12,13 @@ export type ExtractFeatureTypes< TFeatures extends TableFeatures, > = UnionToIntersection< { - [K in keyof TFeatures]: TFeatures[K] extends TableFeature< - infer FeatureConstructorOptions - > - ? TKey extends keyof FeatureConstructorOptions - ? FeatureConstructorOptions[TKey] - : never - : any + [K in keyof TFeatures]: K extends 'coreReativityFeature' + ? never + : TFeatures[K] extends TableFeature + ? TKey extends keyof FeatureConstructorOptions + ? FeatureConstructorOptions[TKey] + : never + : any }[keyof TFeatures] > diff --git a/packages/table-core/tests/helpers/generateTestTable.ts b/packages/table-core/tests/helpers/generateTestTable.ts index d0b01c0ffa..3f47ee2141 100644 --- a/packages/table-core/tests/helpers/generateTestTable.ts +++ b/packages/table-core/tests/helpers/generateTestTable.ts @@ -1,7 +1,7 @@ import { constructTable, coreFeatures } from '../../src' import { generateTestColumnDefs } from '../fixtures/data/generateTestColumnDefs' import { generateTestData } from '../fixtures/data/generateTestData' -import { defaultReactivityBindings } from '../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../src/core/reactivity/constructReactivityBindings' import type { Row, Table, @@ -27,12 +27,12 @@ export function generateTestTableWithData( return constructTable({ data, columns, - reactivity: defaultReactivityBindings(), getSubRows: (row: Row) => row.subRows, ...options, _features: { ...coreFeatures, ...options?._features, + coreReativityFeature: constructReactivityBindings(), }, } as any) } @@ -45,11 +45,11 @@ export function generateTestTableFromData( return constructTable({ data, columns, - reactivity: defaultReactivityBindings(), ...options, _features: { ...coreFeatures, ...options?._features, + coreReativityFeature: constructReactivityBindings(), }, } as any) } diff --git a/packages/table-core/tests/helpers/rowPinningHelpers.ts b/packages/table-core/tests/helpers/rowPinningHelpers.ts index 7787b068f1..8537623555 100644 --- a/packages/table-core/tests/helpers/rowPinningHelpers.ts +++ b/packages/table-core/tests/helpers/rowPinningHelpers.ts @@ -7,7 +7,7 @@ import { rowPinningFeature, } from '../../src' import { generateTestData } from '../fixtures/data/generateTestData' -import { defaultReactivityBindings } from '../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../src/core/reactivity/constructReactivityBindings' import { generateTestTableWithData } from './generateTestTable' import type { ColumnDef, RowPinningState, TableOptions } from '../../src' import type { Person } from '../fixtures/data/types' @@ -16,6 +16,7 @@ import type { Person } from '../fixtures/data/types' const _features = { ...coreFeatures, rowPinningFeature, + coreReativityFeature: constructReactivityBindings(), } as any type personKeys = keyof Person @@ -78,7 +79,6 @@ export function createRowPinningTable( const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), data, columns, getSubRows: (row: any) => row.subRows, diff --git a/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts b/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts index 5a0aff5efa..c5e4570e71 100644 --- a/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts +++ b/packages/table-core/tests/implementation/features/row-pinning/rowPinningFeature.test.ts @@ -12,7 +12,7 @@ import { createTableWithMockOnPinningChange, } from '../../../helpers/rowPinningHelpers' import { generateTestData } from '../../../fixtures/data/generateTestData' -import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../../../src/core/reactivity/constructReactivityBindings' import type { ColumnDef, Row } from '../../../../src' import type { Person } from '../../../fixtures/data/types' @@ -20,6 +20,7 @@ import type { Person } from '../../../fixtures/data/types' const _features = { ...coreFeatures, rowPinningFeature, + coreReativityFeature: constructReactivityBindings(), } type personKeys = keyof Person @@ -170,6 +171,7 @@ describe('table methods', () => { ...coreFeatures, rowPinningFeature, rowPaginationFeature, + coreReativityFeature: constructReactivityBindings(), } const table = constructTable({ @@ -177,10 +179,9 @@ describe('table methods', () => { _rowModels: { paginatedRowModel: createPaginatedRowModel(), }, - reactivity: defaultReactivityBindings(), data, columns, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, enableRowPinning: true, renderFallbackValue: '', initialState: { @@ -210,6 +211,7 @@ describe('table methods', () => { ...coreFeatures, rowPinningFeature, rowPaginationFeature, + coreReativityFeature: constructReactivityBindings(), } const table = constructTable({ @@ -217,10 +219,9 @@ describe('table methods', () => { _rowModels: { paginatedRowModel: createPaginatedRowModel(), }, - reactivity: defaultReactivityBindings(), data, columns, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, enableRowPinning: true, renderFallbackValue: '', initialState: { diff --git a/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts b/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts index 2cab56cd89..e5d661d2d3 100644 --- a/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts +++ b/packages/table-core/tests/implementation/features/row-selection/rowSelectionFeature.test.ts @@ -1,19 +1,22 @@ import { describe, expect, it } from 'vitest' import { constructTable, + coreFeatures, createColumnHelper, rowSelectionFeature, } from '../../../../src' import * as RowSelectionUtils from '../../../../src/features/row-selection/rowSelectionFeature.utils' import { generateTestData } from '../../../fixtures/data/generateTestData' -import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../../../src/core/reactivity/constructReactivityBindings' import type { Person } from '../../../fixtures/data/types' -import type { ColumnDef } from '../../../../src' +import type { ColumnDef, Row } from '../../../../src' // TODO: bring up to new test structure const _features = { + ...coreFeatures, rowSelectionFeature, + coreReativityFeature: constructReactivityBindings(), } type personKeys = keyof Person @@ -42,11 +45,10 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: { '0': true, @@ -72,11 +74,10 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: { '0': true, @@ -102,11 +103,10 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: {}, }, @@ -129,7 +129,6 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -155,7 +154,6 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -181,7 +179,6 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -207,7 +204,6 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -229,7 +225,6 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, @@ -251,11 +246,10 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: {}, }, @@ -276,11 +270,10 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: { '0.0': true, @@ -303,11 +296,10 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: { '0.0': true, @@ -330,11 +322,11 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), - enableRowSelection: (row) => row.index === 0, // only first row is selectable (of 2 sub-rows) + enableRowSelection: (row: Row) => + row.index === 0, // only first row is selectable (of 2 sub-rows) renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: { '0.0': true, // first sub-row @@ -356,11 +348,10 @@ describe('rowSelectionFeature', () => { const table = constructTable({ _features, _rowModels: {}, - reactivity: defaultReactivityBindings(), enableRowSelection: true, renderFallbackValue: '', data, - getSubRows: (row) => row.subRows, + getSubRows: (originalRow: Person, _idx: number) => originalRow.subRows, initialState: { rowSelection: { '0.0.0': true, // first nested sub-row diff --git a/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts b/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts index 2fce5b2332..89525337d3 100644 --- a/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts +++ b/packages/table-core/tests/performance/features/column-grouping/columnGroupingFeature.test.ts @@ -8,7 +8,7 @@ import { } from '../../../../src' import { createColumnHelper } from '../../../../src/helpers/columnHelper' import { generateTestData } from '../../../fixtures/data/generateTestData' -import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../../../src/core/reactivity/constructReactivityBindings' import type { Person } from '../../../fixtures/data/types' import type { ColumnDef } from '../../../../src' @@ -43,11 +43,14 @@ describe('#getGroupedRowModel', () => { data.forEach((p) => (p.age = 123)) const table = constructTable({ - _features: { columnGroupingFeature, ...coreFeatures }, + _features: { + columnGroupingFeature, + ...coreFeatures, + coreReativityFeature: constructReactivityBindings(), + }, _rowModels: { groupedRowModel: createGroupedRowModel(aggregationFns), }, - reactivity: defaultReactivityBindings(), onStateChange() {}, renderFallbackValue: '', data, diff --git a/packages/table-core/tests/unit/core/columns/constructColumn.test.ts b/packages/table-core/tests/unit/core/columns/constructColumn.test.ts index 873de5069d..cab80dec67 100644 --- a/packages/table-core/tests/unit/core/columns/constructColumn.test.ts +++ b/packages/table-core/tests/unit/core/columns/constructColumn.test.ts @@ -2,14 +2,16 @@ import { describe, expect, it } from 'vitest' import { coreColumnsFeature } from '../../../../src/core/columns/coreColumnsFeature' import { constructColumn } from '../../../../src/core/columns/constructColumn' import { constructTable } from '../../../../src' -import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../../../src/core/reactivity/constructReactivityBindings' import type { ColumnDef } from '../../../../src/types/ColumnDef' describe('constructColumn', () => { it('should create a column with all core column APIs and properties', () => { const table = constructTable({ - _features: { coreColumnsFeature }, - reactivity: defaultReactivityBindings(), + _features: { + coreColumnsFeature, + coreReativityFeature: constructReactivityBindings(), + }, columns: [] as Array, data: [] as Array, }) diff --git a/packages/table-core/tests/unit/core/table/constructTable.test.ts b/packages/table-core/tests/unit/core/table/constructTable.test.ts index 7b8a396600..cf9a489d95 100644 --- a/packages/table-core/tests/unit/core/table/constructTable.test.ts +++ b/packages/table-core/tests/unit/core/table/constructTable.test.ts @@ -1,14 +1,14 @@ import { describe, expect, it } from 'vitest' import { constructTable, coreFeatures } from '../../../../src' -import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../../../src/core/reactivity/constructReactivityBindings' describe('constructTable', () => { it('should create a table with all core table APIs and properties', () => { const table = constructTable({ _features: { ...coreFeatures, + coreReativityFeature: constructReactivityBindings(), }, - reactivity: defaultReactivityBindings(), columns: [], data: [], }) diff --git a/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts b/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts index 42e0270065..e14704da3e 100644 --- a/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts +++ b/packages/table-core/tests/unit/core/table/stockFeaturesInitialState.test.ts @@ -1,12 +1,14 @@ import { describe, expect, it } from 'vitest' import { constructTable, stockFeatures } from '../../../../src' -import { defaultReactivityBindings } from '../../../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../../../src/core/reactivity/constructReactivityBindings' describe('constructTable with stockFeatures', () => { it('should include all feature states in initial state', () => { const table = constructTable({ - _features: stockFeatures, - reactivity: defaultReactivityBindings(), + _features: { + ...stockFeatures, + coreReativityFeature: constructReactivityBindings(), + }, columns: [], data: [], }) diff --git a/packages/table-core/tests/unit/core/tableAtoms.test.ts b/packages/table-core/tests/unit/core/tableAtoms.test.ts index f52cba66f7..b10ba9f200 100644 --- a/packages/table-core/tests/unit/core/tableAtoms.test.ts +++ b/packages/table-core/tests/unit/core/tableAtoms.test.ts @@ -2,11 +2,12 @@ import { describe, expect, it, vi } from 'vitest' import { createAtom } from '@tanstack/store' import { constructTable, + coreFeatures, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, } from '../../../src' -import { defaultReactivityBindings } from '../../../src/core/reactivity/defaultReactivityBindings' +import { constructReactivityBindings } from '../../../src/core/reactivity/constructReactivityBindings' import type { PaginationState, SortingState, @@ -21,9 +22,12 @@ const _features = { function makeTable(options: any = {}) { return constructTable({ - _features, + _features: { + ...coreFeatures, + ..._features, + coreReativityFeature: constructReactivityBindings(), + }, _rowModels: {}, - reactivity: defaultReactivityBindings(), columns: [], data: [], ...options, diff --git a/packages/vue-table/tests/unit/signals.test.ts b/packages/vue-table/tests/unit/signals.test.ts index 49b8ddde34..49698eb2ae 100644 --- a/packages/vue-table/tests/unit/signals.test.ts +++ b/packages/vue-table/tests/unit/signals.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'vitest' import { nextTick } from 'vue' -import { vueReactivity } from '../../src/signals' +import { vueReactivity } from '../../src/reactivity' describe('vueReactivity', () => { test('creates writable and readonly atoms from Vue refs', async () => { From ae7eb1fa50894e227aef76e513e0000c50d88122 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 2 May 2026 22:04:25 -0500 Subject: [PATCH 12/14] update useLegacyTable selector --- packages/react-table/src/useLegacyTable.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/react-table/src/useLegacyTable.ts b/packages/react-table/src/useLegacyTable.ts index eec0d424da..adad4681d7 100644 --- a/packages/react-table/src/useLegacyTable.ts +++ b/packages/react-table/src/useLegacyTable.ts @@ -461,11 +461,7 @@ export function useLegacyTable( const getState = useCallback(() => { // all state except for columns and data - return { - ...table.state, - columns: undefined, - data: undefined, - } + return table.state }, [table]) const setState = useCallback( From 781e5bc2f84a31fdd6360aa0ca92770a6c3073d7 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 2 May 2026 22:23:16 -0500 Subject: [PATCH 13/14] fix useLegacyTable infinite re-render bug --- packages/react-table/src/useLegacyTable.ts | 97 +++++++++++----------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/packages/react-table/src/useLegacyTable.ts b/packages/react-table/src/useLegacyTable.ts index adad4681d7..5a6c0d5098 100644 --- a/packages/react-table/src/useLegacyTable.ts +++ b/packages/react-table/src/useLegacyTable.ts @@ -15,7 +15,7 @@ import { sortFns, stockFeatures, } from '@tanstack/table-core' -import { useCallback, useMemo } from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTable } from './useTable' import type { AggregationFns, @@ -402,52 +402,55 @@ export function useLegacyTable( ...restOptions } = options - // Build the _rowModels object based on which legacy options were provided - const _rowModels: CreateRowModels_All = {} - - // Map v8 row model factories to v9 _rowModels - // Note: getCoreRowModel is handled automatically in v9, so we ignore it - - if (getFilteredRowModel) { - _rowModels.filteredRowModel = createFilteredRowModel({ - ...filterFns, - ...options.filterFns, - }) - } - - if (getSortedRowModel) { - _rowModels.sortedRowModel = createSortedRowModel({ - ...sortFns, - ...options.sortFns, - }) - } - - if (getPaginationRowModel) { - _rowModels.paginatedRowModel = createPaginatedRowModel() - } - - if (getExpandedRowModel) { - _rowModels.expandedRowModel = createExpandedRowModel() - } - - if (getGroupedRowModel) { - _rowModels.groupedRowModel = createGroupedRowModel({ - ...aggregationFns, - ...options.aggregationFns, - }) - } - - if (getFacetedRowModel) { - _rowModels.facetedRowModel = createFacetedRowModel() - } - - if (getFacetedMinMaxValues) { - _rowModels.facetedMinMaxValues = createFacetedMinMaxValues() - } - - if (getFacetedUniqueValues) { - _rowModels.facetedUniqueValues = createFacetedUniqueValues() - } + const [_rowModels] = useState(() => { + const rowModels: CreateRowModels_All = {} + + // Legacy row model options are setup-only. Capture the first render's + // marker options to match the table instance lifecycle. + + if (getFilteredRowModel) { + rowModels.filteredRowModel = createFilteredRowModel({ + ...filterFns, + ...options.filterFns, + }) + } + + if (getSortedRowModel) { + rowModels.sortedRowModel = createSortedRowModel({ + ...sortFns, + ...options.sortFns, + }) + } + + if (getPaginationRowModel) { + rowModels.paginatedRowModel = createPaginatedRowModel() + } + + if (getExpandedRowModel) { + rowModels.expandedRowModel = createExpandedRowModel() + } + + if (getGroupedRowModel) { + rowModels.groupedRowModel = createGroupedRowModel({ + ...aggregationFns, + ...options.aggregationFns, + }) + } + + if (getFacetedRowModel) { + rowModels.facetedRowModel = createFacetedRowModel() + } + + if (getFacetedMinMaxValues) { + rowModels.facetedMinMaxValues = createFacetedMinMaxValues() + } + + if (getFacetedUniqueValues) { + rowModels.facetedUniqueValues = createFacetedUniqueValues() + } + + return rowModels + }) // Call useTable with the v9 API, subscribing to all state changes const table = useTable>( From b804881ea2a690be240a24ac506748ef5ac632fa Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Sat, 2 May 2026 22:28:31 -0500 Subject: [PATCH 14/14] remove bad comment --- packages/react-table/src/useLegacyTable.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-table/src/useLegacyTable.ts b/packages/react-table/src/useLegacyTable.ts index 5a6c0d5098..daa0ad7474 100644 --- a/packages/react-table/src/useLegacyTable.ts +++ b/packages/react-table/src/useLegacyTable.ts @@ -463,7 +463,6 @@ export function useLegacyTable( ) const getState = useCallback(() => { - // all state except for columns and data return table.state }, [table])