@shotleybuilder/svelte-table-kit 0.4.0 → 0.5.0

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 CHANGED
@@ -66,6 +66,7 @@ Svelte Table Kit brings Airtable-like functionality to your Svelte applications
66
66
  **AI-Ready:**
67
67
  - 🤖 JSON-schema driven configuration
68
68
  - 🧠 AI agents can generate table configs from natural language
69
+ - ⚡ **Reactive config prop** - Update table state dynamically without remounting (v0.5.0+)
69
70
  - 📋 Preset configurations for common use cases
70
71
  - 🔧 Programmatic table setup and state management
71
72
 
@@ -135,9 +136,18 @@ Customize initial table state programmatically:
135
136
  {data}
136
137
  {columns}
137
138
  config={{
138
- defaultColumnOrder: ['name', 'role', 'age', 'id'], // Set initial column order
139
- defaultColumnSizing: { name: 200, role: 150 }, // Set initial column widths
140
- defaultVisibleColumns: ['name', 'role', 'age'] // Hide 'id' column initially
139
+ id: 'my-view-v1',
140
+ version: '1.0',
141
+ defaultColumnOrder: ['name', 'role', 'age', 'id'],
142
+ defaultColumnSizing: { name: 200, role: 150 },
143
+ defaultVisibleColumns: ['name', 'role', 'age'],
144
+ defaultFilters: [
145
+ { id: 'f1', field: 'role', operator: 'equals', value: 'Developer' }
146
+ ],
147
+ defaultSorting: [
148
+ { columnId: 'name', direction: 'asc' }
149
+ ],
150
+ filterLogic: 'and'
141
151
  }}
142
152
  features={{
143
153
  columnVisibility: true,
@@ -148,6 +158,66 @@ Customize initial table state programmatically:
148
158
  />
149
159
  ```
150
160
 
161
+ ### Reactive Configuration (v0.5.0+)
162
+
163
+ **The `config` prop is fully reactive** - update it dynamically to change table state without remounting:
164
+
165
+ ```svelte
166
+ <script>
167
+ import { TableKit } from '@shotleybuilder/svelte-table-kit';
168
+
169
+ let tableConfig = $state({
170
+ id: 'query-1',
171
+ version: '1.0',
172
+ defaultFilters: [
173
+ { id: 'f1', field: 'status', operator: 'equals', value: 'active' }
174
+ ]
175
+ });
176
+
177
+ // Update config - table reacts automatically
178
+ function showPendingItems() {
179
+ tableConfig = {
180
+ id: 'query-2', // New ID triggers update
181
+ version: '1.0',
182
+ defaultFilters: [
183
+ { id: 'f1', field: 'status', operator: 'equals', value: 'pending' }
184
+ ]
185
+ };
186
+ }
187
+ </script>
188
+
189
+ <button on:click={showPendingItems}>Show Pending</button>
190
+ <TableKit {data} {columns} config={tableConfig} persistState={false} />
191
+ ```
192
+
193
+ **Perfect for AI-driven tables:**
194
+
195
+ ```svelte
196
+ <script>
197
+ let aiConfig = $state(undefined);
198
+
199
+ async function askAI(question) {
200
+ const response = await fetch('/api/nl-query', {
201
+ method: 'POST',
202
+ body: JSON.stringify({ question })
203
+ });
204
+ aiConfig = await response.json(); // Table updates automatically
205
+ }
206
+ </script>
207
+
208
+ <input
209
+ placeholder="Ask a question about the data..."
210
+ on:submit={(e) => askAI(e.target.value)}
211
+ />
212
+ <TableKit {data} {columns} config={aiConfig} persistState={false} />
213
+ ```
214
+
215
+ **Key Points:**
216
+ - Config changes detected by comparing `config.id`
217
+ - Set `persistState={false}` to prevent localStorage conflicts
218
+ - When config is active, localStorage is automatically ignored
219
+ - No `{#key}` blocks needed - updates are smooth and instant
220
+
151
221
  ### Feature Flags
152
222
 
153
223
  Control which features are enabled:
@@ -225,10 +295,10 @@ TableKit is headless by default. You can:
225
295
  |------|------|---------|-------------|
226
296
  | `data` | `T[]` | `[]` | Table data array |
227
297
  | `columns` | `ColumnDef<T>[]` | `[]` | Column definitions |
228
- | `config` | `TableConfig` | `undefined` | AI-generated or preset config |
298
+ | `config` | `TableConfig` | `undefined` | Reactive table configuration (requires `id` and `version`) |
229
299
  | `features` | `TableFeatures` | All enabled | Feature flags |
230
300
  | `storageKey` | `string` | `undefined` | LocalStorage key for persistence |
231
- | `persistState` | `boolean` | `true` | Enable state persistence |
301
+ | `persistState` | `boolean` | `true` | Enable state persistence (auto-disabled when config is active) |
232
302
  | `theme` | `'light' \| 'dark' \| 'auto'` | `'light'` | Theme mode |
233
303
  | `align` | `'left' \| 'center' \| 'right'` | `'left'` | Column text alignment |
234
304
  | `rowHeight` | `'short' \| 'medium' \| 'tall' \| 'extra_tall'` | `'medium'` | Row height preset |
@@ -237,6 +307,25 @@ TableKit is headless by default. You can:
237
307
  | `onRowSelect` | `(rows: T[]) => void` | `undefined` | Row selection handler |
238
308
  | `onStateChange` | `(state: TableState) => void` | `undefined` | State change handler |
239
309
 
310
+ ### TableConfig Type
311
+
312
+ ```typescript
313
+ interface TableConfig {
314
+ id: string; // Required: Unique identifier for change detection
315
+ version: string; // Required: Config version
316
+ defaultColumnOrder?: string[]; // Column IDs in display order
317
+ defaultColumnSizing?: Record<string, number>; // Column widths in pixels
318
+ defaultVisibleColumns?: string[]; // Visible column IDs (others hidden)
319
+ defaultFilters?: FilterCondition[]; // Initial filter conditions
320
+ defaultSorting?: SortConfig[]; // Initial sort configuration
321
+ filterLogic?: 'and' | 'or'; // Filter combination logic
322
+ pagination?: {
323
+ pageSize: number;
324
+ pageSizeOptions?: number[];
325
+ };
326
+ }
327
+ ```
328
+
240
329
  ---
241
330
 
242
331
  ## 🎯 Use Cases
@@ -47,32 +47,71 @@ export let onRowClick = void 0;
47
47
  export let onRowSelect = void 0;
48
48
  export let onStateChange = void 0;
49
49
  let sorting = writable([]);
50
- let columnVisibility = writable(
51
- persistState && storageKey ? loadColumnVisibility(storageKey) : {}
52
- );
53
- let columnSizing = writable(
54
- persistState && storageKey ? loadColumnSizing(storageKey) : {}
55
- );
56
- let columnFilters = writable(
57
- persistState && storageKey ? loadColumnFilters(storageKey) : []
58
- );
59
- let columnOrder = writable(
60
- persistState && storageKey ? loadColumnOrder(storageKey) : config?.defaultColumnOrder || []
61
- );
50
+ let columnVisibility = writable({});
51
+ let columnSizing = writable({});
52
+ let columnFilters = writable([]);
53
+ let columnOrder = writable([]);
62
54
  let filterConditions = writable([]);
63
55
  let filterLogic = writable("and");
64
56
  let filterBarExpanded = false;
57
+ let previousConfigId = config?.id;
58
+ let configInitialized = false;
65
59
  let grouping = writable([]);
66
60
  let expanded = writable(true);
67
61
  let groupBarExpanded = false;
62
+ $: {
63
+ const configChanged = config?.id && config.id !== previousConfigId;
64
+ const hasConfig = config !== void 0 && config !== null;
65
+ if (hasConfig && config && (configChanged || !configInitialized)) {
66
+ if (config.defaultColumnOrder && config.defaultColumnOrder.length > 0) {
67
+ columnOrder.set(config.defaultColumnOrder);
68
+ }
69
+ if (config.defaultVisibleColumns && columns.length > 0) {
70
+ const visibilityMap = {};
71
+ columns.forEach((col) => {
72
+ const colId = col.accessorKey || col.id;
73
+ if (config && config.defaultVisibleColumns) {
74
+ visibilityMap[colId] = config.defaultVisibleColumns.includes(colId);
75
+ }
76
+ });
77
+ columnVisibility.set(visibilityMap);
78
+ }
79
+ if (config.defaultSorting) {
80
+ sorting.set(config.defaultSorting);
81
+ }
82
+ if (config.defaultFilters) {
83
+ filterConditions.set(config.defaultFilters);
84
+ }
85
+ if (config.filterLogic) {
86
+ filterLogic.set(config.filterLogic);
87
+ }
88
+ if (config.defaultColumnSizing) {
89
+ columnSizing.set(config.defaultColumnSizing);
90
+ }
91
+ previousConfigId = config.id;
92
+ configInitialized = true;
93
+ } else if (!hasConfig && persistState && storageKey && !configInitialized) {
94
+ columnOrder.set(loadColumnOrder(storageKey) || []);
95
+ columnVisibility.set(loadColumnVisibility(storageKey) || {});
96
+ columnSizing.set(loadColumnSizing(storageKey) || {});
97
+ columnFilters.set(loadColumnFilters(storageKey) || []);
98
+ configInitialized = true;
99
+ } else if (!hasConfig && !configInitialized) {
100
+ configInitialized = true;
101
+ }
102
+ }
68
103
  $: horizontalPadding = columnSpacing === "narrow" ? 0.5 : columnSpacing === "wide" ? 2 : 1;
69
104
  $: verticalPadding = rowHeight === "short" ? 0.375 : rowHeight === "tall" ? 1 : rowHeight === "extra_tall" ? 1.5 : 0.75;
70
105
  $: filteredData = applyFilters(data, $filterConditions, $filterLogic);
71
- $: if (persistState && storageKey && isBrowser) {
72
- saveColumnVisibility(storageKey, $columnVisibility);
73
- saveColumnSizing(storageKey, $columnSizing);
74
- saveColumnFilters(storageKey, $columnFilters);
75
- saveColumnOrder(storageKey, $columnOrder);
106
+ $: {
107
+ const hasActiveConfig = config !== void 0 && config !== null;
108
+ const shouldPersist = persistState && !hasActiveConfig && storageKey && isBrowser;
109
+ if (shouldPersist) {
110
+ saveColumnVisibility(storageKey, $columnVisibility);
111
+ saveColumnSizing(storageKey, $columnSizing);
112
+ saveColumnFilters(storageKey, $columnFilters);
113
+ saveColumnOrder(storageKey, $columnOrder);
114
+ }
76
115
  }
77
116
  let showColumnPicker = false;
78
117
  let columnPickerButton = null;
package/dist/types.d.ts CHANGED
@@ -38,6 +38,7 @@ export interface TableConfig {
38
38
  right?: string[];
39
39
  };
40
40
  defaultFilters?: FilterCondition[];
41
+ filterLogic?: FilterLogic;
41
42
  defaultSorting?: SortConfig[];
42
43
  pagination?: {
43
44
  pageSize: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shotleybuilder/svelte-table-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A comprehensive, AI-configurable data table component for Svelte and SvelteKit, built on TanStack Table v8",
5
5
  "author": "Sertantai",
6
6
  "license": "MIT",