@tanstack/angular-table 9.0.0-alpha.5 → 9.0.0-alpha.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +127 -0
  2. package/dist/README.md +127 -0
  3. package/dist/fesm2022/tanstack-angular-table-static-functions.mjs +6 -0
  4. package/dist/fesm2022/tanstack-angular-table-static-functions.mjs.map +1 -0
  5. package/dist/fesm2022/tanstack-angular-table.mjs +1357 -242
  6. package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
  7. package/dist/types/tanstack-angular-table-static-functions.d.ts +1 -0
  8. package/dist/types/tanstack-angular-table.d.ts +793 -0
  9. package/package.json +39 -19
  10. package/skills/angular/angular-rendering-directives/SKILL.md +415 -0
  11. package/skills/angular/angular-rendering-directives/references/content-shapes.md +142 -0
  12. package/skills/angular/angular-rendering-directives/references/create-table-hook-registries.md +89 -0
  13. package/skills/angular/angular-rendering-directives/references/di-tokens.md +171 -0
  14. package/skills/angular/angular-rendering-directives/references/flex-render-component-options.md +64 -0
  15. package/skills/angular/client-to-server/SKILL.md +467 -0
  16. package/skills/angular/compose-with-tanstack-query/SKILL.md +482 -0
  17. package/skills/angular/compose-with-tanstack-store/SKILL.md +397 -0
  18. package/skills/angular/compose-with-tanstack-virtual/SKILL.md +400 -0
  19. package/skills/angular/getting-started/SKILL.md +496 -0
  20. package/skills/angular/getting-started/references/feature-row-model-mapping.md +48 -0
  21. package/skills/angular/migrate-v8-to-v9/SKILL.md +419 -0
  22. package/skills/angular/migrate-v8-to-v9/references/v8-to-v9-mapping.md +261 -0
  23. package/skills/angular/production-readiness/SKILL.md +469 -0
  24. package/skills/angular/table-state/SKILL.md +429 -0
  25. package/skills/angular/table-state/references/external-atoms-and-app-hook.md +152 -0
  26. package/src/flex-render/context.ts +14 -0
  27. package/src/flex-render/flags.ts +34 -0
  28. package/src/flex-render/flexRenderComponent.ts +288 -0
  29. package/src/flex-render/flexRenderComponentFactory.ts +251 -0
  30. package/src/flex-render/renderer.ts +393 -0
  31. package/src/flex-render/view.ts +226 -0
  32. package/src/flexRender.ts +124 -0
  33. package/src/helpers/cell.ts +104 -0
  34. package/src/helpers/createTableHook.ts +499 -0
  35. package/src/helpers/flexRenderCell.ts +136 -0
  36. package/src/helpers/header.ts +99 -0
  37. package/src/helpers/table.ts +85 -0
  38. package/src/index.ts +23 -70
  39. package/src/injectTable.ts +206 -0
  40. package/src/{lazy-signal-initializer.ts → lazySignalInitializer.ts} +2 -2
  41. package/src/reactivity.ts +147 -0
  42. package/static-functions/index.ts +1 -0
  43. package/static-functions/ng-package.json +6 -0
  44. package/dist/esm2022/flex-render.mjs +0 -150
  45. package/dist/esm2022/index.mjs +0 -48
  46. package/dist/esm2022/lazy-signal-initializer.mjs +0 -43
  47. package/dist/esm2022/proxy.mjs +0 -83
  48. package/dist/esm2022/tanstack-angular-table.mjs +0 -5
  49. package/dist/flex-render.d.ts +0 -31
  50. package/dist/index.d.ts +0 -5
  51. package/dist/lazy-signal-initializer.d.ts +0 -5
  52. package/dist/proxy.d.ts +0 -3
  53. package/src/__tests__/createAngularTable.test.ts +0 -95
  54. package/src/__tests__/flex-render.test.ts +0 -178
  55. package/src/__tests__/lazy-init.test.ts +0 -124
  56. package/src/__tests__/test-setup.ts +0 -12
  57. package/src/__tests__/test-utils.ts +0 -62
  58. package/src/flex-render.ts +0 -187
  59. package/src/proxy.ts +0 -97
@@ -0,0 +1,99 @@
1
+ import { Directive, InjectionToken, inject, input } from '@angular/core'
2
+ import { CellData, Header, RowData, TableFeatures } from '@tanstack/table-core'
3
+ import type { Signal } from '@angular/core'
4
+
5
+ /**
6
+ * DI context shape for a TanStack Table header.
7
+ *
8
+ * This exists to make the current `Header` injectable by any nested component/directive
9
+ * without passing it through inputs/props.
10
+ */
11
+ export interface TanStackTableHeaderContext<
12
+ TFeatures extends TableFeatures,
13
+ TData extends RowData,
14
+ TValue extends CellData,
15
+ > {
16
+ /** Signal that returns the current header instance. */
17
+ header: Signal<Header<TFeatures, TData, TValue>>
18
+ }
19
+
20
+ /**
21
+ * Injection token that provides access to the current header.
22
+ *
23
+ * This token is provided by the {@link TanStackTableHeader} directive.
24
+ */
25
+ export const TanStackTableHeaderToken = new InjectionToken<
26
+ TanStackTableHeaderContext<any, any, any>['header']
27
+ >('[TanStack Table] HeaderContext')
28
+
29
+ /**
30
+ * Provides a TanStack Table `Header` instance in Angular DI.
31
+ *
32
+ * The header can be injected by:
33
+ * - any descendant of an element using `[tanStackTableHeader]="..."`
34
+ * - any component instantiated by `*flexRender` when the render props contains `header`
35
+ *
36
+ * @example
37
+ * ```html
38
+ * <th [tanStackTableHeader]="header">
39
+ * <app-sort-indicator />
40
+ * </th>
41
+ * ```
42
+ *
43
+ * ```ts
44
+ * @Component({
45
+ * selector: 'app-sort-indicator',
46
+ * template: `
47
+ * <button (click)="toggle()">
48
+ * {{ header().column.id }}
49
+ * </button>
50
+ * `,
51
+ * })
52
+ * export class SortIndicatorComponent {
53
+ * readonly header = injectTableHeaderContext()
54
+ *
55
+ * toggle() {
56
+ * this.header().column.toggleSorting()
57
+ * }
58
+ * }
59
+ * ```
60
+ */
61
+ @Directive({
62
+ selector: '[tanStackTableHeader]',
63
+ exportAs: 'header',
64
+ providers: [
65
+ {
66
+ provide: TanStackTableHeaderToken,
67
+ useFactory: () => inject(TanStackTableHeader).header,
68
+ },
69
+ ],
70
+ })
71
+ export class TanStackTableHeader<
72
+ TFeatures extends TableFeatures,
73
+ TData extends RowData,
74
+ TValue extends CellData,
75
+ > implements TanStackTableHeaderContext<TFeatures, TData, TValue> {
76
+ /**
77
+ * The current TanStack Table header.
78
+ *
79
+ * Provided as a required signal input so DI consumers always read the latest value.
80
+ */
81
+ readonly header = input.required<Header<TFeatures, TData, TValue>>({
82
+ alias: 'tanStackTableHeader',
83
+ })
84
+ }
85
+
86
+ /**
87
+ * Injects the current TanStack Table header signal.
88
+ *
89
+ * Available when:
90
+ * - there is a nearest `[tanStackTableHeader]` directive in the DI tree, or
91
+ * - the caller is rendered via `*flexRender` with render props containing `header`
92
+ */
93
+ export function injectTableHeaderContext<
94
+ TFeatures extends TableFeatures,
95
+ TData extends RowData,
96
+ TValue extends CellData,
97
+ >(): TanStackTableHeaderContext<TFeatures, TData, TValue>['header'] {
98
+ return inject(TanStackTableHeaderToken)
99
+ }
@@ -0,0 +1,85 @@
1
+ import { Directive, InjectionToken, inject, input } from '@angular/core'
2
+ import { RowData, TableFeatures } from '@tanstack/table-core'
3
+ import { AngularTable } from '../injectTable'
4
+ import type { Signal } from '@angular/core'
5
+
6
+ /**
7
+ * Injection token that provides access to the current {@link AngularTable} instance.
8
+ *
9
+ * This token is provided by the {@link TanStackTable} directive.
10
+ */
11
+ export const TanStackTableToken = new InjectionToken<
12
+ Signal<AngularTable<any, any>>
13
+ >('[TanStack Table] Table Context')
14
+
15
+ /**
16
+ * Provides a TanStack Table instance (`AngularTable`) in Angular DI.
17
+ *
18
+ * The table can be injected by:
19
+ * - any descendant of an element using `[tanStackTable]="..."`
20
+ * - any component instantiated by `*flexRender` when the render props contains `table`
21
+ *
22
+ * @example
23
+ * ```html
24
+ * <div [tanStackTable]="table">
25
+ * <app-pagination />
26
+ * </div>
27
+ * ```
28
+ *
29
+ * ```ts
30
+ * @Component({
31
+ * selector: 'app-pagination',
32
+ * template: `
33
+ * <button (click)="prev()" [disabled]="!table().getCanPreviousPage()">Prev</button>
34
+ * <button (click)="next()" [disabled]="!table().getCanNextPage()">Next</button>
35
+ * `,
36
+ * })
37
+ * export class PaginationComponent {
38
+ * readonly table = injectTableContext()
39
+ *
40
+ * prev() {
41
+ * this.table().previousPage()
42
+ * }
43
+ * next() {
44
+ * this.table().nextPage()
45
+ * }
46
+ * }
47
+ * ```
48
+ */
49
+ @Directive({
50
+ selector: '[tanStackTable]',
51
+ exportAs: 'table',
52
+ providers: [
53
+ {
54
+ provide: TanStackTableToken,
55
+ useFactory: () => inject(TanStackTable).table,
56
+ },
57
+ ],
58
+ })
59
+ export class TanStackTable<
60
+ TFeatures extends TableFeatures,
61
+ TData extends RowData,
62
+ > {
63
+ /**
64
+ * The current TanStack Table instance.
65
+ *
66
+ * Provided as a required signal input so DI consumers always read the latest value.
67
+ */
68
+ readonly table = input.required<AngularTable<TFeatures, TData>>({
69
+ alias: 'tanStackTable',
70
+ })
71
+ }
72
+
73
+ /**
74
+ * Injects the current TanStack Table instance signal.
75
+ *
76
+ * Available when:
77
+ * - there is a nearest `[tanStackTable]` directive in the DI tree, or
78
+ * - the caller is rendered via `*flexRender` with render props containing `table`
79
+ */
80
+ export function injectTableContext<
81
+ TFeatures extends TableFeatures,
82
+ TData extends RowData,
83
+ >(): Signal<AngularTable<TFeatures, TData>> {
84
+ return inject(TanStackTableToken)
85
+ }
package/src/index.ts CHANGED
@@ -1,73 +1,26 @@
1
- import { computed, type Signal, signal } from '@angular/core'
2
- import {
3
- RowData,
4
- TableOptions,
5
- TableOptionsResolved,
6
- TableState,
7
- createTable,
8
- type Table,
9
- } from '@tanstack/table-core'
10
- import { lazyInit } from './lazy-signal-initializer'
11
- import { proxifyTable } from './proxy'
1
+ import { FlexRenderCell } from './helpers/flexRenderCell'
2
+ import { FlexRenderDirective } from './flexRender'
12
3
 
13
4
  export * from '@tanstack/table-core'
14
5
 
15
- export {
16
- type FlexRenderContent,
17
- FlexRenderComponent,
18
- FlexRenderDirective,
19
- injectFlexRenderContext,
20
- } from './flex-render'
21
-
22
- export function createAngularTable<TData extends RowData>(
23
- options: () => TableOptions<TData>
24
- ): Table<TData> & Signal<Table<TData>> {
25
- return lazyInit(() => {
26
- const resolvedOptions = {
27
- state: {},
28
- onStateChange: () => {},
29
- renderFallbackValue: null,
30
- ...options(),
31
- }
32
-
33
- const table = createTable(resolvedOptions)
34
-
35
- // By default, manage table state here using the table's initial state
36
- const state = signal<TableState>(table.initialState)
37
-
38
- // Compose table options using computed.
39
- // This is to allow `tableSignal` to listen and set table option
40
- const updatedOptions = computed<TableOptionsResolved<TData>>(() => {
41
- // listen to table state changed
42
- const tableState = state()
43
- // listen to input options changed
44
- const tableOptions = options()
45
- return {
46
- ...table.options,
47
- ...resolvedOptions,
48
- ...tableOptions,
49
- state: { ...tableState, ...tableOptions.state },
50
- onStateChange: updater => {
51
- const value =
52
- updater instanceof Function ? updater(tableState) : updater
53
- state.set(value)
54
- resolvedOptions.onStateChange?.(updater)
55
- },
56
- }
57
- })
58
-
59
- // convert table instance to signal for proxify to listen to any table state and options changes
60
- const tableSignal = computed(
61
- () => {
62
- table.setOptions(updatedOptions())
63
- return table
64
- },
65
- {
66
- equal: () => false,
67
- }
68
- )
69
-
70
- // proxify Table instance to provide ability for consumer to listen to any table state changes
71
- return proxifyTable(tableSignal)
72
- })
73
- }
6
+ export * from './flexRender'
7
+ export * from './injectTable'
8
+ export * from './flex-render/flexRenderComponent'
9
+
10
+ export * from './helpers/cell'
11
+ export * from './helpers/header'
12
+ export * from './helpers/table'
13
+ export * from './helpers/createTableHook'
14
+ export * from './helpers/flexRenderCell'
15
+
16
+ /**
17
+ * Constant helper to import FlexRender directives.
18
+ *
19
+ * You should prefer to use this constant over importing the directives separately,
20
+ * as it ensures you always have the correct set of directives over library updates.
21
+ *
22
+ * @see {@link FlexRenderDirective} and {@link FlexRenderCell} for more details on the directives included in this export.
23
+ */
24
+ export const FlexRender = [FlexRenderDirective, FlexRenderCell] as const
25
+
26
+ export { shallow } from '@tanstack/angular-store'
@@ -0,0 +1,206 @@
1
+ import {
2
+ Injector,
3
+ NgZone,
4
+ assertInInjectionContext,
5
+ effect,
6
+ inject,
7
+ untracked,
8
+ } from '@angular/core'
9
+ import { constructTable } from '@tanstack/table-core'
10
+ import { lazyInit } from './lazySignalInitializer'
11
+ import { angularReactivity } from './reactivity'
12
+ import type {
13
+ Atom,
14
+ ReadonlyAtom,
15
+ ReadonlyStore,
16
+ Store,
17
+ } from '@tanstack/angular-store'
18
+ import type {
19
+ RowData,
20
+ Table,
21
+ TableFeatures,
22
+ TableOptions,
23
+ TableState,
24
+ } from '@tanstack/table-core'
25
+
26
+ export type SubscribeSource<TValue> =
27
+ | Atom<TValue>
28
+ | ReadonlyAtom<TValue>
29
+ | Store<TValue>
30
+ | ReadonlyStore<TValue>
31
+
32
+ export type AngularTable<
33
+ TFeatures extends TableFeatures,
34
+ TData extends RowData,
35
+ > = Table<TFeatures, TData> & {
36
+ /**
37
+ * @deprecated Prefer `table.atoms.<slice>.get()` for template/render reads
38
+ * of a specific state slice, `table.state` for full-state debug snapshots, or
39
+ * Angular computed values around explicit selectors. `table.store.state` is a
40
+ * current-value snapshot and is easy to misuse in render code.
41
+ */
42
+ readonly store: Table<TFeatures, TData>['store']
43
+ /**
44
+ * The current table state exposed as a flat proxy. Prefer
45
+ * `table.atoms.<slice>.get()` when reading a specific slice.
46
+ */
47
+ readonly state: Readonly<TableState<TFeatures>>
48
+ }
49
+
50
+ function createStateProxy<
51
+ TFeatures extends TableFeatures,
52
+ TData extends RowData,
53
+ >(table: Table<TFeatures, TData>): Readonly<TableState<TFeatures>> {
54
+ const getSnapshot = () => {
55
+ const snapshot = {} as TableState<TFeatures>
56
+ const stateKeys = Object.keys(table.initialState) as Array<
57
+ keyof TableState<TFeatures>
58
+ >
59
+
60
+ for (const key of stateKeys) {
61
+ snapshot[key] = table.atoms[key].get()
62
+ }
63
+
64
+ return snapshot
65
+ }
66
+
67
+ const target = {} as TableState<TFeatures>
68
+
69
+ return new Proxy(target, {
70
+ get(target, prop, receiver) {
71
+ if (prop === 'toJSON') {
72
+ return getSnapshot
73
+ }
74
+
75
+ if (typeof prop === 'string' && prop in table.initialState) {
76
+ return table.atoms[prop as keyof TableState<TFeatures>]?.get()
77
+ }
78
+
79
+ return Reflect.get(target, prop, receiver)
80
+ },
81
+ has(_, prop) {
82
+ return typeof prop === 'string' && prop in table.initialState
83
+ },
84
+ ownKeys() {
85
+ return Reflect.ownKeys(table.initialState)
86
+ },
87
+ getOwnPropertyDescriptor(_, prop) {
88
+ if (typeof prop !== 'string' || !(prop in table.initialState)) {
89
+ return undefined
90
+ }
91
+
92
+ return {
93
+ enumerable: true,
94
+ configurable: true,
95
+ }
96
+ },
97
+ })
98
+ }
99
+
100
+ /**
101
+ * Creates and returns an Angular-reactive table instance.
102
+ *
103
+ * The initializer is intentionally re-evaluated whenever any signal read inside it changes.
104
+ * This is how the adapter keeps the table in sync with Angular's reactivity model.
105
+ *
106
+ * Because of that behavior, keep expensive/static values (for example `columns`, feature setup, row models)
107
+ * as stable references outside the initializer, and only read reactive state (`data()`, pagination/filter/sorting signals, etc.)
108
+ * inside it.
109
+ *
110
+ * The returned table is also signal-reactive: table state and table APIs are wired for Angular signals, so you can safely consume table methods inside `computed(...)` and `effect(...)`.
111
+ *
112
+ * @example
113
+ * 1. Register the table features you need
114
+ * ```ts
115
+ * // Register only the features you need
116
+ * import {tableFeatures, rowPaginationFeature} from '@tanstack/angular-table';
117
+ * const _features = tableFeatures({
118
+ * rowPaginationFeature,
119
+ * // ...all other features you need
120
+ * })
121
+ *
122
+ * // Use all table core features
123
+ * import {stockFeatures} from '@tanstack/angular-table';
124
+ * const _features = tableFeatures(stockFeatures);
125
+ * ```
126
+ * 2. Prepare the table columns
127
+ * ```ts
128
+ * import {ColumnDef} from '@tanstack/angular-table';
129
+ *
130
+ * type MyData = {}
131
+ *
132
+ * const columns: ColumnDef<typeof _features, MyData>[] = [
133
+ * // ...column definitions
134
+ * ]
135
+ *
136
+ * // or using createColumnHelper
137
+ * import {createColumnHelper} from '@tanstack/angular-table';
138
+ * const columnHelper = createColumnHelper<typeof _features, MyData>();
139
+ * const columns = columnHelper.columns([
140
+ * columnHelper.accessor(...),
141
+ * // ...other columns
142
+ * ])
143
+ * ```
144
+ * 3. Create the table instance with `injectTable`
145
+ * ```ts
146
+ * const table = injectTable(() => {
147
+ * // ...table options,
148
+ * _features,
149
+ * columns: columns,
150
+ * data: myDataSignal(),
151
+ * })
152
+ * ```
153
+ *
154
+ * @returns An Angular-reactive TanStack Table instance.
155
+ */
156
+ export function injectTable<
157
+ TFeatures extends TableFeatures,
158
+ TData extends RowData,
159
+ >(
160
+ options: () => TableOptions<TFeatures, TData>,
161
+ ): AngularTable<TFeatures, TData> {
162
+ assertInInjectionContext(injectTable)
163
+ const injector = inject(Injector)
164
+ const ngZone = inject(NgZone)
165
+
166
+ return ngZone.runOutsideAngular(() =>
167
+ lazyInit(() => {
168
+ const table = constructTable({
169
+ ...options(),
170
+ _features: {
171
+ coreReativityFeature: angularReactivity(injector),
172
+ ...options()._features,
173
+ },
174
+ }) as AngularTable<TFeatures, TData>
175
+ const stateProxy = createStateProxy(table)
176
+
177
+ Object.defineProperty(table, 'state', {
178
+ get() {
179
+ return stateProxy
180
+ },
181
+ configurable: true,
182
+ enumerable: true,
183
+ })
184
+
185
+ let isMount = true
186
+ effect(
187
+ () => {
188
+ const newOptions = options()
189
+ if (isMount) {
190
+ isMount = false
191
+ return
192
+ }
193
+ untracked(() =>
194
+ table.setOptions((previous) => ({
195
+ ...previous,
196
+ ...newOptions,
197
+ })),
198
+ )
199
+ },
200
+ { injector, debugName: 'tableOptionsUpdate' },
201
+ )
202
+
203
+ return table
204
+ }),
205
+ )
206
+ }
@@ -2,7 +2,7 @@ import { untracked } from '@angular/core'
2
2
 
3
3
  /**
4
4
  * Implementation from @tanstack/angular-query
5
- * {@link https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/util/lazy-init/lazy-init.ts}
5
+ * {https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/util/lazy-init/lazy-init.ts}
6
6
  */
7
7
  export function lazyInit<T extends object>(initializer: () => T): T {
8
8
  let object: T | null = null
@@ -18,7 +18,7 @@ export function lazyInit<T extends object>(initializer: () => T): T {
18
18
  const table = () => {}
19
19
 
20
20
  return new Proxy<T>(table as T, {
21
- apply(target: T, thisArg: any, argArray: any[]): any {
21
+ apply(target: T, thisArg: any, argArray: Array<any>): any {
22
22
  initializeObject()
23
23
  if (typeof object === 'function') {
24
24
  return Reflect.apply(object, thisArg, argArray)
@@ -0,0 +1,147 @@
1
+ import {
2
+ DestroyRef,
3
+ NgZone,
4
+ computed,
5
+ effect,
6
+ signal,
7
+ untracked,
8
+ } from '@angular/core'
9
+ import { batch, createAtom } from '@tanstack/angular-store'
10
+ import type { Atom, Observer, ReadonlyAtom } from '@tanstack/angular-store'
11
+ import type {
12
+ TableAtomOptions,
13
+ TableReactivityBindings,
14
+ } from '@tanstack/table-core/reactivity'
15
+ import type { Injector, Signal, WritableSignal } from '@angular/core'
16
+
17
+ const optionsStoreDebugName = 'table/optionsStore'
18
+
19
+ function observerToCallback<T>(
20
+ observerOrNext: Observer<T> | ((value: T) => void),
21
+ ): (value: T) => void {
22
+ return typeof observerOrNext === 'function'
23
+ ? observerOrNext
24
+ : (value) => observerOrNext.next?.(value)
25
+ }
26
+
27
+ function signalToReadonlyAtom<T>(
28
+ source: Signal<T>,
29
+ getSource: () => T,
30
+ subscribeSource: (observerOrNext: Observer<T> | ((value: T) => void)) => {
31
+ unsubscribe: () => void
32
+ },
33
+ ): ReadonlyAtom<T> {
34
+ return Object.assign(source, {
35
+ get: () => {
36
+ const value = getSource()
37
+ source()
38
+ return value
39
+ },
40
+ subscribe: subscribeSource as ReadonlyAtom<T>['subscribe'],
41
+ })
42
+ }
43
+
44
+ function signalToWritableAtom<T>(
45
+ source: WritableSignal<T>,
46
+ injector: Injector,
47
+ ): Atom<T> {
48
+ const observers = new Set<(value: T) => void>()
49
+ let observed = false
50
+ effect(
51
+ () => {
52
+ const value = source()
53
+ if (!observed) {
54
+ observed = true
55
+ return
56
+ }
57
+ observers.forEach((observer) => observer(value))
58
+ },
59
+ { injector },
60
+ )
61
+
62
+ return Object.assign(source.asReadonly(), {
63
+ set: (updater: T | ((prevVal: T) => T)) => {
64
+ typeof updater === 'function'
65
+ ? source.update(updater as (val: T) => T)
66
+ : source.set(updater)
67
+ },
68
+ get: () => source(),
69
+ subscribe: ((observerOrNext: Observer<T> | ((value: T) => void)) => {
70
+ const observer = observerToCallback(observerOrNext)
71
+ observers.add(observer)
72
+
73
+ return {
74
+ unsubscribe: () => observers.delete(observer),
75
+ }
76
+ }) as Atom<T>['subscribe'],
77
+ })
78
+ }
79
+
80
+ /**
81
+ * Creates the table-core reactivity bindings used by the Angular adapter.
82
+ *
83
+ * Table state atoms are backed by TanStack Store atoms. The options store stays
84
+ * framework-native because row-model APIs read `table.options` directly during
85
+ * render. Readonly table atoms bridge Store dependency tracking into Angular
86
+ * computed signals.
87
+ */
88
+ export function angularReactivity(injector: Injector): TableReactivityBindings {
89
+ const ngZone = injector.get(NgZone)
90
+ const destroyRef = injector.get(DestroyRef)
91
+
92
+ return {
93
+ createOptionsStore: true,
94
+ schedule: (fn) => ngZone.runOutsideAngular(() => queueMicrotask(fn)),
95
+ createReadonlyAtom: <T>(fn: () => T, options?: TableAtomOptions<T>) => {
96
+ const storeAtom = createAtom(() => fn(), {
97
+ compare: options?.compare,
98
+ })
99
+ const version = signal(0, {
100
+ equal: () => false,
101
+ })
102
+ const subscription = storeAtom.subscribe(() => {
103
+ version.update((value) => value + 1)
104
+ })
105
+ destroyRef.onDestroy(() => subscription.unsubscribe())
106
+
107
+ const value = computed(
108
+ () => {
109
+ version()
110
+ return storeAtom.get()
111
+ },
112
+ {
113
+ equal: options?.compare,
114
+ debugName: options?.debugName,
115
+ },
116
+ )
117
+ return signalToReadonlyAtom(
118
+ value,
119
+ () => storeAtom.get(),
120
+ (observerOrNext) => {
121
+ const observer = observerToCallback(observerOrNext)
122
+ return storeAtom.subscribe(() => {
123
+ observer(storeAtom.get())
124
+ })
125
+ },
126
+ )
127
+ },
128
+ createWritableAtom: <T>(
129
+ value: T,
130
+ options?: TableAtomOptions<T>,
131
+ ): Atom<T> => {
132
+ if (options?.debugName === optionsStoreDebugName) {
133
+ const writableSignal = signal(value, {
134
+ equal: options.compare,
135
+ debugName: options.debugName,
136
+ })
137
+ return signalToWritableAtom(writableSignal, injector)
138
+ }
139
+
140
+ return createAtom(value, {
141
+ compare: options?.compare,
142
+ })
143
+ },
144
+ untrack: untracked,
145
+ batch,
146
+ }
147
+ }
@@ -0,0 +1 @@
1
+ export * from '@tanstack/table-core/static-functions'
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "../node_modules/ng-packagr/ng-package.schema.json",
3
+ "lib": {
4
+ "entryFile": "index.ts"
5
+ }
6
+ }