@shotleybuilder/svelte-table-kit 0.6.0 → 0.10.1
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 +11 -0
- package/dist/TableKit.svelte +66 -1
- package/dist/TableKit.svelte.d.ts +1 -1
- package/dist/components/CellContextMenu.svelte +208 -0
- package/dist/components/CellContextMenu.svelte.d.ts +41 -0
- package/dist/components/ColumnMenu.svelte +3 -3
- package/dist/components/FilterBar.svelte +76 -0
- package/dist/components/FilterBar.svelte.d.ts +3 -0
- package/dist/components/FilterCondition.svelte +571 -28
- package/dist/components/FilterCondition.svelte.d.ts +7 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/stores/persistence.d.ts +10 -1
- package/dist/stores/persistence.js +14 -1
- package/dist/types.d.ts +1 -0
- package/dist/utils/fuzzy.d.ts +47 -0
- package/dist/utils/fuzzy.js +142 -0
- package/package.json +76 -76
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { VisibilityState, ColumnSizingState, ColumnFiltersState, ColumnOrderState, SortingState, PaginationState } from '@tanstack/svelte-table';
|
|
2
|
+
import type { ColumnOrderMode } from '../types';
|
|
2
3
|
/**
|
|
3
4
|
* Check if we're in a browser environment
|
|
4
|
-
*
|
|
5
|
+
* Uses cross-bundler compatible check instead of SvelteKit-specific imports
|
|
5
6
|
*/
|
|
6
7
|
export declare const isBrowser: boolean;
|
|
7
8
|
/**
|
|
@@ -52,6 +53,14 @@ export declare function loadPagination(storageKey: string, defaultPageSize?: num
|
|
|
52
53
|
* Save pagination state to localStorage
|
|
53
54
|
*/
|
|
54
55
|
export declare function savePagination(storageKey: string, state: PaginationState): void;
|
|
56
|
+
/**
|
|
57
|
+
* Load filter column order mode from localStorage
|
|
58
|
+
*/
|
|
59
|
+
export declare function loadFilterColumnOrderMode(storageKey: string): ColumnOrderMode;
|
|
60
|
+
/**
|
|
61
|
+
* Save filter column order mode to localStorage
|
|
62
|
+
*/
|
|
63
|
+
export declare function saveFilterColumnOrderMode(storageKey: string, mode: ColumnOrderMode): void;
|
|
55
64
|
/**
|
|
56
65
|
* Clear all table state from localStorage
|
|
57
66
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// LocalStorage persistence utilities
|
|
2
2
|
/**
|
|
3
3
|
* Check if we're in a browser environment
|
|
4
|
-
*
|
|
4
|
+
* Uses cross-bundler compatible check instead of SvelteKit-specific imports
|
|
5
5
|
*/
|
|
6
6
|
export const isBrowser = typeof window !== 'undefined' && typeof localStorage !== 'undefined';
|
|
7
7
|
/**
|
|
@@ -107,6 +107,18 @@ export function loadPagination(storageKey, defaultPageSize = 10) {
|
|
|
107
107
|
export function savePagination(storageKey, state) {
|
|
108
108
|
saveToStorage(`${storageKey}_pagination`, state);
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Load filter column order mode from localStorage
|
|
112
|
+
*/
|
|
113
|
+
export function loadFilterColumnOrderMode(storageKey) {
|
|
114
|
+
return loadFromStorage(`${storageKey}_filter_column_order_mode`, 'definition');
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Save filter column order mode to localStorage
|
|
118
|
+
*/
|
|
119
|
+
export function saveFilterColumnOrderMode(storageKey, mode) {
|
|
120
|
+
saveToStorage(`${storageKey}_filter_column_order_mode`, mode);
|
|
121
|
+
}
|
|
110
122
|
/**
|
|
111
123
|
* Clear all table state from localStorage
|
|
112
124
|
*/
|
|
@@ -120,6 +132,7 @@ export function clearTableState(storageKey) {
|
|
|
120
132
|
localStorage.removeItem(`${storageKey}_column_order`);
|
|
121
133
|
localStorage.removeItem(`${storageKey}_sorting`);
|
|
122
134
|
localStorage.removeItem(`${storageKey}_pagination`);
|
|
135
|
+
localStorage.removeItem(`${storageKey}_filter_column_order_mode`);
|
|
123
136
|
}
|
|
124
137
|
catch (error) {
|
|
125
138
|
console.error('Failed to clear table state:', error);
|
package/dist/types.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface ViewPreset {
|
|
|
54
54
|
}
|
|
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
|
+
export type ColumnOrderMode = 'definition' | 'ui' | 'alphabetical';
|
|
57
58
|
export interface FilterCondition {
|
|
58
59
|
id: string;
|
|
59
60
|
field: string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy search utilities for column/field picker
|
|
3
|
+
*/
|
|
4
|
+
export interface FuzzyMatch {
|
|
5
|
+
/** The original string that was matched */
|
|
6
|
+
text: string;
|
|
7
|
+
/** The match score (higher = better match) */
|
|
8
|
+
score: number;
|
|
9
|
+
/** Indices of characters that matched in the original string */
|
|
10
|
+
matchedIndices: number[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Perform fuzzy matching of a search pattern against a target string.
|
|
14
|
+
* Returns null if no match, or a FuzzyMatch object with score and matched indices.
|
|
15
|
+
*
|
|
16
|
+
* Scoring:
|
|
17
|
+
* - Consecutive matches score higher
|
|
18
|
+
* - Matches at word boundaries score higher
|
|
19
|
+
* - Matches at the start of the string score higher
|
|
20
|
+
* - Case-insensitive matching
|
|
21
|
+
*
|
|
22
|
+
* @param pattern - The search pattern (e.g., "cdt" to find "Created Date")
|
|
23
|
+
* @param target - The target string to search in
|
|
24
|
+
* @returns FuzzyMatch object or null if no match
|
|
25
|
+
*/
|
|
26
|
+
export declare function fuzzyMatch(pattern: string, target: string): FuzzyMatch | null;
|
|
27
|
+
/**
|
|
28
|
+
* Search through an array of strings and return matches sorted by score.
|
|
29
|
+
*
|
|
30
|
+
* @param pattern - The search pattern
|
|
31
|
+
* @param items - Array of strings to search
|
|
32
|
+
* @param limit - Maximum number of results to return (default: all)
|
|
33
|
+
* @returns Array of FuzzyMatch objects sorted by score (highest first)
|
|
34
|
+
*/
|
|
35
|
+
export declare function fuzzySearch(pattern: string, items: string[], limit?: number): FuzzyMatch[];
|
|
36
|
+
/**
|
|
37
|
+
* Highlight matched characters in a string by wrapping them in a specified tag.
|
|
38
|
+
* Returns an array of segments with matched/unmatched flags for rendering.
|
|
39
|
+
*
|
|
40
|
+
* @param text - The original text
|
|
41
|
+
* @param matchedIndices - Array of indices that matched
|
|
42
|
+
* @returns Array of text segments with isMatch flag
|
|
43
|
+
*/
|
|
44
|
+
export declare function highlightMatches(text: string, matchedIndices: number[]): {
|
|
45
|
+
text: string;
|
|
46
|
+
isMatch: boolean;
|
|
47
|
+
}[];
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy search utilities for column/field picker
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Perform fuzzy matching of a search pattern against a target string.
|
|
6
|
+
* Returns null if no match, or a FuzzyMatch object with score and matched indices.
|
|
7
|
+
*
|
|
8
|
+
* Scoring:
|
|
9
|
+
* - Consecutive matches score higher
|
|
10
|
+
* - Matches at word boundaries score higher
|
|
11
|
+
* - Matches at the start of the string score higher
|
|
12
|
+
* - Case-insensitive matching
|
|
13
|
+
*
|
|
14
|
+
* @param pattern - The search pattern (e.g., "cdt" to find "Created Date")
|
|
15
|
+
* @param target - The target string to search in
|
|
16
|
+
* @returns FuzzyMatch object or null if no match
|
|
17
|
+
*/
|
|
18
|
+
export function fuzzyMatch(pattern, target) {
|
|
19
|
+
if (!pattern) {
|
|
20
|
+
return { text: target, score: 0, matchedIndices: [] };
|
|
21
|
+
}
|
|
22
|
+
const patternLower = pattern.toLowerCase();
|
|
23
|
+
const targetLower = target.toLowerCase();
|
|
24
|
+
// Quick check: all pattern characters must exist in target
|
|
25
|
+
let patternIdx = 0;
|
|
26
|
+
for (let i = 0; i < targetLower.length && patternIdx < patternLower.length; i++) {
|
|
27
|
+
if (targetLower[i] === patternLower[patternIdx]) {
|
|
28
|
+
patternIdx++;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (patternIdx !== patternLower.length) {
|
|
32
|
+
return null; // Not all pattern characters found
|
|
33
|
+
}
|
|
34
|
+
// Find optimal match positions using greedy algorithm
|
|
35
|
+
const matchedIndices = [];
|
|
36
|
+
let score = 0;
|
|
37
|
+
patternIdx = 0;
|
|
38
|
+
let lastMatchIdx = -1;
|
|
39
|
+
let consecutiveCount = 0;
|
|
40
|
+
for (let i = 0; i < targetLower.length && patternIdx < patternLower.length; i++) {
|
|
41
|
+
if (targetLower[i] === patternLower[patternIdx]) {
|
|
42
|
+
matchedIndices.push(i);
|
|
43
|
+
// Base score for match
|
|
44
|
+
score += 1;
|
|
45
|
+
// Bonus for consecutive matches
|
|
46
|
+
if (lastMatchIdx === i - 1) {
|
|
47
|
+
consecutiveCount++;
|
|
48
|
+
score += consecutiveCount * 2;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
consecutiveCount = 0;
|
|
52
|
+
}
|
|
53
|
+
// Bonus for match at start of string
|
|
54
|
+
if (i === 0) {
|
|
55
|
+
score += 10;
|
|
56
|
+
}
|
|
57
|
+
// Bonus for match at word boundary (after space, underscore, or uppercase)
|
|
58
|
+
if (i > 0) {
|
|
59
|
+
const prevChar = target[i - 1];
|
|
60
|
+
const currChar = target[i];
|
|
61
|
+
if (prevChar === ' ' || prevChar === '_' || prevChar === '-') {
|
|
62
|
+
score += 5;
|
|
63
|
+
}
|
|
64
|
+
else if (currChar === currChar.toUpperCase() && currChar !== currChar.toLowerCase()) {
|
|
65
|
+
// CamelCase boundary
|
|
66
|
+
score += 3;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
lastMatchIdx = i;
|
|
70
|
+
patternIdx++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Bonus for shorter target strings (prefer more specific matches)
|
|
74
|
+
score += Math.max(0, 20 - target.length);
|
|
75
|
+
// Bonus if pattern matches significant portion of target
|
|
76
|
+
const coverageRatio = pattern.length / target.length;
|
|
77
|
+
score += Math.floor(coverageRatio * 10);
|
|
78
|
+
return {
|
|
79
|
+
text: target,
|
|
80
|
+
score,
|
|
81
|
+
matchedIndices
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Search through an array of strings and return matches sorted by score.
|
|
86
|
+
*
|
|
87
|
+
* @param pattern - The search pattern
|
|
88
|
+
* @param items - Array of strings to search
|
|
89
|
+
* @param limit - Maximum number of results to return (default: all)
|
|
90
|
+
* @returns Array of FuzzyMatch objects sorted by score (highest first)
|
|
91
|
+
*/
|
|
92
|
+
export function fuzzySearch(pattern, items, limit) {
|
|
93
|
+
if (!pattern) {
|
|
94
|
+
// Return all items with zero score when no pattern
|
|
95
|
+
const results = items.map((text) => ({ text, score: 0, matchedIndices: [] }));
|
|
96
|
+
return limit ? results.slice(0, limit) : results;
|
|
97
|
+
}
|
|
98
|
+
const matches = [];
|
|
99
|
+
for (const item of items) {
|
|
100
|
+
const match = fuzzyMatch(pattern, item);
|
|
101
|
+
if (match) {
|
|
102
|
+
matches.push(match);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Sort by score descending
|
|
106
|
+
matches.sort((a, b) => b.score - a.score);
|
|
107
|
+
return limit ? matches.slice(0, limit) : matches;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Highlight matched characters in a string by wrapping them in a specified tag.
|
|
111
|
+
* Returns an array of segments with matched/unmatched flags for rendering.
|
|
112
|
+
*
|
|
113
|
+
* @param text - The original text
|
|
114
|
+
* @param matchedIndices - Array of indices that matched
|
|
115
|
+
* @returns Array of text segments with isMatch flag
|
|
116
|
+
*/
|
|
117
|
+
export function highlightMatches(text, matchedIndices) {
|
|
118
|
+
if (!matchedIndices.length) {
|
|
119
|
+
return [{ text, isMatch: false }];
|
|
120
|
+
}
|
|
121
|
+
const matchSet = new Set(matchedIndices);
|
|
122
|
+
const segments = [];
|
|
123
|
+
let currentSegment = '';
|
|
124
|
+
let currentIsMatch = matchSet.has(0);
|
|
125
|
+
for (let i = 0; i < text.length; i++) {
|
|
126
|
+
const isMatch = matchSet.has(i);
|
|
127
|
+
if (isMatch === currentIsMatch) {
|
|
128
|
+
currentSegment += text[i];
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
if (currentSegment) {
|
|
132
|
+
segments.push({ text: currentSegment, isMatch: currentIsMatch });
|
|
133
|
+
}
|
|
134
|
+
currentSegment = text[i];
|
|
135
|
+
currentIsMatch = isMatch;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (currentSegment) {
|
|
139
|
+
segments.push({ text: currentSegment, isMatch: currentIsMatch });
|
|
140
|
+
}
|
|
141
|
+
return segments;
|
|
142
|
+
}
|
package/package.json
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
2
|
+
"name": "@shotleybuilder/svelte-table-kit",
|
|
3
|
+
"version": "0.10.1",
|
|
4
|
+
"description": "A comprehensive, AI-configurable data table component for Svelte and SvelteKit, built on TanStack Table v8",
|
|
5
|
+
"author": "Sertantai",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"svelte",
|
|
9
|
+
"sveltekit",
|
|
10
|
+
"table",
|
|
11
|
+
"datagrid",
|
|
12
|
+
"tanstack",
|
|
13
|
+
"data-table",
|
|
14
|
+
"airtable",
|
|
15
|
+
"headless",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/shotleybuilder/svelte-table-kit.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/shotleybuilder/svelte-table-kit/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/shotleybuilder/svelte-table-kit#readme",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"svelte": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"svelte": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./styles": "./dist/styles/table-kit.css"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"!dist/**/*.test.*",
|
|
39
|
+
"!dist/**/*.spec.*"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"dev": "vite dev",
|
|
43
|
+
"build": "vite build && npm run package",
|
|
44
|
+
"package": "svelte-kit sync && svelte-package && publint",
|
|
45
|
+
"prepublishOnly": "npm run package",
|
|
46
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
47
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
48
|
+
"test": "vitest",
|
|
49
|
+
"test:ui": "vitest --ui",
|
|
50
|
+
"test:run": "vitest run",
|
|
51
|
+
"lint": "prettier --check . && eslint .",
|
|
52
|
+
"format": "prettier --write ."
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"svelte": "^4.0.0 || ^5.0.0",
|
|
56
|
+
"@tanstack/svelte-table": "^8.0.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@sveltejs/adapter-auto": "^3.0.0",
|
|
60
|
+
"@sveltejs/kit": "^2.0.0",
|
|
61
|
+
"@sveltejs/package": "^2.0.0",
|
|
62
|
+
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
|
63
|
+
"@tanstack/svelte-table": "^8.21.3",
|
|
64
|
+
"@types/eslint": "^9.6.0",
|
|
65
|
+
"eslint": "^9.0.0",
|
|
66
|
+
"eslint-config-prettier": "^9.1.0",
|
|
67
|
+
"eslint-plugin-svelte": "^2.36.0",
|
|
68
|
+
"prettier": "^3.1.1",
|
|
69
|
+
"prettier-plugin-svelte": "^3.1.2",
|
|
70
|
+
"publint": "^0.1.9",
|
|
71
|
+
"svelte": "^4.2.7",
|
|
72
|
+
"svelte-check": "^3.6.0",
|
|
73
|
+
"tslib": "^2.4.1",
|
|
74
|
+
"typescript": "^5.0.0",
|
|
75
|
+
"vite": "^5.0.11",
|
|
76
|
+
"vitest": "^2.0.0"
|
|
77
|
+
}
|
|
78
78
|
}
|