@shotleybuilder/svelte-table-kit 0.1.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.
@@ -0,0 +1,31 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type ColumnDef } from '@tanstack/svelte-table';
3
+ import type { TableKitProps } from './types';
4
+ declare class __sveltets_Render<T> {
5
+ props(): {
6
+ data?: T[] | undefined;
7
+ columns?: ColumnDef<T>[] | undefined;
8
+ storageKey?: TableKitProps<T_1>["storageKey"];
9
+ persistState?: TableKitProps<T_1>["persistState"];
10
+ features?: TableKitProps<T_1>["features"];
11
+ onRowClick?: ((row: T) => void) | undefined;
12
+ onRowSelect?: ((rows: T[]) => void) | undefined;
13
+ onStateChange?: TableKitProps<T_1>["onStateChange"];
14
+ };
15
+ events(): {} & {
16
+ [evt: string]: CustomEvent<any>;
17
+ };
18
+ slots(): {
19
+ empty: {};
20
+ cell: {
21
+ cell: import("@tanstack/table-core").Cell<T, unknown>;
22
+ column: string;
23
+ };
24
+ };
25
+ }
26
+ export type TableKitProps_<T> = ReturnType<__sveltets_Render<T>['props']>;
27
+ export type TableKitEvents<T> = ReturnType<__sveltets_Render<T>['events']>;
28
+ export type TableKitSlots<T> = ReturnType<__sveltets_Render<T>['slots']>;
29
+ export default class TableKit<T> extends SvelteComponent<TableKitProps_<T>, TableKitEvents<T>, TableKitSlots<T>> {
30
+ }
31
+ export {};
@@ -0,0 +1,291 @@
1
+ <script>import FilterConditionComponent from "./FilterCondition.svelte";
2
+ export let columns;
3
+ export let conditions = [];
4
+ export let onConditionsChange;
5
+ export let logic = "and";
6
+ export let onLogicChange;
7
+ let isExpanded = false;
8
+ function generateId() {
9
+ return `filter-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
10
+ }
11
+ function addCondition() {
12
+ const newCondition = {
13
+ id: generateId(),
14
+ field: "",
15
+ operator: "equals",
16
+ value: ""
17
+ };
18
+ onConditionsChange([...conditions, newCondition]);
19
+ isExpanded = true;
20
+ }
21
+ function updateCondition(index, updated) {
22
+ const newConditions = [...conditions];
23
+ newConditions[index] = updated;
24
+ onConditionsChange(newConditions);
25
+ }
26
+ function removeCondition(index) {
27
+ const newConditions = conditions.filter((_, i) => i !== index);
28
+ onConditionsChange(newConditions);
29
+ if (newConditions.length === 0) {
30
+ isExpanded = false;
31
+ }
32
+ }
33
+ function clearAllConditions() {
34
+ onConditionsChange([]);
35
+ isExpanded = false;
36
+ }
37
+ $: hasConditions = conditions.length > 0;
38
+ $: filterCount = conditions.filter(
39
+ (c) => c.field && (c.operator === "is_empty" || c.operator === "is_not_empty" || c.value !== null && c.value !== void 0 && c.value !== "")
40
+ ).length;
41
+ </script>
42
+
43
+ <div class="filter-bar">
44
+ <!-- Compact Filter Button -->
45
+ <button class="filter-toggle-btn" on:click={() => (isExpanded = !isExpanded)}>
46
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
47
+ <path
48
+ stroke-linecap="round"
49
+ stroke-linejoin="round"
50
+ stroke-width="2"
51
+ d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
52
+ />
53
+ </svg>
54
+ Filter
55
+ {#if filterCount > 0}
56
+ <span class="filter-badge">{filterCount}</span>
57
+ {/if}
58
+ <svg class="chevron" class:expanded={isExpanded} fill="none" stroke="currentColor" viewBox="0 0 24 24">
59
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
60
+ </svg>
61
+ </button>
62
+
63
+ <!-- Expandable Filter Panel -->
64
+ {#if isExpanded}
65
+ <div class="filter-panel">
66
+ {#if hasConditions}
67
+ <div class="filter-header">
68
+ {#if hasConditions}
69
+ <button class="clear-all-btn" on:click={clearAllConditions}> Clear all </button>
70
+ {/if}
71
+ </div>
72
+
73
+ <div class="filter-conditions">
74
+ {#each conditions as condition, index (condition.id)}
75
+ <div class="condition-row">
76
+ {#if index === 0}
77
+ <span class="filter-label">Where</span>
78
+ {:else}
79
+ <select
80
+ class="logic-select"
81
+ value={logic}
82
+ on:change={(e) => {
83
+ const newLogic = e.currentTarget.value;
84
+ onLogicChange(newLogic === 'or' ? 'or' : 'and');
85
+ }}
86
+ >
87
+ <option value="and">and</option>
88
+ <option value="or">or</option>
89
+ </select>
90
+ {/if}
91
+ <div class="condition-wrapper">
92
+ <FilterConditionComponent
93
+ {condition}
94
+ {columns}
95
+ onUpdate={(updated) => updateCondition(index, updated)}
96
+ onRemove={() => removeCondition(index)}
97
+ />
98
+ </div>
99
+ </div>
100
+ {/each}
101
+ </div>
102
+ {/if}
103
+
104
+ <button class="add-condition-btn" on:click={addCondition}>
105
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
106
+ <path
107
+ stroke-linecap="round"
108
+ stroke-linejoin="round"
109
+ stroke-width="2"
110
+ d="M12 6v6m0 0v6m0-6h6m-6 0H6"
111
+ />
112
+ </svg>
113
+ Add condition
114
+ </button>
115
+ </div>
116
+ {/if}
117
+ </div>
118
+
119
+ <style>
120
+ .filter-bar {
121
+ position: relative;
122
+ }
123
+
124
+ /* Compact Filter Toggle Button */
125
+ .filter-toggle-btn {
126
+ display: inline-flex;
127
+ align-items: center;
128
+ gap: 0.5rem;
129
+ padding: 0.5rem 1rem;
130
+ font-size: 0.875rem;
131
+ font-weight: 500;
132
+ color: #374151;
133
+ background: white;
134
+ border: 1px solid #d1d5db;
135
+ border-radius: 0.375rem;
136
+ cursor: pointer;
137
+ transition: all 0.2s;
138
+ }
139
+
140
+ .filter-toggle-btn:hover {
141
+ background: #f9fafb;
142
+ border-color: #9ca3af;
143
+ }
144
+
145
+ .filter-badge {
146
+ display: inline-flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ min-width: 1.25rem;
150
+ height: 1.25rem;
151
+ padding: 0 0.375rem;
152
+ font-size: 0.75rem;
153
+ font-weight: 600;
154
+ color: white;
155
+ background: #4f46e5;
156
+ border-radius: 0.75rem;
157
+ }
158
+
159
+ .chevron {
160
+ width: 1rem;
161
+ height: 1rem;
162
+ transition: transform 0.2s;
163
+ }
164
+
165
+ .chevron.expanded {
166
+ transform: rotate(180deg);
167
+ }
168
+
169
+ /* Expandable Filter Panel */
170
+ .filter-panel {
171
+ position: absolute;
172
+ top: calc(100% + 0.5rem);
173
+ left: 0;
174
+ z-index: 20;
175
+ min-width: 600px;
176
+ padding: 1rem;
177
+ background: white;
178
+ border: 1px solid #e5e7eb;
179
+ border-radius: 0.5rem;
180
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
181
+ }
182
+
183
+ .filter-header {
184
+ display: flex;
185
+ justify-content: flex-end;
186
+ align-items: center;
187
+ margin-bottom: 0.75rem;
188
+ }
189
+
190
+ .filter-label {
191
+ flex-shrink: 0;
192
+ display: inline-flex;
193
+ align-items: center;
194
+ min-width: 60px;
195
+ padding: 0.375rem 0.5rem;
196
+ font-size: 0.75rem;
197
+ font-weight: 600;
198
+ color: #374151;
199
+ text-align: left;
200
+ height: fit-content;
201
+ margin-top: 0.5rem;
202
+ box-sizing: border-box;
203
+ }
204
+
205
+ .clear-all-btn {
206
+ font-size: 0.75rem;
207
+ color: #6b7280;
208
+ background: none;
209
+ border: none;
210
+ cursor: pointer;
211
+ padding: 0.25rem 0.5rem;
212
+ border-radius: 0.25rem;
213
+ transition: all 0.2s;
214
+ }
215
+
216
+ .clear-all-btn:hover {
217
+ color: #dc2626;
218
+ background: #fee2e2;
219
+ }
220
+
221
+ .filter-conditions {
222
+ display: flex;
223
+ flex-direction: column;
224
+ gap: 0.5rem;
225
+ margin-bottom: 0.75rem;
226
+ }
227
+
228
+ .condition-row {
229
+ display: flex;
230
+ align-items: flex-start;
231
+ gap: 0.5rem;
232
+ }
233
+
234
+ .logic-select {
235
+ flex-shrink: 0;
236
+ padding: 0.375rem 0.5rem;
237
+ font-size: 0.75rem;
238
+ font-weight: 600;
239
+ color: #6b7280;
240
+ text-transform: lowercase;
241
+ background: white;
242
+ border: 1px solid #d1d5db;
243
+ border-radius: 0.375rem;
244
+ cursor: pointer;
245
+ min-width: 60px;
246
+ height: fit-content;
247
+ margin-top: 0.5rem;
248
+ }
249
+
250
+ .logic-select:hover {
251
+ background: #f9fafb;
252
+ border-color: #9ca3af;
253
+ }
254
+
255
+ .logic-select:focus {
256
+ outline: none;
257
+ border-color: #4f46e5;
258
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
259
+ }
260
+
261
+ .condition-wrapper {
262
+ flex: 1;
263
+ display: flex;
264
+ flex-direction: column;
265
+ }
266
+
267
+ .add-condition-btn {
268
+ display: inline-flex;
269
+ align-items: center;
270
+ gap: 0.5rem;
271
+ padding: 0.5rem 0.75rem;
272
+ font-size: 0.875rem;
273
+ font-weight: 500;
274
+ color: #4f46e5;
275
+ background: white;
276
+ border: 1px dashed #4f46e5;
277
+ border-radius: 0.375rem;
278
+ cursor: pointer;
279
+ transition: all 0.2s;
280
+ }
281
+
282
+ .add-condition-btn:hover {
283
+ background: #eef2ff;
284
+ border-style: solid;
285
+ }
286
+
287
+ .icon {
288
+ width: 1rem;
289
+ height: 1rem;
290
+ }
291
+ </style>
@@ -0,0 +1,24 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { ColumnDef } from '@tanstack/svelte-table';
3
+ import type { FilterCondition, FilterLogic } from '../types';
4
+ declare const __propDef: {
5
+ props: {
6
+ columns: ColumnDef<any>[];
7
+ conditions?: FilterCondition[];
8
+ onConditionsChange: (conditions: FilterCondition[]) => void;
9
+ logic?: FilterLogic;
10
+ onLogicChange: (logic: FilterLogic) => void;
11
+ };
12
+ events: {
13
+ [evt: string]: CustomEvent<any>;
14
+ };
15
+ slots: {};
16
+ exports?: {} | undefined;
17
+ bindings?: string | undefined;
18
+ };
19
+ export type FilterBarProps = typeof __propDef.props;
20
+ export type FilterBarEvents = typeof __propDef.events;
21
+ export type FilterBarSlots = typeof __propDef.slots;
22
+ export default class FilterBar extends SvelteComponent<FilterBarProps, FilterBarEvents, FilterBarSlots> {
23
+ }
24
+ export {};
@@ -0,0 +1,135 @@
1
+ <script>export let condition;
2
+ export let columns;
3
+ export let onUpdate;
4
+ export let onRemove;
5
+ const operatorOptions = [
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
+ ];
19
+ function handleFieldChange(event) {
20
+ const field = event.target.value;
21
+ onUpdate({ ...condition, field });
22
+ }
23
+ function handleOperatorChange(event) {
24
+ const operator = event.target.value;
25
+ onUpdate({ ...condition, operator });
26
+ }
27
+ function handleValueChange(event) {
28
+ const value = event.target.value;
29
+ onUpdate({ ...condition, value });
30
+ }
31
+ $: valueDisabled = condition.operator === "is_empty" || condition.operator === "is_not_empty";
32
+ </script>
33
+
34
+ <div class="filter-condition">
35
+ <select class="field-select" value={condition.field} on:change={handleFieldChange}>
36
+ <option value="">Select field...</option>
37
+ {#each columns as column}
38
+ {@const columnId = column.accessorKey || column.id}
39
+ {#if columnId}
40
+ <option value={columnId}>
41
+ {column.header || columnId}
42
+ </option>
43
+ {/if}
44
+ {/each}
45
+ </select>
46
+
47
+ <select class="operator-select" value={condition.operator} on:change={handleOperatorChange}>
48
+ {#each operatorOptions as option}
49
+ <option value={option.value}>{option.label}</option>
50
+ {/each}
51
+ </select>
52
+
53
+ <input
54
+ type="text"
55
+ class="value-input"
56
+ value={condition.value || ''}
57
+ on:input={handleValueChange}
58
+ disabled={valueDisabled}
59
+ placeholder={valueDisabled ? 'N/A' : 'Enter value...'}
60
+ />
61
+
62
+ <button class="remove-btn" on:click={onRemove} title="Remove condition">
63
+ <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
64
+ <path
65
+ stroke-linecap="round"
66
+ stroke-linejoin="round"
67
+ stroke-width="2"
68
+ d="M6 18L18 6M6 6l12 12"
69
+ />
70
+ </svg>
71
+ </button>
72
+ </div>
73
+
74
+ <style>
75
+ .filter-condition {
76
+ display: flex;
77
+ gap: 0.5rem;
78
+ align-items: center;
79
+ padding: 0.5rem;
80
+ background: #f9fafb;
81
+ border-radius: 0.375rem;
82
+ }
83
+
84
+ .field-select,
85
+ .operator-select,
86
+ .value-input {
87
+ padding: 0.375rem 0.75rem;
88
+ font-size: 0.875rem;
89
+ border: 1px solid #d1d5db;
90
+ border-radius: 0.375rem;
91
+ background: white;
92
+ }
93
+
94
+ .field-select {
95
+ flex: 1;
96
+ min-width: 120px;
97
+ }
98
+
99
+ .operator-select {
100
+ flex: 0.8;
101
+ min-width: 100px;
102
+ }
103
+
104
+ .value-input {
105
+ flex: 1;
106
+ min-width: 120px;
107
+ }
108
+
109
+ .value-input:disabled {
110
+ background: #f3f4f6;
111
+ color: #9ca3af;
112
+ cursor: not-allowed;
113
+ }
114
+
115
+ .remove-btn {
116
+ flex-shrink: 0;
117
+ padding: 0.375rem;
118
+ background: none;
119
+ border: none;
120
+ color: #6b7280;
121
+ cursor: pointer;
122
+ border-radius: 0.25rem;
123
+ transition: all 0.2s;
124
+ }
125
+
126
+ .remove-btn:hover {
127
+ background: #fee2e2;
128
+ color: #dc2626;
129
+ }
130
+
131
+ .icon {
132
+ width: 1rem;
133
+ height: 1rem;
134
+ }
135
+ </style>
@@ -0,0 +1,23 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { ColumnDef } from '@tanstack/svelte-table';
3
+ import type { FilterCondition } from '../types';
4
+ declare const __propDef: {
5
+ props: {
6
+ condition: FilterCondition;
7
+ columns: ColumnDef<any>[];
8
+ onUpdate: (condition: FilterCondition) => void;
9
+ onRemove: () => void;
10
+ };
11
+ events: {
12
+ [evt: string]: CustomEvent<any>;
13
+ };
14
+ slots: {};
15
+ exports?: {} | undefined;
16
+ bindings?: string | undefined;
17
+ };
18
+ export type FilterConditionProps = typeof __propDef.props;
19
+ export type FilterConditionEvents = typeof __propDef.events;
20
+ export type FilterConditionSlots = typeof __propDef.slots;
21
+ export default class FilterCondition extends SvelteComponent<FilterConditionProps, FilterConditionEvents, FilterConditionSlots> {
22
+ }
23
+ export {};