@shotleybuilder/svelte-gridlite-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.
Files changed (43) hide show
  1. package/README.md +260 -0
  2. package/dist/GridLite.svelte +1361 -0
  3. package/dist/GridLite.svelte.d.ts +42 -0
  4. package/dist/components/CellContextMenu.svelte +209 -0
  5. package/dist/components/CellContextMenu.svelte.d.ts +28 -0
  6. package/dist/components/ColumnMenu.svelte +234 -0
  7. package/dist/components/ColumnMenu.svelte.d.ts +29 -0
  8. package/dist/components/ColumnPicker.svelte +403 -0
  9. package/dist/components/ColumnPicker.svelte.d.ts +29 -0
  10. package/dist/components/FilterBar.svelte +390 -0
  11. package/dist/components/FilterBar.svelte.d.ts +38 -0
  12. package/dist/components/FilterCondition.svelte +643 -0
  13. package/dist/components/FilterCondition.svelte.d.ts +35 -0
  14. package/dist/components/GroupBar.svelte +463 -0
  15. package/dist/components/GroupBar.svelte.d.ts +33 -0
  16. package/dist/components/RowDetailModal.svelte +213 -0
  17. package/dist/components/RowDetailModal.svelte.d.ts +25 -0
  18. package/dist/components/SortBar.svelte +232 -0
  19. package/dist/components/SortBar.svelte.d.ts +30 -0
  20. package/dist/components/SortCondition.svelte +129 -0
  21. package/dist/components/SortCondition.svelte.d.ts +30 -0
  22. package/dist/index.d.ts +23 -0
  23. package/dist/index.js +29 -0
  24. package/dist/query/builder.d.ts +160 -0
  25. package/dist/query/builder.js +432 -0
  26. package/dist/query/live.d.ts +50 -0
  27. package/dist/query/live.js +118 -0
  28. package/dist/query/schema.d.ts +30 -0
  29. package/dist/query/schema.js +75 -0
  30. package/dist/state/migrations.d.ts +29 -0
  31. package/dist/state/migrations.js +113 -0
  32. package/dist/state/views.d.ts +54 -0
  33. package/dist/state/views.js +130 -0
  34. package/dist/styles/gridlite.css +966 -0
  35. package/dist/types.d.ts +164 -0
  36. package/dist/types.js +2 -0
  37. package/dist/utils/filters.d.ts +14 -0
  38. package/dist/utils/filters.js +49 -0
  39. package/dist/utils/formatters.d.ts +16 -0
  40. package/dist/utils/formatters.js +39 -0
  41. package/dist/utils/fuzzy.d.ts +47 -0
  42. package/dist/utils/fuzzy.js +142 -0
  43. package/package.json +76 -0
@@ -0,0 +1,403 @@
1
+ <script>import { tick } from "svelte";
2
+ export let columns;
3
+ export let columnConfigs = [];
4
+ export let columnVisibility = {};
5
+ export let columnOrder = [];
6
+ export let isOpen;
7
+ export let defaultVisibleColumns = void 0;
8
+ export let onVisibilityChange;
9
+ export let onToggleAll;
10
+ export let onOrderChange = void 0;
11
+ let searchQuery = "";
12
+ let searchInput;
13
+ let selectedColumns = /* @__PURE__ */ new Set();
14
+ let lastClickedColumn = null;
15
+ let draggedColumns = [];
16
+ let dropTargetColumn = null;
17
+ let dropPosition = null;
18
+ let dropTargetSection = null;
19
+ let focusedIndex = -1;
20
+ let listContainer;
21
+ $: if (isOpen) {
22
+ tick().then(() => {
23
+ searchInput?.focus();
24
+ });
25
+ } else {
26
+ searchQuery = "";
27
+ selectedColumns = /* @__PURE__ */ new Set();
28
+ lastClickedColumn = null;
29
+ focusedIndex = -1;
30
+ }
31
+ function getLabel(col) {
32
+ const cfg = columnConfigs.find((c) => c.name === col.name);
33
+ return cfg?.label ?? col.name;
34
+ }
35
+ function isVisible(columnName, vis) {
36
+ if (columnName in vis) {
37
+ return vis[columnName];
38
+ }
39
+ if (defaultVisibleColumns) {
40
+ return defaultVisibleColumns.includes(columnName);
41
+ }
42
+ return true;
43
+ }
44
+ function ordered(cols, order) {
45
+ if (order.length === 0) return cols;
46
+ return [...cols].sort((a, b) => {
47
+ const ai = order.indexOf(a.name);
48
+ const bi = order.indexOf(b.name);
49
+ if (ai === -1 && bi === -1) return 0;
50
+ if (ai === -1) return 1;
51
+ if (bi === -1) return -1;
52
+ return ai - bi;
53
+ });
54
+ }
55
+ $: filteredColumns = columns.filter((col) => {
56
+ if (!searchQuery) return true;
57
+ const q = searchQuery.toLowerCase();
58
+ const label = getLabel(col).toLowerCase();
59
+ const name = col.name.toLowerCase();
60
+ return label.includes(q) || name.includes(q);
61
+ });
62
+ $: visibleCols = ordered(filteredColumns.filter((c) => isVisible(c.name, columnVisibility)), columnOrder);
63
+ $: hiddenCols = ordered(filteredColumns.filter((c) => !isVisible(c.name, columnVisibility)), columnOrder);
64
+ $: allItems = [...visibleCols, ...hiddenCols];
65
+ $: totalVisible = columns.filter((c) => isVisible(c.name, columnVisibility)).length;
66
+ $: totalHidden = columns.length - totalVisible;
67
+ function handleItemClick(event, colName) {
68
+ const isMeta = event.metaKey || event.ctrlKey;
69
+ const isShift = event.shiftKey;
70
+ if (isShift && lastClickedColumn) {
71
+ const lastIdx = allItems.findIndex((c) => c.name === lastClickedColumn);
72
+ const curIdx = allItems.findIndex((c) => c.name === colName);
73
+ if (lastIdx !== -1 && curIdx !== -1) {
74
+ const start = Math.min(lastIdx, curIdx);
75
+ const end = Math.max(lastIdx, curIdx);
76
+ const next = new Set(selectedColumns);
77
+ for (let i = start; i <= end; i++) {
78
+ next.add(allItems[i].name);
79
+ }
80
+ selectedColumns = next;
81
+ }
82
+ } else if (isMeta) {
83
+ const next = new Set(selectedColumns);
84
+ if (next.has(colName)) {
85
+ next.delete(colName);
86
+ } else {
87
+ next.add(colName);
88
+ }
89
+ selectedColumns = next;
90
+ } else {
91
+ selectedColumns = /* @__PURE__ */ new Set([colName]);
92
+ }
93
+ lastClickedColumn = colName;
94
+ }
95
+ function handleDragStart(event, colName) {
96
+ if (!event.dataTransfer) return;
97
+ if (selectedColumns.has(colName) && selectedColumns.size > 1) {
98
+ draggedColumns = allItems.filter((c) => selectedColumns.has(c.name)).map((c) => c.name);
99
+ } else {
100
+ draggedColumns = [colName];
101
+ selectedColumns = /* @__PURE__ */ new Set([colName]);
102
+ }
103
+ event.dataTransfer.effectAllowed = "move";
104
+ event.dataTransfer.setData("text/plain", draggedColumns.join(","));
105
+ if (draggedColumns.length > 1) {
106
+ const ghost = document.createElement("div");
107
+ ghost.className = "gridlite-column-picker-drag-ghost";
108
+ ghost.textContent = `${draggedColumns.length} columns`;
109
+ document.body.appendChild(ghost);
110
+ event.dataTransfer.setDragImage(ghost, 0, 0);
111
+ requestAnimationFrame(() => ghost.remove());
112
+ }
113
+ }
114
+ function handleDragOver(event, colName, section) {
115
+ if (draggedColumns.length === 0) return;
116
+ event.preventDefault();
117
+ if (event.dataTransfer) {
118
+ event.dataTransfer.dropEffect = "move";
119
+ }
120
+ const target = event.currentTarget;
121
+ const rect = target.getBoundingClientRect();
122
+ const midY = rect.top + rect.height / 2;
123
+ dropPosition = event.clientY < midY ? "before" : "after";
124
+ dropTargetColumn = colName;
125
+ dropTargetSection = section;
126
+ }
127
+ function handleDragOverSection(event, section) {
128
+ if (draggedColumns.length === 0) return;
129
+ event.preventDefault();
130
+ if (event.dataTransfer) {
131
+ event.dataTransfer.dropEffect = "move";
132
+ }
133
+ dropTargetSection = section;
134
+ }
135
+ function handleDrop(event) {
136
+ event.preventDefault();
137
+ if (draggedColumns.length === 0) return;
138
+ const targetSection = dropTargetSection;
139
+ const targetCol = dropTargetColumn;
140
+ const pos = dropPosition;
141
+ if (targetSection) {
142
+ for (const colName of draggedColumns) {
143
+ const currentlyVisible = isVisible(colName, columnVisibility);
144
+ if (targetSection === "visible" && !currentlyVisible) {
145
+ onVisibilityChange(colName, true);
146
+ } else if (targetSection === "hidden" && currentlyVisible) {
147
+ onVisibilityChange(colName, false);
148
+ }
149
+ }
150
+ }
151
+ if (targetSection === "visible" && onOrderChange) {
152
+ let currentOrder = columnOrder.length > 0 ? [...columnOrder] : columns.map((c) => c.name);
153
+ const remaining = currentOrder.filter((name) => !draggedColumns.includes(name));
154
+ if (targetCol && pos) {
155
+ const insertIdx = remaining.indexOf(targetCol);
156
+ if (insertIdx !== -1) {
157
+ const finalIdx = pos === "after" ? insertIdx + 1 : insertIdx;
158
+ remaining.splice(finalIdx, 0, ...draggedColumns);
159
+ } else {
160
+ remaining.push(...draggedColumns);
161
+ }
162
+ } else {
163
+ remaining.push(...draggedColumns);
164
+ }
165
+ onOrderChange(remaining);
166
+ }
167
+ draggedColumns = [];
168
+ dropTargetColumn = null;
169
+ dropPosition = null;
170
+ dropTargetSection = null;
171
+ }
172
+ function handleDragEnd() {
173
+ draggedColumns = [];
174
+ dropTargetColumn = null;
175
+ dropPosition = null;
176
+ dropTargetSection = null;
177
+ }
178
+ function handleDragLeave(event) {
179
+ const related = event.relatedTarget;
180
+ if (related && event.currentTarget.contains(related)) return;
181
+ dropTargetColumn = null;
182
+ dropPosition = null;
183
+ }
184
+ function handleKeydown(event) {
185
+ if (!isOpen) return;
186
+ switch (event.key) {
187
+ case "ArrowDown":
188
+ event.preventDefault();
189
+ focusedIndex = Math.min(focusedIndex + 1, allItems.length - 1);
190
+ if (focusedIndex >= 0) {
191
+ selectedColumns = /* @__PURE__ */ new Set([allItems[focusedIndex].name]);
192
+ lastClickedColumn = allItems[focusedIndex].name;
193
+ }
194
+ scrollToFocused();
195
+ break;
196
+ case "ArrowUp":
197
+ event.preventDefault();
198
+ focusedIndex = Math.max(focusedIndex - 1, 0);
199
+ if (focusedIndex >= 0 && allItems.length > 0) {
200
+ selectedColumns = /* @__PURE__ */ new Set([allItems[focusedIndex].name]);
201
+ lastClickedColumn = allItems[focusedIndex].name;
202
+ }
203
+ scrollToFocused();
204
+ break;
205
+ case " ":
206
+ event.preventDefault();
207
+ if (focusedIndex >= 0 && focusedIndex < allItems.length) {
208
+ const col = allItems[focusedIndex];
209
+ const vis = isVisible(col.name, columnVisibility);
210
+ onVisibilityChange(col.name, !vis);
211
+ }
212
+ break;
213
+ case "a":
214
+ if (event.ctrlKey || event.metaKey) {
215
+ event.preventDefault();
216
+ selectedColumns = new Set(allItems.map((c) => c.name));
217
+ }
218
+ break;
219
+ case "Escape":
220
+ event.preventDefault();
221
+ if (selectedColumns.size > 0) {
222
+ selectedColumns = /* @__PURE__ */ new Set();
223
+ focusedIndex = -1;
224
+ }
225
+ break;
226
+ }
227
+ }
228
+ function scrollToFocused() {
229
+ tick().then(() => {
230
+ const items = listContainer?.querySelectorAll(".gridlite-column-picker-item");
231
+ if (items && focusedIndex >= 0 && focusedIndex < items.length) {
232
+ items[focusedIndex].scrollIntoView({ block: "nearest" });
233
+ }
234
+ });
235
+ }
236
+ </script>
237
+
238
+ {#if isOpen}
239
+ <!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
240
+ <div
241
+ class="gridlite-column-picker"
242
+ on:click|stopPropagation
243
+ on:keydown={handleKeydown}
244
+ on:drop={handleDrop}
245
+ on:dragend={handleDragEnd}
246
+ role="listbox"
247
+ aria-label="Column picker"
248
+ tabindex="-1"
249
+ >
250
+ <!-- Search -->
251
+ <div class="gridlite-column-picker-search">
252
+ <svg class="gridlite-column-picker-search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
253
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
254
+ </svg>
255
+ <input
256
+ bind:this={searchInput}
257
+ class="gridlite-column-picker-search-input"
258
+ type="text"
259
+ placeholder="Find a column..."
260
+ bind:value={searchQuery}
261
+ />
262
+ {#if searchQuery}
263
+ <button
264
+ class="gridlite-column-picker-search-clear"
265
+ on:click={() => { searchQuery = ''; searchInput?.focus(); }}
266
+ type="button"
267
+ title="Clear search"
268
+ >
269
+ <svg width="12" height="12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
270
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
271
+ </svg>
272
+ </button>
273
+ {/if}
274
+ </div>
275
+
276
+ <!-- Actions -->
277
+ <div class="gridlite-column-picker-actions">
278
+ <button type="button" on:click={() => onToggleAll(true)}>Show All</button>
279
+ <button type="button" on:click={() => onToggleAll(false)}>Hide All</button>
280
+ {#if selectedColumns.size > 1}
281
+ <span class="gridlite-column-picker-selection-count">{selectedColumns.size} selected</span>
282
+ {/if}
283
+ </div>
284
+
285
+ <div class="gridlite-column-picker-list" bind:this={listContainer}>
286
+ <!-- Visible section -->
287
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
288
+ <div
289
+ class="gridlite-column-picker-section"
290
+ on:dragover={(e) => handleDragOverSection(e, 'visible')}
291
+ >
292
+ <div class="gridlite-column-picker-section-header">
293
+ <span>Visible</span>
294
+ <span class="gridlite-column-picker-section-count">{totalVisible}</span>
295
+ </div>
296
+ {#each visibleCols as col, i (col.name)}
297
+ {@const globalIdx = allItems.indexOf(col)}
298
+ {@const isSelected = selectedColumns.has(col.name)}
299
+ {@const isDragged = draggedColumns.includes(col.name)}
300
+ {@const isDropTarget = dropTargetColumn === col.name}
301
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
302
+ <div
303
+ class="gridlite-column-picker-item"
304
+ class:selected={isSelected}
305
+ class:dragged={isDragged}
306
+ class:drop-before={isDropTarget && dropPosition === 'before'}
307
+ class:drop-after={isDropTarget && dropPosition === 'after'}
308
+ class:focused={focusedIndex === globalIdx}
309
+ on:click={(e) => handleItemClick(e, col.name)}
310
+ on:dragover={(e) => handleDragOver(e, col.name, 'visible')}
311
+ on:dragleave={handleDragLeave}
312
+ role="option"
313
+ tabindex="-1"
314
+ aria-selected={isSelected}
315
+ >
316
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
317
+ <span
318
+ class="gridlite-column-picker-drag-handle"
319
+ draggable="true"
320
+ on:dragstart={(e) => handleDragStart(e, col.name)}
321
+ >
322
+ <svg width="10" height="14" viewBox="0 0 10 14" fill="currentColor">
323
+ <circle cx="3" cy="2" r="1.2" /><circle cx="7" cy="2" r="1.2" />
324
+ <circle cx="3" cy="7" r="1.2" /><circle cx="7" cy="7" r="1.2" />
325
+ <circle cx="3" cy="12" r="1.2" /><circle cx="7" cy="12" r="1.2" />
326
+ </svg>
327
+ </span>
328
+ <input
329
+ type="checkbox"
330
+ checked={true}
331
+ on:change|stopPropagation={() => onVisibilityChange(col.name, false)}
332
+ on:click|stopPropagation
333
+ />
334
+ <span class="gridlite-column-picker-item-label">{getLabel(col)}</span>
335
+ </div>
336
+ {/each}
337
+ {#if visibleCols.length === 0}
338
+ <div class="gridlite-column-picker-empty">
339
+ {searchQuery ? 'No matching visible columns' : 'No visible columns'}
340
+ </div>
341
+ {/if}
342
+ </div>
343
+
344
+ <!-- Hidden section -->
345
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
346
+ <div
347
+ class="gridlite-column-picker-section"
348
+ on:dragover={(e) => handleDragOverSection(e, 'hidden')}
349
+ >
350
+ <div class="gridlite-column-picker-section-header">
351
+ <span>Hidden</span>
352
+ <span class="gridlite-column-picker-section-count">{totalHidden}</span>
353
+ </div>
354
+ {#each hiddenCols as col (col.name)}
355
+ {@const globalIdx = allItems.indexOf(col)}
356
+ {@const isSelected = selectedColumns.has(col.name)}
357
+ {@const isDragged = draggedColumns.includes(col.name)}
358
+ {@const isDropTarget = dropTargetColumn === col.name}
359
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
360
+ <div
361
+ class="gridlite-column-picker-item gridlite-column-picker-item-hidden"
362
+ class:selected={isSelected}
363
+ class:dragged={isDragged}
364
+ class:drop-before={isDropTarget && dropPosition === 'before'}
365
+ class:drop-after={isDropTarget && dropPosition === 'after'}
366
+ class:focused={focusedIndex === globalIdx}
367
+ on:click={(e) => handleItemClick(e, col.name)}
368
+ on:dragover={(e) => handleDragOver(e, col.name, 'hidden')}
369
+ on:dragleave={handleDragLeave}
370
+ role="option"
371
+ tabindex="-1"
372
+ aria-selected={isSelected}
373
+ >
374
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
375
+ <span
376
+ class="gridlite-column-picker-drag-handle"
377
+ draggable="true"
378
+ on:dragstart={(e) => handleDragStart(e, col.name)}
379
+ >
380
+ <svg width="10" height="14" viewBox="0 0 10 14" fill="currentColor">
381
+ <circle cx="3" cy="2" r="1.2" /><circle cx="7" cy="2" r="1.2" />
382
+ <circle cx="3" cy="7" r="1.2" /><circle cx="7" cy="7" r="1.2" />
383
+ <circle cx="3" cy="12" r="1.2" /><circle cx="7" cy="12" r="1.2" />
384
+ </svg>
385
+ </span>
386
+ <input
387
+ type="checkbox"
388
+ checked={false}
389
+ on:change|stopPropagation={() => onVisibilityChange(col.name, true)}
390
+ on:click|stopPropagation
391
+ />
392
+ <span class="gridlite-column-picker-item-label">{getLabel(col)}</span>
393
+ </div>
394
+ {/each}
395
+ {#if hiddenCols.length === 0}
396
+ <div class="gridlite-column-picker-empty">
397
+ {searchQuery ? 'No matching hidden columns' : 'No hidden columns'}
398
+ </div>
399
+ {/if}
400
+ </div>
401
+ </div>
402
+ </div>
403
+ {/if}
@@ -0,0 +1,29 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { ColumnMetadata, ColumnConfig } from '../types.js';
3
+ declare const __propDef: {
4
+ props: {
5
+ columns: ColumnMetadata[];
6
+ columnConfigs?: ColumnConfig[];
7
+ columnVisibility?: Record<string, boolean>;
8
+ columnOrder?: string[];
9
+ isOpen: boolean;
10
+ defaultVisibleColumns?: string[] | undefined;
11
+ onVisibilityChange: (column: string, visible: boolean) => void;
12
+ onToggleAll: (show: boolean) => void;
13
+ onOrderChange?: ((newOrder: string[]) => void) | undefined;
14
+ };
15
+ events: {
16
+ click: PointerEvent;
17
+ } & {
18
+ [evt: string]: CustomEvent<any>;
19
+ };
20
+ slots: {};
21
+ exports?: {} | undefined;
22
+ bindings?: string | undefined;
23
+ };
24
+ export type ColumnPickerProps = typeof __propDef.props;
25
+ export type ColumnPickerEvents = typeof __propDef.events;
26
+ export type ColumnPickerSlots = typeof __propDef.slots;
27
+ export default class ColumnPicker extends SvelteComponent<ColumnPickerProps, ColumnPickerEvents, ColumnPickerSlots> {
28
+ }
29
+ export {};