@tanstack/svelte-table 9.0.0-alpha.2 → 9.0.0-alpha.26
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.
- package/README.md +117 -0
- package/dist/AppCell.svelte +13 -0
- package/dist/AppCell.svelte.d.ts +9 -0
- package/dist/AppHeader.svelte +13 -0
- package/dist/AppHeader.svelte.d.ts +9 -0
- package/dist/AppTable.svelte +11 -0
- package/dist/AppTable.svelte.d.ts +7 -0
- package/dist/FlexRender.svelte +103 -0
- package/dist/FlexRender.svelte.d.ts +51 -0
- package/dist/Subscribe.svelte +45 -0
- package/dist/Subscribe.svelte.d.ts +44 -0
- package/dist/context-keys.d.ts +3 -0
- package/dist/context-keys.js +3 -0
- package/dist/createTable.svelte.d.ts +32 -0
- package/dist/createTable.svelte.js +82 -0
- package/dist/createTableHook.svelte.d.ts +236 -0
- package/dist/createTableHook.svelte.js +170 -0
- package/dist/createTableState.svelte.d.ts +2 -0
- package/dist/createTableState.svelte.js +12 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.js +6 -3
- package/dist/merge-objects.d.ts +8 -0
- package/dist/merge-objects.js +29 -0
- package/dist/render-component.d.ts +64 -9
- package/dist/render-component.js +60 -4
- package/package.json +17 -9
- package/src/AppCell.svelte +13 -0
- package/src/AppHeader.svelte +13 -0
- package/src/AppTable.svelte +11 -0
- package/src/FlexRender.svelte +103 -0
- package/src/Subscribe.svelte +45 -0
- package/src/context-keys.ts +3 -0
- package/src/createTable.svelte.ts +165 -0
- package/src/createTableHook.svelte.ts +636 -0
- package/src/createTableState.svelte.ts +15 -0
- package/src/index.ts +15 -3
- package/src/merge-objects.ts +43 -0
- package/src/render-component.ts +74 -10
- package/dist/flex-render.svelte +0 -17
- package/dist/flex-render.svelte.d.ts +0 -28
- package/dist/table.svelte.d.ts +0 -36
- package/dist/table.svelte.js +0 -87
- package/src/flex-render.svelte +0 -35
- package/src/table.svelte.ts +0 -121
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/svelte-table",
|
|
3
|
-
"version": "9.0.0-alpha.
|
|
3
|
+
"version": "9.0.0-alpha.26",
|
|
4
4
|
"description": "Headless UI for building powerful tables & datagrids for Svelte.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,23 +33,31 @@
|
|
|
33
33
|
"./package.json": "./package.json"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": ">=
|
|
36
|
+
"node": ">=16"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
40
|
"src"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@tanstack/
|
|
43
|
+
"@tanstack/svelte-store": "^0.10.3",
|
|
44
|
+
"@tanstack/table-core": "9.0.0-alpha.23"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@sveltejs/package": "^2.
|
|
47
|
+
"@sveltejs/package": "^2.5.7",
|
|
48
|
+
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
49
|
+
"eslint-plugin-svelte": "^3.17.0",
|
|
50
|
+
"svelte": "^5.55.1",
|
|
51
|
+
"svelte-check": "^4.4.6"
|
|
47
52
|
},
|
|
48
53
|
"peerDependencies": {
|
|
49
|
-
"svelte": "^5.0.0
|
|
50
|
-
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
|
51
|
-
"svelte-check": "^3.6.8",
|
|
52
|
-
"svelte-eslint-parser": "^0.33.1"
|
|
54
|
+
"svelte": "^5.0.0"
|
|
53
55
|
},
|
|
54
|
-
"scripts": {
|
|
56
|
+
"scripts": {
|
|
57
|
+
"clean": "rimraf ./build && rimraf ./dist",
|
|
58
|
+
"test:eslint": "eslint ./src",
|
|
59
|
+
"test:types": "svelte-check --tsconfig ./tsconfig.json",
|
|
60
|
+
"test:build": "publint --strict",
|
|
61
|
+
"build": "svelte-package --input ./src --output ./dist"
|
|
62
|
+
}
|
|
55
63
|
}
|
|
@@ -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,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,45 @@
|
|
|
1
|
+
<script
|
|
2
|
+
lang="ts"
|
|
3
|
+
generics="TFeatures extends TableFeatures, TData extends RowData, TSelected = unknown"
|
|
4
|
+
>
|
|
5
|
+
import type {
|
|
6
|
+
NoInfer,
|
|
7
|
+
RowData,
|
|
8
|
+
TableFeatures,
|
|
9
|
+
TableState,
|
|
10
|
+
} from '@tanstack/table-core'
|
|
11
|
+
import type { Snippet } from 'svelte'
|
|
12
|
+
import type { SvelteTable } from './createTable.svelte'
|
|
13
|
+
|
|
14
|
+
type Props<
|
|
15
|
+
TFeatures extends TableFeatures,
|
|
16
|
+
TData extends RowData,
|
|
17
|
+
TSelected,
|
|
18
|
+
> = {
|
|
19
|
+
/**
|
|
20
|
+
* A function that selects a portion of the table state to subscribe to.
|
|
21
|
+
* @param state - The full table state
|
|
22
|
+
* @returns The selected portion of state
|
|
23
|
+
*/
|
|
24
|
+
selector: (state: NoInfer<TableState<TFeatures>>) => TSelected
|
|
25
|
+
/**
|
|
26
|
+
* A Svelte snippet that receives the selected state and renders content.
|
|
27
|
+
* The snippet will be called with the selected state whenever it changes.
|
|
28
|
+
*/
|
|
29
|
+
children: Snippet<[state: Readonly<TSelected>]>
|
|
30
|
+
/**
|
|
31
|
+
* The table instance to subscribe to
|
|
32
|
+
*/
|
|
33
|
+
table: SvelteTable<TFeatures, TData, unknown>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let { table, selector, children }: Props<TFeatures, TData, TSelected> =
|
|
37
|
+
$props()
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<!--
|
|
41
|
+
Access table.store.state in the template (a reactive context).
|
|
42
|
+
The constructReactivityFeature interceptor makes this trigger
|
|
43
|
+
the $state-based notifier, so Svelte tracks it automatically.
|
|
44
|
+
-->
|
|
45
|
+
{@render children(selector(table.store.state as TableState<TFeatures>))}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {
|
|
2
|
+
constructReactivityFeature,
|
|
3
|
+
constructTable,
|
|
4
|
+
} from '@tanstack/table-core'
|
|
5
|
+
import { useStore } from '@tanstack/svelte-store'
|
|
6
|
+
import { untrack } from 'svelte'
|
|
7
|
+
import { mergeObjects } from './merge-objects'
|
|
8
|
+
import type {
|
|
9
|
+
NoInfer,
|
|
10
|
+
RowData,
|
|
11
|
+
Table,
|
|
12
|
+
TableFeatures,
|
|
13
|
+
TableOptions,
|
|
14
|
+
TableState,
|
|
15
|
+
} from '@tanstack/table-core'
|
|
16
|
+
import type { Snippet } from 'svelte'
|
|
17
|
+
|
|
18
|
+
export type SvelteTable<
|
|
19
|
+
TFeatures extends TableFeatures,
|
|
20
|
+
TData extends RowData,
|
|
21
|
+
TSelected = {},
|
|
22
|
+
> = Table<TFeatures, TData> & {
|
|
23
|
+
/**
|
|
24
|
+
* A Svelte component that allows you to subscribe to the table state.
|
|
25
|
+
*
|
|
26
|
+
* This is useful for opting into state subscriptions for specific parts of the table state.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* <table.Subscribe selector={(state) => ({ rowSelection: state.rowSelection })}>
|
|
30
|
+
* {(state) => (
|
|
31
|
+
* <tr>
|
|
32
|
+
* // render the row
|
|
33
|
+
* </tr>
|
|
34
|
+
* )}
|
|
35
|
+
* </table.Subscribe>
|
|
36
|
+
*/
|
|
37
|
+
Subscribe: <TSelected>(props: {
|
|
38
|
+
selector: (state: NoInfer<TableState<TFeatures>>) => TSelected
|
|
39
|
+
children: ((state: Readonly<TSelected>) => Snippet) | Snippet
|
|
40
|
+
}) => any
|
|
41
|
+
/**
|
|
42
|
+
* 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`.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const table = createTable(options, (state) => ({ globalFilter: state.globalFilter })) // only globalFilter is part of the selected state
|
|
46
|
+
*
|
|
47
|
+
* console.log(table.state.globalFilter)
|
|
48
|
+
*/
|
|
49
|
+
readonly state: Readonly<TSelected>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createTable<
|
|
53
|
+
TFeatures extends TableFeatures,
|
|
54
|
+
TData extends RowData,
|
|
55
|
+
TSelected = {},
|
|
56
|
+
>(
|
|
57
|
+
tableOptions: TableOptions<TFeatures, TData>,
|
|
58
|
+
selector: (state: TableState<TFeatures>) => TSelected = () =>
|
|
59
|
+
({}) as TSelected,
|
|
60
|
+
): SvelteTable<TFeatures, TData, TSelected> {
|
|
61
|
+
// 1. Create $state-based notifier for reactivity feature
|
|
62
|
+
let notifierValue = $state(0)
|
|
63
|
+
|
|
64
|
+
// 2. Construct reactivity feature (same pattern as solid/vue/angular)
|
|
65
|
+
const svelteReactivityFeature = constructReactivityFeature<TFeatures, TData>({
|
|
66
|
+
stateNotifier: () => notifierValue,
|
|
67
|
+
optionsNotifier: () => notifierValue,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// 3. Merge reactivity feature into options using mergeObjects (preserves getters)
|
|
71
|
+
const mergedOptions = mergeObjects(tableOptions, {
|
|
72
|
+
_features: mergeObjects(tableOptions._features, {
|
|
73
|
+
svelteReactivityFeature,
|
|
74
|
+
}),
|
|
75
|
+
}) as any
|
|
76
|
+
|
|
77
|
+
// 4. Set up resolved options with mergeOptions handler
|
|
78
|
+
const resolvedOptions = mergeObjects(
|
|
79
|
+
{
|
|
80
|
+
mergeOptions: (
|
|
81
|
+
defaultOptions: TableOptions<TFeatures, TData>,
|
|
82
|
+
newOptions: Partial<TableOptions<TFeatures, TData>>,
|
|
83
|
+
) => {
|
|
84
|
+
return mergeObjects(defaultOptions, newOptions)
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
mergedOptions,
|
|
88
|
+
) as TableOptions<TFeatures, TData>
|
|
89
|
+
|
|
90
|
+
// 5. Construct table
|
|
91
|
+
const table = constructTable(resolvedOptions) as SvelteTable<
|
|
92
|
+
TFeatures,
|
|
93
|
+
TData,
|
|
94
|
+
TSelected
|
|
95
|
+
>
|
|
96
|
+
|
|
97
|
+
// 6. Subscribe to all state and options via useStore
|
|
98
|
+
const allState = useStore(table.store, (state) => state)
|
|
99
|
+
const allOptions = useStore(table.optionsStore, (options) => options)
|
|
100
|
+
|
|
101
|
+
// 7. Sync store changes -> notifier.
|
|
102
|
+
// Use $effect.pre so this runs before DOM updates (like Solid's createComputed).
|
|
103
|
+
// Use untrack for the write so the effect only depends on allState/allOptions,
|
|
104
|
+
// not on notifierValue itself (which would cause an infinite loop).
|
|
105
|
+
$effect.pre(() => {
|
|
106
|
+
allState.current
|
|
107
|
+
allOptions.current
|
|
108
|
+
untrack(() => {
|
|
109
|
+
notifierValue++
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// 8. Sync options reactively. When controlled state changes (e.g., $state
|
|
114
|
+
// inside createTableState), the effect re-runs and calls setOptions.
|
|
115
|
+
// Use $effect.pre so the table sees updated options BEFORE the DOM renders,
|
|
116
|
+
// ensuring getRowModel() returns current data (not stale, one-frame-behind data).
|
|
117
|
+
// The reactive reads (state getters, data getter) happen OUTSIDE untrack
|
|
118
|
+
// so they become dependencies. The setOptions call is INSIDE untrack to
|
|
119
|
+
// prevent tracking notifierValue (read via store.state's stateNotifier
|
|
120
|
+
// interceptor inside setOptions), which would cause an infinite loop.
|
|
121
|
+
$effect.pre(() => {
|
|
122
|
+
// Read reactive getters to create $effect dependencies on external state
|
|
123
|
+
const state = mergedOptions.state
|
|
124
|
+
if (state) {
|
|
125
|
+
for (const key in state) {
|
|
126
|
+
void state[key]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
void mergedOptions.data
|
|
130
|
+
|
|
131
|
+
untrack(() => {
|
|
132
|
+
table.setOptions((prev) => {
|
|
133
|
+
return mergeObjects(prev, mergedOptions) as TableOptions<
|
|
134
|
+
TFeatures,
|
|
135
|
+
TData
|
|
136
|
+
>
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// 9. Subscribe component
|
|
142
|
+
table.Subscribe = function Subscribe<TSel>(props: {
|
|
143
|
+
selector: (state: TableState<TFeatures>) => TSel
|
|
144
|
+
children: ((state: Readonly<TSel>) => Snippet) | Snippet
|
|
145
|
+
}): any {
|
|
146
|
+
const selected = useStore(table.store, props.selector)
|
|
147
|
+
if (typeof props.children === 'function') {
|
|
148
|
+
return props.children(selected.current)
|
|
149
|
+
}
|
|
150
|
+
return props.children
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 10. State selector
|
|
154
|
+
const stateStore = useStore(table.store, selector)
|
|
155
|
+
|
|
156
|
+
Object.defineProperty(table, 'state', {
|
|
157
|
+
get() {
|
|
158
|
+
return stateStore.current
|
|
159
|
+
},
|
|
160
|
+
configurable: true,
|
|
161
|
+
enumerable: true,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return table
|
|
165
|
+
}
|