@tanstack/svelte-table 9.0.0-alpha.4 → 9.0.0-alpha.41

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 (50) hide show
  1. package/README.md +117 -0
  2. package/dist/AppCell.svelte +13 -0
  3. package/dist/AppCell.svelte.d.ts +9 -0
  4. package/dist/AppHeader.svelte +13 -0
  5. package/dist/AppHeader.svelte.d.ts +9 -0
  6. package/dist/AppTable.svelte +11 -0
  7. package/dist/AppTable.svelte.d.ts +7 -0
  8. package/dist/FlexRender.svelte +103 -0
  9. package/dist/FlexRender.svelte.d.ts +51 -0
  10. package/dist/context-keys.d.ts +3 -0
  11. package/dist/context-keys.js +3 -0
  12. package/dist/createTable.svelte.d.ts +13 -0
  13. package/dist/createTable.svelte.js +74 -0
  14. package/dist/createTableHook.svelte.d.ts +235 -0
  15. package/dist/createTableHook.svelte.js +170 -0
  16. package/dist/createTableState.svelte.d.ts +2 -0
  17. package/dist/createTableState.svelte.js +12 -0
  18. package/dist/flex-render.d.ts +1 -0
  19. package/dist/flex-render.js +2 -0
  20. package/dist/index.d.ts +8 -3
  21. package/dist/index.js +6 -3
  22. package/dist/merge-objects.d.ts +8 -0
  23. package/dist/merge-objects.js +29 -0
  24. package/dist/render-component.d.ts +64 -9
  25. package/dist/render-component.js +60 -4
  26. package/dist/static-functions.d.ts +1 -0
  27. package/dist/static-functions.js +1 -0
  28. package/dist/subscribe.d.ts +9 -0
  29. package/dist/subscribe.js +9 -0
  30. package/package.json +27 -9
  31. package/src/AppCell.svelte +13 -0
  32. package/src/AppHeader.svelte +13 -0
  33. package/src/AppTable.svelte +11 -0
  34. package/src/FlexRender.svelte +103 -0
  35. package/src/context-keys.ts +3 -0
  36. package/src/createTable.svelte.ts +133 -0
  37. package/src/createTableHook.svelte.ts +636 -0
  38. package/src/createTableState.svelte.ts +15 -0
  39. package/src/flex-render.ts +3 -0
  40. package/src/index.ts +20 -3
  41. package/src/merge-objects.ts +43 -0
  42. package/src/render-component.ts +74 -10
  43. package/src/static-functions.ts +1 -0
  44. package/src/subscribe.ts +23 -0
  45. package/dist/flex-render.svelte +0 -35
  46. package/dist/flex-render.svelte.d.ts +0 -28
  47. package/dist/table.svelte.d.ts +0 -28
  48. package/dist/table.svelte.js +0 -87
  49. package/src/flex-render.svelte +0 -35
  50. package/src/table.svelte.ts +0 -117
@@ -1,20 +1,51 @@
1
1
  /**
2
- * A helper class to make it easy to identify Svelte components in `columnDef.cell` and `columnDef.header` properties.
2
+ * A helper class to make it easy to identify Svelte components in
3
+ * `columnDef.cell` and `columnDef.header` properties.
4
+ *
5
+ * > NOTE: This class should only be used internally by the adapter. If you're
6
+ * reading this and you don't know what this is for, you probably don't need it.
7
+ *
3
8
  * @example
4
9
  * ```svelte
5
- * {#if cell.column.columnDef.cell(cell.getContext()) instanceof RenderComponentConfig}
6
- * <svelte:component this={columnDef.cell.component} {...columnDef.cell.props} />
10
+ * {@const result = content(context as any)}
11
+ * {#if result instanceof RenderComponentConfig}
12
+ * {@const { component: Component, props } = result}
13
+ * <Component {...props} />
7
14
  * {/if}
8
15
  * ```
9
16
  * */
10
17
  export class RenderComponentConfig {
11
- constructor(component, props = {}) {
18
+ constructor(component, props) {
12
19
  this.component = component;
13
20
  this.props = props;
14
21
  }
15
22
  }
23
+ /**
24
+ * A helper class to make it easy to identify Svelte Snippets in `columnDef.cell` and `columnDef.header` properties.
25
+ *
26
+ * > NOTE: This class should only be used internally by the adapter. If you're
27
+ * reading this and you don't know what this is for, you probably don't need it.
28
+ *
29
+ * @example
30
+ * ```svelte
31
+ * {@const result = content(context as any)}
32
+ * {#if result instanceof RenderSnippetConfig}
33
+ * {@const { snippet, params } = result}
34
+ * {@render snippet(params)}
35
+ * {/if}
36
+ * ```
37
+ * */
38
+ export class RenderSnippetConfig {
39
+ constructor(snippet, params) {
40
+ this.snippet = snippet;
41
+ this.params = params;
42
+ }
43
+ }
16
44
  /**
17
45
  * A helper function to help create cells from Svelte components through ColumnDef's `cell` and `header` properties.
46
+ *
47
+ * This is only to be used with Svelte Components - use `renderSnippet` for Svelte Snippets.
48
+ *
18
49
  * @param component A Svelte component
19
50
  * @param props The props to pass to `component`
20
51
  * @returns A `RenderComponentConfig` object that helps svelte-table know how to render the header/cell component.
@@ -33,3 +64,28 @@ export class RenderComponentConfig {
33
64
  * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs}
34
65
  */
35
66
  export const renderComponent = (component, props) => new RenderComponentConfig(component, props);
67
+ /**
68
+ * A helper function to help create cells from Svelte Snippets through ColumnDef's `cell` and `header` properties.
69
+ *
70
+ * *The snippet must only take one parameter.*
71
+ *
72
+ * This is only to be used with Snippets - use `renderComponent` for Svelte Components.
73
+ *
74
+ * @param snippet
75
+ * @param params
76
+ * @returns
77
+ * @example
78
+ * ```ts
79
+ * // +page.svelte
80
+ * const defaultColumns = [
81
+ * columnHelper.accessor('name', {
82
+ * cell: cell => renderSnippet(nameSnippet, { name: cell.row.name }),
83
+ * }),
84
+ * columnHelper.accessor('state', {
85
+ * cell: cell => renderSnippet(stateSnippet, { state: cell.row.state }),
86
+ * }),
87
+ * ]
88
+ * ```
89
+ * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs}
90
+ */
91
+ export const renderSnippet = (snippet, params) => new RenderSnippetConfig(snippet, params);
@@ -0,0 +1 @@
1
+ export * from '@tanstack/table-core/static-functions';
@@ -0,0 +1 @@
1
+ export * from '@tanstack/table-core/static-functions';
@@ -0,0 +1,9 @@
1
+ import type { RowData, Table, TableFeatures, TableState } from '@tanstack/table-core';
2
+ /**
3
+ * Fine-grained subscription to `table.store` using `useSelector` with shallow
4
+ * comparison. Call from `<script>` so the selector is contextually typed
5
+ * (same inference model as React’s `Subscribe` / `table.Subscribe`).
6
+ */
7
+ export declare function subscribeTable<TFeatures extends TableFeatures, TData extends RowData, TSelected>(table: Table<TFeatures, TData>, selector: (state: TableState<TFeatures>) => TSelected): {
8
+ readonly current: TSelected;
9
+ };
@@ -0,0 +1,9 @@
1
+ import { shallow, useSelector } from '@tanstack/svelte-store';
2
+ /**
3
+ * Fine-grained subscription to `table.store` using `useSelector` with shallow
4
+ * comparison. Call from `<script>` so the selector is contextually typed
5
+ * (same inference model as React’s `Subscribe` / `table.Subscribe`).
6
+ */
7
+ export function subscribeTable(table, selector) {
8
+ return useSelector(table.store, selector, { compare: shallow });
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/svelte-table",
3
- "version": "9.0.0-alpha.4",
3
+ "version": "9.0.0-alpha.41",
4
4
  "description": "Headless UI for building powerful tables & datagrids for Svelte.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -30,26 +30,44 @@
30
30
  "svelte": "./dist/index.js",
31
31
  "import": "./dist/index.js"
32
32
  },
33
+ "./static-functions": {
34
+ "types": "./dist/static-functions.d.ts",
35
+ "svelte": "./dist/static-functions.js",
36
+ "import": "./dist/static-functions.js"
37
+ },
38
+ "./flex-render": {
39
+ "types": "./dist/flex-render.d.ts",
40
+ "svelte": "./dist/flex-render.js",
41
+ "import": "./dist/flex-render.js"
42
+ },
33
43
  "./package.json": "./package.json"
34
44
  },
35
45
  "engines": {
36
- "node": ">=12"
46
+ "node": ">=16"
37
47
  },
38
48
  "files": [
39
49
  "dist",
40
50
  "src"
41
51
  ],
42
52
  "dependencies": {
43
- "@tanstack/table-core": "9.0.0-alpha.4"
53
+ "@tanstack/svelte-store": "^0.12.0",
54
+ "@tanstack/table-core": "9.0.0-alpha.41"
44
55
  },
45
56
  "devDependencies": {
46
- "@sveltejs/package": "^2.3.1",
47
- "@sveltejs/vite-plugin-svelte": "^4.0.0-next",
48
- "svelte": "^5.0.0-next",
49
- "svelte-check": "^3.7.0"
57
+ "@sveltejs/package": "^2.5.7",
58
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
59
+ "eslint-plugin-svelte": "^3.17.1",
60
+ "svelte": "^5.55.5",
61
+ "svelte-check": "^4.4.7"
50
62
  },
51
63
  "peerDependencies": {
52
- "svelte": "^5.0.0-next"
64
+ "svelte": "^5.0.0"
53
65
  },
54
- "scripts": {}
66
+ "scripts": {
67
+ "clean": "rimraf ./build && rimraf ./dist",
68
+ "test:eslint": "eslint ./src",
69
+ "test:types": "svelte-check --tsconfig ./tsconfig.json",
70
+ "test:build": "publint --strict",
71
+ "build": "svelte-package --input ./src --output ./dist"
72
+ }
55
73
  }
@@ -0,0 +1,13 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+
4
+ interface Props {
5
+ cell: any
6
+ cellComponents: any
7
+ children: Snippet<[any]>
8
+ }
9
+
10
+ let { children, cell, cellComponents }: Props = $props()
11
+ </script>
12
+
13
+ {@render children?.(Object.assign(cell, cellComponents))}
@@ -0,0 +1,13 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+
4
+ interface Props {
5
+ header: any
6
+ headerComponents: any
7
+ children: Snippet<[any]>
8
+ }
9
+
10
+ let { children, header, headerComponents }: Props = $props()
11
+ </script>
12
+
13
+ {@render children?.(Object.assign(header, headerComponents))}
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+
4
+ interface Props {
5
+ children: Snippet
6
+ }
7
+
8
+ let { children }: Props = $props()
9
+ </script>
10
+
11
+ {@render children?.()}
@@ -0,0 +1,103 @@
1
+ <script
2
+ lang="ts"
3
+ generics="TFeatures extends TableFeatures, TData extends RowData, TValue extends CellData"
4
+ >
5
+ import { isFunction } from '@tanstack/table-core'
6
+ import {
7
+ RenderComponentConfig,
8
+ RenderSnippetConfig,
9
+ } from './render-component'
10
+ import type {
11
+ Cell,
12
+ CellContext,
13
+ CellData,
14
+ ColumnDefTemplate,
15
+ Header,
16
+ HeaderContext,
17
+ RowData,
18
+ TableFeatures,
19
+ } from '@tanstack/table-core'
20
+
21
+ type Props =
22
+ | {
23
+ /** The cell or header field of the current cell's column definition. */
24
+ content?: ColumnDefTemplate<
25
+ | HeaderContext<TFeatures, TData, TValue>
26
+ | CellContext<TFeatures, TData, TValue>
27
+ >
28
+ /** The result of the `getContext()` function of the header or cell */
29
+ context:
30
+ | HeaderContext<TFeatures, TData, TValue>
31
+ | CellContext<TFeatures, TData, TValue>
32
+ cell?: never
33
+ header?: never
34
+ footer?: never
35
+ }
36
+ | {
37
+ cell: Cell<TFeatures, TData, TValue>
38
+ content?: never
39
+ context?: never
40
+ header?: never
41
+ footer?: never
42
+ }
43
+ | {
44
+ header: Header<TFeatures, TData, TValue>
45
+ content?: never
46
+ context?: never
47
+ cell?: never
48
+ footer?: never
49
+ }
50
+ | {
51
+ footer: Header<TFeatures, TData, TValue>
52
+ content?: never
53
+ context?: never
54
+ cell?: never
55
+ header?: never
56
+ }
57
+
58
+ let props: Props = $props()
59
+
60
+ // Resolve content and context from either the new cell/header/footer props
61
+ // or the legacy content/context props.
62
+ const resolved = $derived.by(() => {
63
+ if ('cell' in props && props.cell) {
64
+ return {
65
+ content: props.cell.column.columnDef.cell,
66
+ context: props.cell.getContext(),
67
+ }
68
+ }
69
+ if ('header' in props && props.header) {
70
+ return {
71
+ content: props.header.column.columnDef.header,
72
+ context: props.header.getContext(),
73
+ }
74
+ }
75
+ if ('footer' in props && props.footer) {
76
+ return {
77
+ content: props.footer.column.columnDef.footer,
78
+ context: props.footer.getContext(),
79
+ }
80
+ }
81
+ return {
82
+ content: props.content,
83
+ context: props.context,
84
+ }
85
+ })
86
+
87
+ // Compute the render result reactively
88
+ const result = $derived(
89
+ isFunction(resolved.content)
90
+ ? resolved.content(resolved.context as any)
91
+ : undefined,
92
+ )
93
+ </script>
94
+
95
+ {#if typeof resolved.content === 'string'}
96
+ {resolved.content}
97
+ {:else if result instanceof RenderComponentConfig}
98
+ <result.component {...result.props} />
99
+ {:else if result instanceof RenderSnippetConfig}
100
+ {@render result.snippet(result.params)}
101
+ {:else if result !== undefined}
102
+ {result}
103
+ {/if}
@@ -0,0 +1,3 @@
1
+ export const tableContextKey = '__tanstack_table_context_key'
2
+ export const cellContextKey = '__tanstack_cell_context_key'
3
+ export const headerContextKey = '__tanstack_header_context_key'
@@ -0,0 +1,133 @@
1
+ import {
2
+ constructReactivityFeature,
3
+ constructTable,
4
+ } from '@tanstack/table-core'
5
+ import { shallow, useSelector } from '@tanstack/svelte-store'
6
+ import { untrack } from 'svelte'
7
+ import { mergeObjects } from './merge-objects'
8
+ import type {
9
+ RowData,
10
+ Table,
11
+ TableFeatures,
12
+ TableOptions,
13
+ TableState,
14
+ } from '@tanstack/table-core'
15
+
16
+ export type SvelteTable<
17
+ TFeatures extends TableFeatures,
18
+ TData extends RowData,
19
+ TSelected = {},
20
+ > = Table<TFeatures, TData> & {
21
+ /**
22
+ * 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`.
23
+ *
24
+ * @example
25
+ * const table = createTable(options, (state) => ({ globalFilter: state.globalFilter })) // only globalFilter is part of the selected state
26
+ *
27
+ * console.log(table.state.globalFilter)
28
+ */
29
+ readonly state: Readonly<TSelected>
30
+ }
31
+
32
+ export function createTable<
33
+ TFeatures extends TableFeatures,
34
+ TData extends RowData,
35
+ TSelected = {},
36
+ >(
37
+ tableOptions: TableOptions<TFeatures, TData>,
38
+ selector: (state: TableState<TFeatures>) => TSelected = () =>
39
+ ({}) as TSelected,
40
+ ): SvelteTable<TFeatures, TData, TSelected> {
41
+ // 1. Create $state-based notifier for reactivity feature
42
+ let notifierValue = $state(0)
43
+
44
+ // 2. Construct reactivity feature (same pattern as solid/vue/angular)
45
+ const svelteReactivityFeature = constructReactivityFeature<TFeatures, TData>({
46
+ stateNotifier: () => notifierValue,
47
+ optionsNotifier: () => notifierValue,
48
+ })
49
+
50
+ // 3. Merge reactivity feature into options using mergeObjects (preserves getters)
51
+ const mergedOptions = mergeObjects(tableOptions, {
52
+ _features: mergeObjects(tableOptions._features, {
53
+ svelteReactivityFeature,
54
+ }),
55
+ }) as any
56
+
57
+ // 4. Set up resolved options with mergeOptions handler
58
+ const resolvedOptions = mergeObjects(
59
+ {
60
+ mergeOptions: (
61
+ defaultOptions: TableOptions<TFeatures, TData>,
62
+ newOptions: Partial<TableOptions<TFeatures, TData>>,
63
+ ) => {
64
+ return mergeObjects(defaultOptions, newOptions)
65
+ },
66
+ },
67
+ mergedOptions,
68
+ ) as TableOptions<TFeatures, TData>
69
+
70
+ // 5. Construct table
71
+ const table = constructTable(resolvedOptions) as SvelteTable<
72
+ TFeatures,
73
+ TData,
74
+ TSelected
75
+ >
76
+
77
+ // 6. Subscribe to all state and options via useSelector
78
+ const allState = useSelector(table.store, (state) => state)
79
+ const allOptions = useSelector(table.optionsStore, (options) => options)
80
+
81
+ // 7. Sync store changes -> notifier.
82
+ // Use $effect.pre so this runs before DOM updates (like Solid's createComputed).
83
+ // Use untrack for the write so the effect only depends on allState/allOptions,
84
+ // not on notifierValue itself (which would cause an infinite loop).
85
+ $effect.pre(() => {
86
+ allState.current
87
+ allOptions.current
88
+ untrack(() => {
89
+ notifierValue++
90
+ })
91
+ })
92
+
93
+ // 8. Sync options reactively. When controlled state changes (e.g., $state
94
+ // inside createTableState), the effect re-runs and calls setOptions.
95
+ // Use $effect.pre so the table sees updated options BEFORE the DOM renders,
96
+ // ensuring getRowModel() returns current data (not stale, one-frame-behind data).
97
+ // The reactive reads (state getters, data getter) happen OUTSIDE untrack
98
+ // so they become dependencies. The setOptions call is INSIDE untrack to
99
+ // prevent tracking notifierValue (read via store.state's stateNotifier
100
+ // interceptor inside setOptions), which would cause an infinite loop.
101
+ $effect.pre(() => {
102
+ // Read reactive getters to create $effect dependencies on external state
103
+ const state = mergedOptions.state
104
+ if (state) {
105
+ for (const key in state) {
106
+ void state[key]
107
+ }
108
+ }
109
+ void mergedOptions.data
110
+
111
+ untrack(() => {
112
+ table.setOptions((prev) => {
113
+ return mergeObjects(prev, mergedOptions) as TableOptions<
114
+ TFeatures,
115
+ TData
116
+ >
117
+ })
118
+ })
119
+ })
120
+
121
+ // 9. State selector
122
+ const stateStore = useSelector(table.store, selector)
123
+
124
+ Object.defineProperty(table, 'state', {
125
+ get() {
126
+ return stateStore.current
127
+ },
128
+ configurable: true,
129
+ enumerable: true,
130
+ })
131
+
132
+ return table
133
+ }