@tanstack/svelte-table 9.0.0-alpha.50 → 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.
@@ -1,7 +1,17 @@
1
1
  import type { RowData, Table, TableFeatures, TableOptions, TableState } from '@tanstack/table-core';
2
- export type SvelteTable<TFeatures extends TableFeatures, TData extends RowData, TSelected = TableState<TFeatures>> = Table<TFeatures, TData> & {
2
+ export type SvelteTable<TFeatures extends TableFeatures, TData extends RowData, TSelected = TableState<TFeatures>> = Omit<Table<TFeatures, TData>, 'store'> & {
3
3
  /**
4
- * The selected state of the table. This state may not match the structure of `table.store.state` because it is selected by the `selector` function that you pass as the 2nd argument to `createTable`.
4
+ * @deprecated Prefer `table.state` for render reads,
5
+ * `table.atoms.<slice>.get()` for slice snapshots, or
6
+ * `useSelector(table.store, selector)` for explicit subscriptions.
7
+ * `table.store.state` is a current-value snapshot and is easy to misuse in
8
+ * render code.
9
+ */
10
+ readonly store: Table<TFeatures, TData>['store'];
11
+ /**
12
+ * The selected state of the table. This state may not match the structure of
13
+ * the full table state because it is selected by the selector function that
14
+ * you pass as the 2nd argument to `createTable`.
5
15
  *
6
16
  * @example
7
17
  * const table = createTable(options, (state) => ({ globalFilter: state.globalFilter })) // only globalFilter is part of the selected state
@@ -2,8 +2,8 @@ import type { TableReactivityBindings } from '@tanstack/table-core/reactivity';
2
2
  /**
3
3
  * Creates the table-core reactivity bindings used by the Svelte adapter.
4
4
  *
5
- * Readonly table atoms are backed by `$derived.by`, writable atoms by `$state`,
6
- * and subscriptions bridge through rune effects so table APIs participate in
7
- * Svelte dependency tracking.
5
+ * Table state atoms are backed by TanStack Store atoms. The options store stays
6
+ * framework-native because row-model APIs read `table.options` directly during
7
+ * render. Readonly table atoms bridge Store dependency tracking into `$derived.by`.
8
8
  */
9
9
  export declare function svelteReactivity(): TableReactivityBindings;
@@ -1,4 +1,6 @@
1
- import { flushSync, untrack } from 'svelte';
1
+ import { untrack } from 'svelte';
2
+ import { batch, createAtom } from '@tanstack/svelte-store';
3
+ const optionsStoreDebugName = 'table/optionsStore';
2
4
  function observerToCallback(observerOrNext) {
3
5
  return typeof observerOrNext === 'function'
4
6
  ? observerOrNext
@@ -14,42 +16,67 @@ function subscribeToRune(getValue, observerOrNext) {
14
16
  });
15
17
  return { unsubscribe };
16
18
  }
19
+ function createRuneWritableAtom(initialValue) {
20
+ let value = $state(initialValue);
21
+ return {
22
+ set: (updater) => {
23
+ value =
24
+ typeof updater === 'function'
25
+ ? updater(value)
26
+ : updater;
27
+ },
28
+ get: () => value,
29
+ subscribe: ((observerOrNext) => {
30
+ return subscribeToRune(() => value, observerOrNext);
31
+ }),
32
+ };
33
+ }
17
34
  /**
18
35
  * Creates the table-core reactivity bindings used by the Svelte adapter.
19
36
  *
20
- * Readonly table atoms are backed by `$derived.by`, writable atoms by `$state`,
21
- * and subscriptions bridge through rune effects so table APIs participate in
22
- * Svelte dependency tracking.
37
+ * Table state atoms are backed by TanStack Store atoms. The options store stays
38
+ * framework-native because row-model APIs read `table.options` directly during
39
+ * render. Readonly table atoms bridge Store dependency tracking into `$derived.by`.
23
40
  */
24
41
  export function svelteReactivity() {
25
42
  return {
26
43
  createOptionsStore: true,
27
44
  schedule: (fn) => queueMicrotask(() => fn()),
28
45
  createReadonlyAtom: (fn, _options) => {
29
- const value = $derived.by(fn);
46
+ const storeAtom = createAtom(() => fn(), {
47
+ compare: _options?.compare,
48
+ });
49
+ let version = $state(0);
50
+ $effect(() => {
51
+ const subscription = storeAtom.subscribe(() => {
52
+ version += 1;
53
+ });
54
+ return () => subscription.unsubscribe();
55
+ });
56
+ const value = $derived.by(() => {
57
+ version;
58
+ return storeAtom.get();
59
+ });
30
60
  return {
31
- get: () => value,
61
+ get: () => {
62
+ const currentValue = storeAtom.get();
63
+ value;
64
+ return currentValue;
65
+ },
32
66
  subscribe: ((observerOrNext) => {
33
67
  return subscribeToRune(() => value, observerOrNext);
34
68
  }),
35
69
  };
36
70
  },
37
71
  createWritableAtom: (initialValue, _options) => {
38
- let value = $state(initialValue);
39
- return {
40
- set: (updater) => {
41
- value =
42
- typeof updater === 'function'
43
- ? updater(value)
44
- : updater;
45
- },
46
- get: () => value,
47
- subscribe: ((observerOrNext) => {
48
- return subscribeToRune(() => value, observerOrNext);
49
- }),
50
- };
72
+ if (_options?.debugName === optionsStoreDebugName) {
73
+ return createRuneWritableAtom(initialValue);
74
+ }
75
+ return createAtom(initialValue, {
76
+ compare: _options?.compare,
77
+ });
51
78
  },
52
79
  untrack: untrack,
53
- batch: (fn) => flushSync(fn),
80
+ batch,
54
81
  };
55
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/svelte-table",
3
- "version": "9.0.0-alpha.50",
3
+ "version": "9.0.0-alpha.52",
4
4
  "description": "Headless UI for building powerful tables & datagrids for Svelte.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -53,13 +53,13 @@
53
53
  ],
54
54
  "dependencies": {
55
55
  "@tanstack/svelte-store": "^0.12.0",
56
- "@tanstack/table-core": "9.0.0-alpha.49"
56
+ "@tanstack/table-core": "9.0.0-alpha.52"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@sveltejs/package": "^2.5.7",
60
60
  "@sveltejs/vite-plugin-svelte": "^7.1.2",
61
- "eslint-plugin-svelte": "^3.17.1",
62
- "svelte": "^5.55.7",
61
+ "eslint-plugin-svelte": "^3.19.0",
62
+ "svelte": "^5.56.0",
63
63
  "svelte-check": "^4.4.8"
64
64
  },
65
65
  "peerDependencies": {
@@ -71,7 +71,7 @@ function logSort() {
71
71
  }
72
72
  ```
73
73
 
74
- `table.store.state` is the full snapshot equivalent.
74
+ `table.state` is the full snapshot equivalent.
75
75
 
76
76
  ## Pattern 2 — Reactive selector via `createTable`
77
77
 
@@ -160,7 +160,7 @@ function exportSelected() {
160
160
  }
161
161
  ```
162
162
 
163
- `table.store.state` is the same idea for a full snapshot.
163
+ `table.state` is the same idea for a full snapshot.
164
164
 
165
165
  ## 6. Key every `{#each}` block on a stable id
166
166
 
@@ -109,7 +109,7 @@ Read the atom directly. Cheapest path; only reactive when called inside a rune-t
109
109
  ```ts
110
110
  const sorting = table.atoms.sorting.get()
111
111
  const pagination = table.atoms.pagination.get()
112
- const flat = table.store.state
112
+ const flat = table.state
113
113
  ```
114
114
 
115
115
  ### Reactive read inside markup — `table.state` selector
@@ -15,9 +15,19 @@ export type SvelteTable<
15
15
  TFeatures extends TableFeatures,
16
16
  TData extends RowData,
17
17
  TSelected = TableState<TFeatures>,
18
- > = Table<TFeatures, TData> & {
18
+ > = Omit<Table<TFeatures, TData>, 'store'> & {
19
19
  /**
20
- * The selected state of the table. This state may not match the structure of `table.store.state` because it is selected by the `selector` function that you pass as the 2nd argument to `createTable`.
20
+ * @deprecated Prefer `table.state` for render reads,
21
+ * `table.atoms.<slice>.get()` for slice snapshots, or
22
+ * `useSelector(table.store, selector)` for explicit subscriptions.
23
+ * `table.store.state` is a current-value snapshot and is easy to misuse in
24
+ * render code.
25
+ */
26
+ readonly store: Table<TFeatures, TData>['store']
27
+ /**
28
+ * The selected state of the table. This state may not match the structure of
29
+ * the full table state because it is selected by the selector function that
30
+ * you pass as the 2nd argument to `createTable`.
21
31
  *
22
32
  * @example
23
33
  * const table = createTable(options, (state) => ({ globalFilter: state.globalFilter })) // only globalFilter is part of the selected state
@@ -82,7 +92,7 @@ export function createTable<
82
92
  ) as TableOptions<TFeatures, TData>
83
93
 
84
94
  // 3. Construct table
85
- const table = constructTable(resolvedOptions) as SvelteTable<
95
+ const table = constructTable(resolvedOptions) as unknown as SvelteTable<
86
96
  TFeatures,
87
97
  TData,
88
98
  TSelected
@@ -1,10 +1,13 @@
1
- import { flushSync, untrack } from 'svelte'
1
+ import { untrack } from 'svelte'
2
+ import { batch, createAtom } from '@tanstack/svelte-store'
2
3
  import type {
3
4
  TableAtomOptions,
4
5
  TableReactivityBindings,
5
6
  } from '@tanstack/table-core/reactivity'
6
7
  import type { Atom, Observer, ReadonlyAtom } from '@tanstack/svelte-store'
7
8
 
9
+ const optionsStoreDebugName = 'table/optionsStore'
10
+
8
11
  function observerToCallback<T>(
9
12
  observerOrNext: Observer<T> | ((value: T) => void),
10
13
  ): (value: T) => void {
@@ -28,22 +31,59 @@ function subscribeToRune<T>(
28
31
  return { unsubscribe }
29
32
  }
30
33
 
34
+ function createRuneWritableAtom<T>(initialValue: T): Atom<T> {
35
+ let value = $state(initialValue)
36
+
37
+ return {
38
+ set: (updater: T | ((prevVal: T) => T)) => {
39
+ value =
40
+ typeof updater === 'function'
41
+ ? (updater as (prevVal: T) => T)(value)
42
+ : updater
43
+ },
44
+ get: () => value,
45
+ subscribe: ((observerOrNext: Observer<T> | ((value: T) => void)) => {
46
+ return subscribeToRune(() => value, observerOrNext)
47
+ }) as Atom<T>['subscribe'],
48
+ }
49
+ }
50
+
31
51
  /**
32
52
  * Creates the table-core reactivity bindings used by the Svelte adapter.
33
53
  *
34
- * Readonly table atoms are backed by `$derived.by`, writable atoms by `$state`,
35
- * and subscriptions bridge through rune effects so table APIs participate in
36
- * Svelte dependency tracking.
54
+ * Table state atoms are backed by TanStack Store atoms. The options store stays
55
+ * framework-native because row-model APIs read `table.options` directly during
56
+ * render. Readonly table atoms bridge Store dependency tracking into `$derived.by`.
37
57
  */
38
58
  export function svelteReactivity(): TableReactivityBindings {
39
59
  return {
40
60
  createOptionsStore: true,
41
61
  schedule: (fn) => queueMicrotask(() => fn()),
42
62
  createReadonlyAtom: <T>(fn: () => T, _options?: TableAtomOptions<T>) => {
43
- const value = $derived.by(fn)
63
+ const storeAtom = createAtom(() => fn(), {
64
+ compare: _options?.compare,
65
+ })
66
+ let version = $state(0)
67
+
68
+ $effect(() => {
69
+ const subscription = storeAtom.subscribe(() => {
70
+ version += 1
71
+ })
72
+
73
+ return () => subscription.unsubscribe()
74
+ })
75
+
76
+ const value = $derived.by(() => {
77
+ version
78
+ return storeAtom.get()
79
+ })
44
80
 
45
81
  return {
46
- get: () => value,
82
+ get: () => {
83
+ const currentValue = storeAtom.get()
84
+ value
85
+ return currentValue
86
+ },
47
87
  subscribe: ((observerOrNext: Observer<T> | ((value: T) => void)) => {
48
88
  return subscribeToRune(() => value, observerOrNext)
49
89
  }) as ReadonlyAtom<T>['subscribe'],
@@ -53,22 +93,15 @@ export function svelteReactivity(): TableReactivityBindings {
53
93
  initialValue: T,
54
94
  _options?: TableAtomOptions<T>,
55
95
  ): Atom<T> => {
56
- let value = $state(initialValue)
57
-
58
- return {
59
- set: (updater: T | ((prevVal: T) => T)) => {
60
- value =
61
- typeof updater === 'function'
62
- ? (updater as (prevVal: T) => T)(value)
63
- : updater
64
- },
65
- get: () => value,
66
- subscribe: ((observerOrNext: Observer<T> | ((value: T) => void)) => {
67
- return subscribeToRune(() => value, observerOrNext)
68
- }) as Atom<T>['subscribe'],
96
+ if (_options?.debugName === optionsStoreDebugName) {
97
+ return createRuneWritableAtom(initialValue)
69
98
  }
99
+
100
+ return createAtom(initialValue, {
101
+ compare: _options?.compare,
102
+ })
70
103
  },
71
104
  untrack: untrack,
72
- batch: (fn) => flushSync(fn),
105
+ batch,
73
106
  }
74
107
  }