@shotleybuilder/svelte-table-kit 0.10.1 → 0.12.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
@@ -26,7 +26,7 @@ Svelte Table Kit brings Airtable-like functionality to your Svelte applications
26
26
  - 📋 **Column context menu** - Quick access to sort, filter, group, and hide actions
27
27
 
28
28
  **Advanced Filtering:**
29
- - 12 filter operators: equals, contains, starts with, greater than, etc.
29
+ - 14 filter operators: equals, contains, starts with, greater than, is_before, is_after, etc.
30
30
  - AND/OR logic between conditions
31
31
  - Collapsible FilterBar UI (space-efficient)
32
32
  - Active filter count badge
@@ -35,6 +35,7 @@ Svelte Table Kit brings Airtable-like functionality to your Svelte applications
35
35
  - **Fuzzy search** - Type to quickly find columns in large tables with highlighted matches (v0.8.0+)
36
36
  - **Value suggestions** - Autocomplete dropdown shows existing column values as you type (v0.10.0+)
37
37
  - **Numeric range hints** - Numeric columns display min/max range in the value input (v0.10.0+)
38
+ - **Data type awareness** - Operators and value inputs adapt based on column data type (v0.11.0+)
38
39
 
39
40
  **Sorting Options:**
40
41
  - **Column header mode** (default) - Click headers to sort with ↑↓↕ indicators
@@ -47,6 +47,9 @@ export let features = {
47
47
  export let onRowClick = void 0;
48
48
  export let onRowSelect = void 0;
49
49
  export let onStateChange = void 0;
50
+ function getColumnId(col) {
51
+ return col.accessorKey || col.id || "";
52
+ }
50
53
  let sorting = writable([]);
51
54
  let columnVisibility = writable({});
52
55
  let columnSizing = writable({});
@@ -79,7 +82,7 @@ $: {
79
82
  if (config.defaultVisibleColumns && columns.length > 0) {
80
83
  const visibilityMap = {};
81
84
  columns.forEach((col) => {
82
- const colId = col.accessorKey || col.id;
85
+ const colId = getColumnId(col);
83
86
  if (config && config.defaultVisibleColumns) {
84
87
  visibilityMap[colId] = config.defaultVisibleColumns.includes(colId);
85
88
  }
@@ -293,7 +296,7 @@ function handleDrop(targetColumnId) {
293
296
  draggedColumnId = null;
294
297
  }
295
298
  $: if ($columnOrder.length === 0 && columns.length > 0) {
296
- columnOrder.set(columns.map((col) => col.accessorKey || col.id));
299
+ columnOrder.set(columns.map((col) => getColumnId(col)));
297
300
  }
298
301
  function showCellContextMenu(event, cell) {
299
302
  if (features.filtering === false) return;
@@ -333,8 +336,8 @@ $: if (onStateChange) {
333
336
  columnVisibility: $columnVisibility,
334
337
  columnOrder: $columnOrder,
335
338
  columnSizing: $columnSizing,
336
- columnFilters: $columnFilters,
337
- sorting: $sorting,
339
+ columnFilters: $filterConditions,
340
+ sorting: $sorting.map((s) => ({ columnId: s.id, direction: s.desc ? "desc" : "asc" })),
338
341
  pagination: $table.getState().pagination
339
342
  });
340
343
  }
@@ -639,8 +642,10 @@ $: if (onStateChange) {
639
642
  >
640
643
  {#if !header.isPlaceholder}
641
644
  <div class="th-wrapper">
645
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
642
646
  <div
643
647
  class="th-content"
648
+ role={features.columnReordering !== false ? 'button' : undefined}
644
649
  style="padding: {verticalPadding}rem {horizontalPadding}rem; cursor: {features.columnReordering !==
645
650
  false
646
651
  ? 'grab'
@@ -660,10 +665,7 @@ $: if (onStateChange) {
660
665
  />
661
666
  </span>
662
667
  <span class="sort-icon">
663
- {{
664
- asc: '↑',
665
- desc: '↓'
666
- }[header.column.getIsSorted()] ?? '↕'}
668
+ {header.column.getIsSorted() === 'asc' ? '↑' : header.column.getIsSorted() === 'desc' ? '↓' : '↕'}
667
669
  </span>
668
670
  </button>
669
671
  {:else}
@@ -1,19 +1,19 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import { type ColumnDef } from '@tanstack/svelte-table';
3
3
  import type { TableKitProps } from './types';
4
- declare class __sveltets_Render<T> {
4
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
5
5
  props(): {
6
6
  data?: T[] | undefined;
7
7
  columns?: ColumnDef<T>[] | undefined;
8
8
  config?: TableKitProps<T_1>["config"];
9
- storageKey?: TableKitProps<T_1>["storageKey"];
9
+ storageKey?: NonNullable<TableKitProps<T_1>["storageKey"]>;
10
10
  persistState?: TableKitProps<T_1>["persistState"];
11
11
  align?: TableKitProps<T_1>["align"];
12
12
  rowHeight?: TableKitProps<T_1>["rowHeight"];
13
13
  columnSpacing?: TableKitProps<T_1>["columnSpacing"];
14
- features?: TableKitProps<T_1>["features"];
14
+ features?: NonNullable<TableKitProps<T_1>["features"]>;
15
15
  onRowClick?: ((row: T) => void) | undefined;
16
- onRowSelect?: ((rows: T[]) => void) | undefined;
16
+ /** @deprecated Row selection not yet implemented - reserved for future use */ onRowSelect?: ((rows: T[]) => void) | undefined;
17
17
  onStateChange?: TableKitProps<T_1>["onStateChange"];
18
18
  };
19
19
  events(): {} & {
@@ -23,14 +23,14 @@ declare class __sveltets_Render<T> {
23
23
  'toolbar-left': {};
24
24
  empty: {};
25
25
  cell: {
26
- cell: import("@tanstack/table-core").Cell<Record<string, any>, unknown>;
26
+ cell: import("@tanstack/table-core").Cell<T, unknown>;
27
27
  column: string;
28
28
  };
29
29
  };
30
30
  }
31
- export type TableKitProps_<T> = ReturnType<__sveltets_Render<T>['props']>;
32
- export type TableKitEvents<T> = ReturnType<__sveltets_Render<T>['events']>;
33
- export type TableKitSlots<T> = ReturnType<__sveltets_Render<T>['slots']>;
34
- export default class TableKit<T> extends SvelteComponent<TableKitProps_<T>, TableKitEvents<T>, TableKitSlots<T>> {
31
+ export type TableKitProps_<T extends Record<string, unknown>> = ReturnType<__sveltets_Render<T>['props']>;
32
+ export type TableKitEvents<T extends Record<string, unknown>> = ReturnType<__sveltets_Render<T>['events']>;
33
+ export type TableKitSlots<T extends Record<string, unknown>> = ReturnType<__sveltets_Render<T>['slots']>;
34
+ export default class TableKit<T extends Record<string, unknown>> extends SvelteComponent<TableKitProps_<T>, TableKitEvents<T>, TableKitSlots<T>> {
35
35
  }
36
36
  export {};
@@ -1,5 +1,6 @@
1
1
  <script>import { loadFilterColumnOrderMode, saveFilterColumnOrderMode } from "../stores/persistence";
2
2
  import { fuzzyMatch, highlightMatches } from "../utils/fuzzy";
3
+ import { getOperatorsForType } from "../utils/filters";
3
4
  import { onMount, tick } from "svelte";
4
5
  export let condition;
5
6
  export let columns;
@@ -131,25 +132,30 @@ $: filteredColumns = (() => {
131
132
  $: if (filteredColumns) {
132
133
  highlightedIndex = 0;
133
134
  }
135
+ $: selectedColumn = condition.field ? columns.find((c) => getColumnId(c) === condition.field) : null;
134
136
  $: selectedColumnLabel = (() => {
135
137
  if (!condition.field) return "";
136
- const col = columns.find((c) => getColumnId(c) === condition.field);
137
- return col ? getColumnLabel(col) : condition.field;
138
+ return selectedColumn ? getColumnLabel(selectedColumn) : condition.field;
138
139
  })();
139
- const operatorOptions = [
140
- { value: "equals", label: "equals" },
141
- { value: "not_equals", label: "does not equal" },
142
- { value: "contains", label: "contains" },
143
- { value: "not_contains", label: "does not contain" },
144
- { value: "starts_with", label: "starts with" },
145
- { value: "ends_with", label: "ends with" },
146
- { value: "is_empty", label: "is empty" },
147
- { value: "is_not_empty", label: "is not empty" },
148
- { value: "greater_than", label: ">" },
149
- { value: "less_than", label: "<" },
150
- { value: "greater_or_equal", label: ">=" },
151
- { value: "less_or_equal", label: "<=" }
152
- ];
140
+ $: columnDataType = (() => {
141
+ if (!selectedColumn) return "text";
142
+ const meta = selectedColumn.meta;
143
+ return meta?.dataType || "text";
144
+ })();
145
+ $: selectOptions = (() => {
146
+ if (!selectedColumn) return [];
147
+ const meta = selectedColumn.meta;
148
+ return meta?.selectOptions || [];
149
+ })();
150
+ $: operatorOptions = getOperatorsForType(columnDataType);
151
+ $: {
152
+ if (condition.field && operatorOptions.length > 0) {
153
+ const currentOperatorValid = operatorOptions.some((op) => op.value === condition.operator);
154
+ if (!currentOperatorValid) {
155
+ onUpdate({ ...condition, operator: "equals" });
156
+ }
157
+ }
158
+ }
153
159
  function selectColumn(col) {
154
160
  const field = getColumnId(col);
155
161
  onUpdate({ ...condition, field });
@@ -339,54 +345,102 @@ $: valueDisabled = condition.operator === "is_empty" || condition.operator === "
339
345
  </select>
340
346
 
341
347
  <div class="value-input-wrapper" bind:this={valueSuggestionsRef}>
342
- <input
343
- bind:this={valueInputRef}
344
- type="text"
345
- class="value-input"
346
- value={condition.value || ''}
347
- on:input={handleValueChange}
348
- on:focus={handleValueFocus}
349
- on:keydown={handleValueKeydown}
350
- disabled={valueDisabled}
351
- placeholder={valueDisabled ? 'N/A' : numericRange ? `${numericRange.min} - ${numericRange.max}` : 'Enter value...'}
352
- autocomplete="off"
353
- />
354
-
355
- {#if numericRange && !valueDisabled}
356
- <div class="numeric-range-hint">
357
- Range: {numericRange.min} - {numericRange.max}
358
- </div>
359
- {/if}
360
-
361
- {#if showValueSuggestions && filteredValueSuggestions.length > 0 && !numericRange}
362
- <div class="value-suggestions">
363
- {#each filteredValueSuggestions as { value, matchedIndices }, i}
364
- <button
365
- class="suggestion-option"
366
- class:highlighted={i === valueSuggestionIndex}
367
- on:click={() => selectValueSuggestion(value)}
368
- on:mouseenter={() => (valueSuggestionIndex = i)}
369
- type="button"
370
- >
371
- {#if matchedIndices.length > 0}
372
- {#each highlightMatches(value, matchedIndices) as segment}
373
- {#if segment.isMatch}
374
- <mark class="match-highlight">{segment.text}</mark>
375
- {:else}
376
- {segment.text}
377
- {/if}
378
- {/each}
379
- {:else}
380
- {value}
381
- {/if}
382
- </button>
348
+ {#if columnDataType === 'boolean'}
349
+ <!-- Boolean: dropdown with true/false -->
350
+ <select
351
+ class="value-input"
352
+ value={condition.value || ''}
353
+ on:change={handleValueChange}
354
+ disabled={valueDisabled}
355
+ >
356
+ <option value="">Select...</option>
357
+ <option value="true">True</option>
358
+ <option value="false">False</option>
359
+ </select>
360
+ {:else if columnDataType === 'select' && selectOptions.length > 0}
361
+ <!-- Select: dropdown with options from meta -->
362
+ <select
363
+ class="value-input"
364
+ value={condition.value || ''}
365
+ on:change={handleValueChange}
366
+ disabled={valueDisabled}
367
+ >
368
+ <option value="">Select...</option>
369
+ {#each selectOptions as opt}
370
+ <option value={opt.value}>{opt.label}</option>
383
371
  {/each}
384
- {#if columnValues.length > 50}
385
- <div class="suggestions-overflow">
386
- and {columnValues.length - 50} more...
387
- </div>
388
- {/if}
389
- </div>
372
+ </select>
373
+ {:else if columnDataType === 'date'}
374
+ <!-- Date: date input -->
375
+ <input
376
+ bind:this={valueInputRef}
377
+ type="date"
378
+ class="value-input"
379
+ value={condition.value || ''}
380
+ on:input={handleValueChange}
381
+ disabled={valueDisabled}
382
+ />
383
+ {:else if columnDataType === 'number'}
384
+ <!-- Number: number input with range hint -->
385
+ <input
386
+ bind:this={valueInputRef}
387
+ type="number"
388
+ class="value-input"
389
+ value={condition.value || ''}
390
+ on:input={handleValueChange}
391
+ disabled={valueDisabled}
392
+ placeholder={valueDisabled ? 'N/A' : numericRange ? `${numericRange.min} - ${numericRange.max}` : 'Enter number...'}
393
+ />
394
+ {#if numericRange && !valueDisabled}
395
+ <div class="numeric-range-hint">
396
+ Range: {numericRange.min} - {numericRange.max}
397
+ </div>
398
+ {/if}
399
+ {:else}
400
+ <!-- Text: text input with autocomplete suggestions -->
401
+ <input
402
+ bind:this={valueInputRef}
403
+ type="text"
404
+ class="value-input"
405
+ value={condition.value || ''}
406
+ on:input={handleValueChange}
407
+ on:focus={handleValueFocus}
408
+ on:keydown={handleValueKeydown}
409
+ disabled={valueDisabled}
410
+ placeholder={valueDisabled ? 'N/A' : 'Enter value...'}
411
+ autocomplete="off"
412
+ />
413
+
414
+ {#if showValueSuggestions && filteredValueSuggestions.length > 0}
415
+ <div class="value-suggestions">
416
+ {#each filteredValueSuggestions as { value, matchedIndices }, i}
417
+ <button
418
+ class="suggestion-option"
419
+ class:highlighted={i === valueSuggestionIndex}
420
+ on:click={() => selectValueSuggestion(value)}
421
+ on:mouseenter={() => (valueSuggestionIndex = i)}
422
+ type="button"
423
+ >
424
+ {#if matchedIndices.length > 0}
425
+ {#each highlightMatches(value, matchedIndices) as segment}
426
+ {#if segment.isMatch}
427
+ <mark class="match-highlight">{segment.text}</mark>
428
+ {:else}
429
+ {segment.text}
430
+ {/if}
431
+ {/each}
432
+ {:else}
433
+ {value}
434
+ {/if}
435
+ </button>
436
+ {/each}
437
+ {#if columnValues.length > 50}
438
+ <div class="suggestions-overflow">
439
+ and {columnValues.length - 50} more...
440
+ </div>
441
+ {/if}
442
+ </div>
443
+ {/if}
390
444
  {/if}
391
445
  </div>
392
446
 
@@ -31,8 +31,11 @@ function clearAllGroups() {
31
31
  onGroupingChange([]);
32
32
  setExpanded(false);
33
33
  }
34
+ function getColumnId(col) {
35
+ return col.accessorKey || col.id || "";
36
+ }
34
37
  $: availableColumns = columns.filter((col) => {
35
- const columnId = col.accessorKey || col.id;
38
+ const columnId = getColumnId(col);
36
39
  return columnId && col.enableGrouping !== false;
37
40
  });
38
41
  $: hasGroups = grouping.length > 0;
@@ -85,7 +88,7 @@ $: canAddMore = grouping.length < MAX_LEVELS;
85
88
  >
86
89
  <option value="">Select field...</option>
87
90
  {#each availableColumns as column}
88
- {@const columnId = column.accessorKey || column.id}
91
+ {@const columnId = getColumnId(column)}
89
92
  <option value={columnId}>
90
93
  {column.header || columnId}
91
94
  </option>
package/dist/index.d.ts CHANGED
@@ -3,10 +3,11 @@ export { default as FilterBar } from './components/FilterBar.svelte';
3
3
  export { default as FilterConditionEditor } from './components/FilterCondition.svelte';
4
4
  export { default as GroupBar } from './components/GroupBar.svelte';
5
5
  export { default as CellContextMenu } from './components/CellContextMenu.svelte';
6
- export type { TableKitProps, TableConfig, ViewPreset, FilterCondition, FilterOperator, FilterLogic, ColumnOrderMode, SortConfig, ClassNameMap, TableFeatures, TableState } from './types';
6
+ export type { TableKitProps, TableConfig, ViewPreset, FilterCondition, FilterOperator, FilterLogic, ColumnOrderMode, ColumnDataType, ColumnMeta, SortConfig, ClassNameMap, TableFeatures, TableState } from './types';
7
7
  export { presets } from './presets';
8
8
  export { generateTableConfig, validateTableConfig, mergeConfigs } from './utils/config';
9
- export { createTextFilter, createSelectFilter, createNumericFilter, evaluateCondition, applyFilters } from './utils/filters';
9
+ export { createTextFilter, createSelectFilter, createNumericFilter, evaluateCondition, applyFilters, getOperatorsForType } from './utils/filters';
10
+ export type { OperatorOption } from './utils/filters';
10
11
  export { formatDate, formatCurrency, formatNumber, formatPercent } from './utils/formatters';
11
12
  export { fuzzyMatch, fuzzySearch, highlightMatches } from './utils/fuzzy';
12
13
  export type { FuzzyMatch } from './utils/fuzzy';
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ export { presets } from './presets';
11
11
  // Utilities for AI configuration
12
12
  export { generateTableConfig, validateTableConfig, mergeConfigs } from './utils/config';
13
13
  // Filter utilities
14
- export { createTextFilter, createSelectFilter, createNumericFilter, evaluateCondition, applyFilters } from './utils/filters';
14
+ export { createTextFilter, createSelectFilter, createNumericFilter, evaluateCondition, applyFilters, getOperatorsForType } from './utils/filters';
15
15
  // Formatters
16
16
  export { formatDate, formatCurrency, formatNumber, formatPercent } from './utils/formatters';
17
17
  // Fuzzy search utilities
package/dist/types.d.ts CHANGED
@@ -55,6 +55,25 @@ export interface ViewPreset {
55
55
  export type FilterOperator = 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'is_empty' | 'is_not_empty' | 'greater_than' | 'less_than' | 'greater_or_equal' | 'less_or_equal' | 'is_before' | 'is_after';
56
56
  export type FilterLogic = 'and' | 'or';
57
57
  export type ColumnOrderMode = 'definition' | 'ui' | 'alphabetical';
58
+ /**
59
+ * Column data type for filter operator adaptation
60
+ */
61
+ export type ColumnDataType = 'text' | 'number' | 'date' | 'boolean' | 'select';
62
+ /**
63
+ * Extended column meta for data type awareness
64
+ * Use in column definitions: { meta: { dataType: 'number', selectOptions: [...] } }
65
+ */
66
+ export interface ColumnMeta {
67
+ /** Data type for filter operator adaptation */
68
+ dataType?: ColumnDataType;
69
+ /** Options for 'select' type columns */
70
+ selectOptions?: {
71
+ value: string;
72
+ label: string;
73
+ }[];
74
+ /** Column group for grouped picker (future feature) */
75
+ group?: string;
76
+ }
58
77
  export interface FilterCondition {
59
78
  id: string;
60
79
  field: string;
@@ -1,4 +1,15 @@
1
- import type { FilterCondition, FilterLogic } from '../types';
1
+ import type { FilterCondition, FilterOperator, FilterLogic, ColumnDataType } from '../types';
2
+ /**
3
+ * Operator option for UI display
4
+ */
5
+ export interface OperatorOption {
6
+ value: FilterOperator;
7
+ label: string;
8
+ }
9
+ /**
10
+ * Get operators available for a specific data type
11
+ */
12
+ export declare function getOperatorsForType(dataType?: ColumnDataType): OperatorOption[];
2
13
  /**
3
14
  * Evaluate a single filter condition against a row value
4
15
  */
@@ -6,7 +17,7 @@ export declare function evaluateCondition(condition: FilterCondition, rowValue:
6
17
  /**
7
18
  * Filter data array by multiple conditions with AND or OR logic
8
19
  */
9
- export declare function applyFilters<T extends Record<string, any>>(data: T[], conditions: FilterCondition[], logic?: FilterLogic): T[];
20
+ export declare function applyFilters<T extends Record<string, unknown>>(data: T[], conditions: FilterCondition[], logic?: FilterLogic): T[];
10
21
  /**
11
22
  * Create a text filter configuration
12
23
  */
@@ -1,4 +1,69 @@
1
1
  // Filter creation and evaluation utilities
2
+ /**
3
+ * All available operators with labels
4
+ */
5
+ const ALL_OPERATORS = [
6
+ { value: 'equals', label: 'equals' },
7
+ { value: 'not_equals', label: 'does not equal' },
8
+ { value: 'contains', label: 'contains' },
9
+ { value: 'not_contains', label: 'does not contain' },
10
+ { value: 'starts_with', label: 'starts with' },
11
+ { value: 'ends_with', label: 'ends with' },
12
+ { value: 'is_empty', label: 'is empty' },
13
+ { value: 'is_not_empty', label: 'is not empty' },
14
+ { value: 'greater_than', label: '>' },
15
+ { value: 'less_than', label: '<' },
16
+ { value: 'greater_or_equal', label: '>=' },
17
+ { value: 'less_or_equal', label: '<=' },
18
+ { value: 'is_before', label: 'is before' },
19
+ { value: 'is_after', label: 'is after' }
20
+ ];
21
+ /**
22
+ * Get operators available for a specific data type
23
+ */
24
+ export function getOperatorsForType(dataType = 'text') {
25
+ switch (dataType) {
26
+ case 'text':
27
+ return ALL_OPERATORS.filter((op) => [
28
+ 'equals',
29
+ 'not_equals',
30
+ 'contains',
31
+ 'not_contains',
32
+ 'starts_with',
33
+ 'ends_with',
34
+ 'is_empty',
35
+ 'is_not_empty'
36
+ ].includes(op.value));
37
+ case 'number':
38
+ return ALL_OPERATORS.filter((op) => [
39
+ 'equals',
40
+ 'not_equals',
41
+ 'greater_than',
42
+ 'less_than',
43
+ 'greater_or_equal',
44
+ 'less_or_equal',
45
+ 'is_empty',
46
+ 'is_not_empty'
47
+ ].includes(op.value));
48
+ case 'date':
49
+ return ALL_OPERATORS.filter((op) => ['equals', 'not_equals', 'is_before', 'is_after', 'is_empty', 'is_not_empty'].includes(op.value));
50
+ case 'boolean':
51
+ return ALL_OPERATORS.filter((op) => ['equals', 'is_empty', 'is_not_empty'].includes(op.value));
52
+ case 'select':
53
+ return ALL_OPERATORS.filter((op) => ['equals', 'not_equals', 'is_empty', 'is_not_empty'].includes(op.value));
54
+ default:
55
+ return ALL_OPERATORS.filter((op) => [
56
+ 'equals',
57
+ 'not_equals',
58
+ 'contains',
59
+ 'not_contains',
60
+ 'starts_with',
61
+ 'ends_with',
62
+ 'is_empty',
63
+ 'is_not_empty'
64
+ ].includes(op.value));
65
+ }
66
+ }
2
67
  /**
3
68
  * Evaluate a single filter condition against a row value
4
69
  */
@@ -32,6 +97,16 @@ export function evaluateCondition(condition, rowValue) {
32
97
  return Number(rowValue) >= Number(value);
33
98
  case 'less_or_equal':
34
99
  return Number(rowValue) <= Number(value);
100
+ case 'is_before': {
101
+ const rowDate = new Date(rowValue);
102
+ const filterDate = new Date(value);
103
+ return !isNaN(rowDate.getTime()) && !isNaN(filterDate.getTime()) && rowDate < filterDate;
104
+ }
105
+ case 'is_after': {
106
+ const rowDate = new Date(rowValue);
107
+ const filterDate = new Date(value);
108
+ return !isNaN(rowDate.getTime()) && !isNaN(filterDate.getTime()) && rowDate > filterDate;
109
+ }
35
110
  default:
36
111
  return true;
37
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shotleybuilder/svelte-table-kit",
3
- "version": "0.10.1",
3
+ "version": "0.12.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",