@classic-homes/theme-svelte 1.25.0 → 1.26.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/dist/lib/components/AlertDialog.svelte +14 -6
- package/dist/lib/components/AlertDialog.svelte.d.ts +8 -2
- package/dist/lib/components/Badge.svelte +6 -2
- package/dist/lib/components/CodeField.svelte +24 -8
- package/dist/lib/components/CodeField.svelte.d.ts +8 -0
- package/dist/lib/components/Combobox.svelte +25 -111
- package/dist/lib/components/DataPanel.svelte +31 -24
- package/dist/lib/components/DataPanelHeader.svelte +66 -26
- package/dist/lib/components/DataTable.svelte +303 -138
- package/dist/lib/components/DataTable.svelte.d.ts +37 -13
- package/dist/lib/components/DateTimePicker.svelte +159 -34
- package/dist/lib/components/Dialog.svelte +14 -6
- package/dist/lib/components/Dialog.svelte.d.ts +8 -2
- package/dist/lib/components/FileUpload.svelte +23 -6
- package/dist/lib/components/FileUpload.svelte.d.ts +7 -2
- package/dist/lib/components/MultiSelect.svelte +28 -155
- package/dist/lib/components/NumberInput.svelte +28 -110
- package/dist/lib/components/OTPInput.svelte +74 -86
- package/dist/lib/components/Select.svelte +24 -29
- package/dist/lib/components/Signature.svelte +80 -48
- package/dist/lib/components/Signature.svelte.d.ts +6 -1
- package/dist/lib/components/SkipLink.svelte +7 -2
- package/dist/lib/components/SkipLink.svelte.d.ts +2 -0
- package/dist/lib/components/Slider.svelte +4 -4
- package/dist/lib/components/Toast.svelte +11 -1
- package/dist/lib/components/ToastContainer.svelte +38 -24
- package/dist/lib/components/Tooltip.svelte +19 -6
- package/dist/lib/components/Tooltip.svelte.d.ts +8 -2
- package/dist/lib/components/data-table/DataTableColumnFilter.svelte +3 -1
- package/dist/lib/components/data-table/DataTableEditableCell.svelte +25 -3
- package/dist/lib/components/data-table/DataTableFacetedFilter.svelte +50 -40
- package/dist/lib/components/data-table/DataTableFacetedFilter.svelte.d.ts +0 -10
- package/dist/lib/components/data-table/DataTableVirtual.svelte +109 -57
- package/dist/lib/components/data-table/DataTableVirtual.svelte.d.ts +2 -0
- package/dist/lib/components/data-table/createDataTable.svelte.d.ts +26 -4
- package/dist/lib/components/data-table/createDataTable.svelte.js +97 -40
- package/dist/lib/components/data-table/index.d.ts +1 -1
- package/dist/lib/components/data-table/types.d.ts +14 -4
- package/dist/lib/components/data-table/types.js +5 -1
- package/dist/lib/components/internal/TriggerChild.svelte +60 -0
- package/dist/lib/components/internal/TriggerChild.svelte.d.ts +32 -0
- package/dist/lib/components/layout/Sidebar.svelte +33 -3
- package/dist/lib/components/layout/Sidebar.svelte.d.ts +1 -1
- package/dist/lib/components/layout/sidebar/SidebarFlyout.svelte +6 -1
- package/dist/lib/components/layout/sidebar/SidebarNavItem.svelte +6 -1
- package/dist/lib/components/layout/sidebar/SidebarSection.svelte +6 -1
- package/dist/lib/components/media/Audio.svelte +16 -0
- package/dist/lib/components/media/Lightbox.svelte +29 -3
- package/dist/lib/components/media/MediaCarousel.svelte +19 -0
- package/dist/lib/components/media/MediaEmbed.svelte +32 -1
- package/dist/lib/components/media/Video.svelte +16 -0
- package/dist/lib/components/media/internal/MediaControls.svelte +9 -1
- package/dist/lib/components/notifications/NotificationBadge.svelte +1 -1
- package/dist/lib/components/notifications/NotificationItem.svelte +19 -28
- package/dist/lib/composables/useEscapeKey.svelte.d.ts +4 -0
- package/dist/lib/composables/useEscapeKey.svelte.js +15 -0
- package/dist/lib/composables/useHlsSource.svelte.js +31 -4
- package/dist/lib/composables/useMediaPlayer.svelte.js +38 -9
- package/dist/lib/composables/useMediaZoom.svelte.js +32 -0
- package/dist/lib/composables/usePersistedForm.svelte.d.ts +6 -2
- package/dist/lib/composables/usePersistedForm.svelte.js +7 -7
- package/dist/lib/composables/useReducedMotion.svelte.js +6 -1
- package/dist/lib/composables/useRovingTabindex.svelte.js +12 -0
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/stores/toast.svelte.d.ts +13 -0
- package/dist/lib/stores/toast.svelte.js +36 -5
- package/dist/lib/utils/media.d.ts +7 -0
- package/dist/lib/utils/media.js +18 -3
- package/package.json +1 -1
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* />
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
|
-
import type
|
|
33
|
+
import { untrack, type Snippet } from 'svelte';
|
|
34
34
|
import type { DataTableColumn } from '../types/components.js';
|
|
35
35
|
import { cn } from '../utils.js';
|
|
36
36
|
import { validateNonEmptyArray } from '../validation.js';
|
|
@@ -41,7 +41,12 @@
|
|
|
41
41
|
getColumnWidthStyle,
|
|
42
42
|
getAriaSortValue,
|
|
43
43
|
} from './data-table/utils.js';
|
|
44
|
-
import type { PaginationState } from './data-table/types.js';
|
|
44
|
+
import type { ColumnFiltersState, PaginationState } from './data-table/types.js';
|
|
45
|
+
import DataTableColumnFilter from './data-table/DataTableColumnFilter.svelte';
|
|
46
|
+
import DataTableColumnVisibility from './data-table/DataTableColumnVisibility.svelte';
|
|
47
|
+
import DataTableEditableCell from './data-table/DataTableEditableCell.svelte';
|
|
48
|
+
import DataTableFacetedFilter from './data-table/DataTableFacetedFilter.svelte';
|
|
49
|
+
import DataTableVirtual from './data-table/DataTableVirtual.svelte';
|
|
45
50
|
import Skeleton from './Skeleton.svelte';
|
|
46
51
|
import Checkbox from './Checkbox.svelte';
|
|
47
52
|
|
|
@@ -69,8 +74,11 @@
|
|
|
69
74
|
sortColumn?: string;
|
|
70
75
|
/** Current sort direction */
|
|
71
76
|
sortDirection?: 'asc' | 'desc';
|
|
72
|
-
/**
|
|
73
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Callback when sort changes - when provided, enables server-side sorting.
|
|
79
|
+
* Called with `column: null` when the sort is cleared.
|
|
80
|
+
*/
|
|
81
|
+
onSort?: (column: string | null, direction: 'asc' | 'desc') => void;
|
|
74
82
|
|
|
75
83
|
// === Interaction Props ===
|
|
76
84
|
/** Callback when row is clicked */
|
|
@@ -79,7 +87,10 @@
|
|
|
79
87
|
// === Selection Props ===
|
|
80
88
|
/** Enable row selection - can be boolean or function for per-row control */
|
|
81
89
|
enableRowSelection?: boolean | ((row: T) => boolean);
|
|
82
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Allow more than one selected row (default: true). When false, selecting
|
|
92
|
+
* a row replaces the previous selection and there is no select-all box.
|
|
93
|
+
*/
|
|
83
94
|
enableMultiRowSelection?: boolean;
|
|
84
95
|
/** Controlled row selection state (Set of row IDs) */
|
|
85
96
|
rowSelection?: Set<string>;
|
|
@@ -89,7 +100,7 @@
|
|
|
89
100
|
getRowId?: (row: T, index: number) => string;
|
|
90
101
|
|
|
91
102
|
// === Visibility Props ===
|
|
92
|
-
/**
|
|
103
|
+
/** Show a "Columns" menu above the table for hiding and showing columns */
|
|
93
104
|
enableColumnVisibility?: boolean;
|
|
94
105
|
/** Controlled visibility state (Set of hidden column IDs) */
|
|
95
106
|
columnVisibility?: Set<string>;
|
|
@@ -97,18 +108,27 @@
|
|
|
97
108
|
onColumnVisibilityChange?: (hiddenColumns: Set<string>) => void;
|
|
98
109
|
|
|
99
110
|
// === Filtering Props ===
|
|
100
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Show a filter row under the header: a text filter per column, or a
|
|
113
|
+
* faceted (multi-select) filter for columns that define `filterOptions`
|
|
114
|
+
*/
|
|
101
115
|
enableFiltering?: boolean;
|
|
102
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Apply `globalFilter` to the rows. No search input is rendered; bind
|
|
118
|
+
* `globalFilter` to your own input.
|
|
119
|
+
*/
|
|
103
120
|
enableGlobalFilter?: boolean;
|
|
104
121
|
/** Controlled global filter value */
|
|
105
122
|
globalFilter?: string;
|
|
106
123
|
/** Callback when global filter changes */
|
|
107
124
|
onGlobalFilterChange?: (filter: string) => void;
|
|
108
|
-
/**
|
|
109
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Controlled column filters. A string value is a substring filter; a
|
|
127
|
+
* string[] value (what the faceted filter sets) matches those values exactly.
|
|
128
|
+
*/
|
|
129
|
+
columnFilters?: ColumnFiltersState;
|
|
110
130
|
/** Callback when column filters change */
|
|
111
|
-
onColumnFiltersChange?: (filters:
|
|
131
|
+
onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
|
|
112
132
|
/** Use server-side filtering (manual mode) */
|
|
113
133
|
manualFiltering?: boolean;
|
|
114
134
|
|
|
@@ -127,15 +147,24 @@
|
|
|
127
147
|
pageCount?: number;
|
|
128
148
|
|
|
129
149
|
// === Virtualization Props ===
|
|
130
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Render rows with DataTableVirtual (only the visible rows are in the DOM)
|
|
152
|
+
* for large datasets (1000+ rows recommended). Pagination, `row` snippets,
|
|
153
|
+
* `hideBelow`, and inline editing do not apply in this mode.
|
|
154
|
+
*/
|
|
131
155
|
enableVirtualization?: boolean;
|
|
156
|
+
/** Height of the scrolling body when virtualized (CSS value, default: 500px) */
|
|
157
|
+
virtualHeight?: string;
|
|
132
158
|
/** Height of each row in pixels for virtualization (default: 48) */
|
|
133
159
|
virtualRowHeight?: number;
|
|
134
160
|
/** Number of rows to render outside the visible viewport (default: 10) */
|
|
135
161
|
virtualOverscan?: number;
|
|
136
162
|
|
|
137
163
|
// === Editing Props ===
|
|
138
|
-
/**
|
|
164
|
+
/**
|
|
165
|
+
* Enable inline cell editing (DataTableEditableCell) for columns that set
|
|
166
|
+
* `enableEditing: true`. Requires `onCellEdit`.
|
|
167
|
+
*/
|
|
139
168
|
enableEditing?: boolean;
|
|
140
169
|
/** Callback when a cell is edited */
|
|
141
170
|
onCellEdit?: (rowId: string, columnId: string, value: unknown, row: T) => void;
|
|
@@ -177,7 +206,7 @@
|
|
|
177
206
|
enableGlobalFilter = false,
|
|
178
207
|
globalFilter = $bindable(''),
|
|
179
208
|
onGlobalFilterChange,
|
|
180
|
-
columnFilters = $bindable(new Map
|
|
209
|
+
columnFilters = $bindable(new Map() as ColumnFiltersState),
|
|
181
210
|
onColumnFiltersChange,
|
|
182
211
|
manualFiltering = false,
|
|
183
212
|
|
|
@@ -191,6 +220,7 @@
|
|
|
191
220
|
|
|
192
221
|
// Virtualization props
|
|
193
222
|
enableVirtualization = false,
|
|
223
|
+
virtualHeight = '500px',
|
|
194
224
|
virtualRowHeight = 48,
|
|
195
225
|
virtualOverscan = 10,
|
|
196
226
|
|
|
@@ -245,18 +275,22 @@
|
|
|
245
275
|
initialPageSize: pageSize,
|
|
246
276
|
initialPageIndex: pageIndex,
|
|
247
277
|
initialGlobalFilter: globalFilter,
|
|
278
|
+
initialHiddenColumns: [...columnVisibility],
|
|
279
|
+
initialSelectedRows: [...rowSelection],
|
|
280
|
+
initialColumnFilters: columnFilters,
|
|
281
|
+
enableMultiRowSelection,
|
|
248
282
|
manualSorting: !!onSort,
|
|
249
283
|
manualFiltering,
|
|
250
284
|
manualPagination,
|
|
251
285
|
// Without the pager every row must render, not just the first page.
|
|
252
|
-
|
|
286
|
+
// The virtual body windows the full row list itself.
|
|
287
|
+
enablePagination: enablePagination && !enableVirtualization,
|
|
253
288
|
pageCount,
|
|
254
289
|
onSortChange: (col, dir) => {
|
|
255
290
|
sortColumn = col ?? undefined;
|
|
256
291
|
sortDirection = dir;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
292
|
+
// A cleared sort is reported too, or a server would stay sorted
|
|
293
|
+
onSort?.(col, dir);
|
|
260
294
|
},
|
|
261
295
|
onGlobalFilterChange: (filter) => {
|
|
262
296
|
globalFilter = filter;
|
|
@@ -281,10 +315,87 @@
|
|
|
281
315
|
},
|
|
282
316
|
}));
|
|
283
317
|
|
|
284
|
-
//
|
|
318
|
+
// Mirror controlled props into the table. Each effect tracks only its prop
|
|
319
|
+
// (the comparison runs untracked), so a table-side change such as a filter
|
|
320
|
+
// resetting the page is never undone by a stale prop value.
|
|
321
|
+
function sameSet(a: Set<string>, b: Set<string>) {
|
|
322
|
+
return a.size === b.size && [...a].every((v) => b.has(v));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function sameFilters(a: ColumnFiltersState, b: ColumnFiltersState) {
|
|
326
|
+
if (a.size !== b.size) return false;
|
|
327
|
+
for (const [key, value] of a) {
|
|
328
|
+
const other = b.get(key);
|
|
329
|
+
if (Array.isArray(value) && Array.isArray(other)) {
|
|
330
|
+
if (value.length !== other.length || value.some((v, i) => v !== other[i])) return false;
|
|
331
|
+
} else if (value !== other) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
$effect.pre(() => {
|
|
339
|
+
const value = globalFilter;
|
|
340
|
+
untrack(() => {
|
|
341
|
+
if (value !== table.globalFilter) table.setGlobalFilter(value);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
$effect.pre(() => {
|
|
346
|
+
const column = sortColumn ?? null;
|
|
347
|
+
const direction = sortDirection;
|
|
348
|
+
untrack(() => {
|
|
349
|
+
if (column !== table.sortColumn || direction !== table.sortDirection) {
|
|
350
|
+
table.syncState({ sortColumn: column, sortDirection: direction });
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
$effect.pre(() => {
|
|
356
|
+
const index = pageIndex;
|
|
357
|
+
const size = pageSize;
|
|
358
|
+
untrack(() => {
|
|
359
|
+
if (index !== table.pageIndex || size !== table.pageSize) {
|
|
360
|
+
table.syncState({ pageIndex: index, pageSize: size });
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
$effect.pre(() => {
|
|
366
|
+
const selection = rowSelection;
|
|
367
|
+
untrack(() => {
|
|
368
|
+
if (!sameSet(selection, table.selectedRows)) table.syncState({ selectedRows: selection });
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
$effect.pre(() => {
|
|
373
|
+
const hidden = columnVisibility;
|
|
374
|
+
untrack(() => {
|
|
375
|
+
if (!sameSet(hidden, table.hiddenColumns)) table.syncState({ hiddenColumns: hidden });
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
$effect.pre(() => {
|
|
380
|
+
const filters = columnFilters;
|
|
381
|
+
untrack(() => {
|
|
382
|
+
if (!sameFilters(filters, table.columnFilters)) table.syncState({ columnFilters: filters });
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const showSelectAll = $derived(!!enableRowSelection && enableMultiRowSelection);
|
|
387
|
+
const canEdit = $derived(enableEditing && !!onCellEdit);
|
|
388
|
+
const columnCount = $derived(table.columns.length + (enableRowSelection ? 1 : 0));
|
|
389
|
+
|
|
390
|
+
function handleCellEdit(rowId: string, columnId: string, value: unknown, row: T) {
|
|
391
|
+
onCellEdit?.(rowId, columnId, value, row);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Editing needs somewhere to send the value; say so rather than silently
|
|
395
|
+
// rendering read-only cells.
|
|
285
396
|
$effect(() => {
|
|
286
|
-
if (
|
|
287
|
-
|
|
397
|
+
if (enableEditing && !onCellEdit) {
|
|
398
|
+
console.warn('[DataTable] `enableEditing` has no effect without an `onCellEdit` callback.');
|
|
288
399
|
}
|
|
289
400
|
});
|
|
290
401
|
|
|
@@ -352,141 +463,195 @@
|
|
|
352
463
|
}
|
|
353
464
|
</script>
|
|
354
465
|
|
|
355
|
-
|
|
356
|
-
<
|
|
357
|
-
{
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
<thead class="border-b bg-muted/50">
|
|
362
|
-
<tr>
|
|
363
|
-
{#if enableRowSelection}
|
|
364
|
-
<th scope="col" class="w-10 px-4 py-3">
|
|
365
|
-
<Checkbox
|
|
366
|
-
checked={table.isAllSelected}
|
|
367
|
-
indeterminate={table.isSomeSelected}
|
|
368
|
-
onCheckedChange={handleSelectAll}
|
|
369
|
-
aria-label="Select all rows"
|
|
370
|
-
/>
|
|
371
|
-
</th>
|
|
372
|
-
{/if}
|
|
466
|
+
{#if enableColumnVisibility}
|
|
467
|
+
<div class="flex justify-end pb-2">
|
|
468
|
+
<DataTableColumnVisibility {table} />
|
|
469
|
+
</div>
|
|
470
|
+
{/if}
|
|
373
471
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
472
|
+
{#if enableVirtualization && !loading}
|
|
473
|
+
<DataTableVirtual
|
|
474
|
+
{table}
|
|
475
|
+
rowHeight={virtualRowHeight}
|
|
476
|
+
overscan={virtualOverscan}
|
|
477
|
+
height={virtualHeight}
|
|
478
|
+
{onRowClick}
|
|
479
|
+
enableRowSelection={!!enableRowSelection}
|
|
480
|
+
enableSelectAll={enableMultiRowSelection}
|
|
481
|
+
class={cn('text-sm', className)}
|
|
482
|
+
/>
|
|
483
|
+
{:else}
|
|
484
|
+
<div class={cn('overflow-x-auto rounded-lg border', className)}>
|
|
485
|
+
<table class="w-full text-sm">
|
|
486
|
+
{#if caption}
|
|
487
|
+
<caption class="sr-only">{caption}</caption>
|
|
488
|
+
{/if}
|
|
489
|
+
|
|
490
|
+
<thead class="border-b bg-muted/50">
|
|
491
|
+
<tr>
|
|
492
|
+
{#if enableRowSelection}
|
|
493
|
+
<th scope="col" class="w-10 px-4 py-3">
|
|
494
|
+
{#if showSelectAll}
|
|
495
|
+
<Checkbox
|
|
496
|
+
checked={table.isAllSelected}
|
|
497
|
+
indeterminate={table.isSomeSelected}
|
|
498
|
+
onCheckedChange={handleSelectAll}
|
|
499
|
+
aria-label="Select all rows"
|
|
500
|
+
/>
|
|
501
|
+
{:else}
|
|
502
|
+
<span class="sr-only">Select</span>
|
|
503
|
+
{/if}
|
|
504
|
+
</th>
|
|
505
|
+
{/if}
|
|
506
|
+
|
|
507
|
+
{#each table.columns as column (column.id)}
|
|
508
|
+
{@const isSorted = table.sortColumn === column.id}
|
|
509
|
+
<th
|
|
510
|
+
scope="col"
|
|
511
|
+
class={cn(
|
|
512
|
+
'px-4 py-3 font-medium text-muted-foreground',
|
|
513
|
+
getAlignClass(column.align),
|
|
514
|
+
column.hideBelow && hideBelowClasses[column.hideBelow],
|
|
515
|
+
column.sortable && 'cursor-pointer select-none hover:bg-muted-hover'
|
|
516
|
+
)}
|
|
517
|
+
style={getColumnWidthStyle(column)}
|
|
518
|
+
aria-sort={getAriaSortValue(isSorted, table.sortDirection)}
|
|
519
|
+
>
|
|
520
|
+
{#if column.sortable}
|
|
521
|
+
<button
|
|
522
|
+
type="button"
|
|
523
|
+
class="inline-flex w-full items-center justify-between gap-2"
|
|
524
|
+
onclick={() => handleHeaderClick(column.id, true)}
|
|
404
525
|
>
|
|
405
|
-
<
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
526
|
+
<span>{column.header}</span>
|
|
527
|
+
<svg
|
|
528
|
+
class={cn(
|
|
529
|
+
'h-4 w-4 transition-transform',
|
|
530
|
+
!isSorted && 'opacity-0',
|
|
531
|
+
isSorted && table.sortDirection === 'desc' && 'rotate-180'
|
|
532
|
+
)}
|
|
533
|
+
fill="none"
|
|
534
|
+
viewBox="0 0 24 24"
|
|
535
|
+
stroke="currentColor"
|
|
536
|
+
stroke-width="2"
|
|
537
|
+
>
|
|
538
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7" />
|
|
539
|
+
</svg>
|
|
540
|
+
</button>
|
|
541
|
+
{:else}
|
|
542
|
+
{column.header}
|
|
543
|
+
{/if}
|
|
544
|
+
</th>
|
|
545
|
+
{/each}
|
|
546
|
+
</tr>
|
|
547
|
+
{#if enableFiltering}
|
|
419
548
|
<tr>
|
|
420
549
|
{#if enableRowSelection}
|
|
421
|
-
<td class="px-4
|
|
422
|
-
<Skeleton class="h-5 w-5" />
|
|
423
|
-
</td>
|
|
550
|
+
<td class="px-4 pb-3"></td>
|
|
424
551
|
{/if}
|
|
425
552
|
{#each table.columns as column (column.id)}
|
|
426
|
-
<td
|
|
427
|
-
|
|
553
|
+
<td
|
|
554
|
+
class={cn(
|
|
555
|
+
'px-4 pb-3 font-normal',
|
|
556
|
+
column.hideBelow && hideBelowClasses[column.hideBelow]
|
|
557
|
+
)}
|
|
558
|
+
>
|
|
559
|
+
{#if column.filterOptions?.length}
|
|
560
|
+
<DataTableFacetedFilter
|
|
561
|
+
{table}
|
|
562
|
+
columnId={column.id}
|
|
563
|
+
title={column.header}
|
|
564
|
+
options={column.filterOptions}
|
|
565
|
+
/>
|
|
566
|
+
{:else}
|
|
567
|
+
<DataTableColumnFilter
|
|
568
|
+
{table}
|
|
569
|
+
columnId={column.id}
|
|
570
|
+
placeholder={column.filterPlaceholder}
|
|
571
|
+
/>
|
|
572
|
+
{/if}
|
|
428
573
|
</td>
|
|
429
574
|
{/each}
|
|
430
575
|
</tr>
|
|
431
|
-
{/
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
{emptyMessage}
|
|
439
|
-
</td>
|
|
440
|
-
</tr>
|
|
441
|
-
{:else}
|
|
442
|
-
{#each table.rows as row, index (row.id)}
|
|
443
|
-
{#if rowSnippet}
|
|
444
|
-
{@render rowSnippet(row.original, index)}
|
|
445
|
-
{:else}
|
|
446
|
-
<tr
|
|
447
|
-
class={cn(
|
|
448
|
-
'bg-card',
|
|
449
|
-
onRowClick && 'cursor-pointer hover:bg-muted-hover',
|
|
450
|
-
row.isSelected && 'bg-primary/5'
|
|
451
|
-
)}
|
|
452
|
-
tabindex={onRowClick ? 0 : undefined}
|
|
453
|
-
onclick={() => handleRowClick(row.original)}
|
|
454
|
-
onkeydown={onRowClick ? (e) => handleRowKeydown(e, row.original) : undefined}
|
|
455
|
-
>
|
|
576
|
+
{/if}
|
|
577
|
+
</thead>
|
|
578
|
+
|
|
579
|
+
<tbody class="divide-y">
|
|
580
|
+
{#if loading}
|
|
581
|
+
{#each Array(loadingRows) as _, i (i)}
|
|
582
|
+
<tr>
|
|
456
583
|
{#if enableRowSelection}
|
|
457
584
|
<td class="px-4 py-3">
|
|
458
|
-
|
|
459
|
-
<Checkbox
|
|
460
|
-
checked={row.isSelected}
|
|
461
|
-
onCheckedChange={() => table.toggleRowSelection(row.id)}
|
|
462
|
-
aria-label={`Select row ${index + 1}`}
|
|
463
|
-
onclick={(e: MouseEvent) => e.stopPropagation()}
|
|
464
|
-
/>
|
|
465
|
-
{/if}
|
|
585
|
+
<Skeleton class="h-5 w-5" />
|
|
466
586
|
</td>
|
|
467
587
|
{/if}
|
|
468
|
-
|
|
469
588
|
{#each table.columns as column (column.id)}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
class={cn(
|
|
473
|
-
'px-4 py-3',
|
|
474
|
-
getAlignClass(column.align),
|
|
475
|
-
column.hideBelow && hideBelowClasses[column.hideBelow]
|
|
476
|
-
)}
|
|
477
|
-
>
|
|
478
|
-
{table.getFormattedCellValue(row.original, column)}
|
|
589
|
+
<td class={cn('px-4 py-3', column.hideBelow && hideBelowClasses[column.hideBelow])}>
|
|
590
|
+
<Skeleton class="h-4 w-3/4" />
|
|
479
591
|
</td>
|
|
480
592
|
{/each}
|
|
481
593
|
</tr>
|
|
482
|
-
{/
|
|
483
|
-
{
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
</
|
|
594
|
+
{/each}
|
|
595
|
+
{:else if table.rows.length === 0}
|
|
596
|
+
<tr>
|
|
597
|
+
<td colspan={columnCount} class="px-4 py-8 text-center text-muted-foreground">
|
|
598
|
+
{emptyMessage}
|
|
599
|
+
</td>
|
|
600
|
+
</tr>
|
|
601
|
+
{:else}
|
|
602
|
+
{#each table.rows as row, index (row.id)}
|
|
603
|
+
{#if rowSnippet}
|
|
604
|
+
{@render rowSnippet(row.original, index)}
|
|
605
|
+
{:else}
|
|
606
|
+
<tr
|
|
607
|
+
class={cn(
|
|
608
|
+
'bg-card',
|
|
609
|
+
onRowClick && 'cursor-pointer hover:bg-muted-hover',
|
|
610
|
+
row.isSelected && 'bg-primary/5'
|
|
611
|
+
)}
|
|
612
|
+
tabindex={onRowClick ? 0 : undefined}
|
|
613
|
+
onclick={() => handleRowClick(row.original)}
|
|
614
|
+
onkeydown={onRowClick ? (e) => handleRowKeydown(e, row.original) : undefined}
|
|
615
|
+
>
|
|
616
|
+
{#if enableRowSelection}
|
|
617
|
+
<td class="px-4 py-3">
|
|
618
|
+
{#if canSelectRow(row.original)}
|
|
619
|
+
<Checkbox
|
|
620
|
+
checked={row.isSelected}
|
|
621
|
+
onCheckedChange={() => table.toggleRowSelection(row.id)}
|
|
622
|
+
aria-label={`Select row ${index + 1}`}
|
|
623
|
+
onclick={(e: MouseEvent) => e.stopPropagation()}
|
|
624
|
+
/>
|
|
625
|
+
{/if}
|
|
626
|
+
</td>
|
|
627
|
+
{/if}
|
|
628
|
+
|
|
629
|
+
{#each table.columns as column (column.id)}
|
|
630
|
+
{@const value = getColumnValue(row.original, column)}
|
|
631
|
+
<td
|
|
632
|
+
class={cn(
|
|
633
|
+
'px-4 py-3',
|
|
634
|
+
getAlignClass(column.align),
|
|
635
|
+
column.hideBelow && hideBelowClasses[column.hideBelow]
|
|
636
|
+
)}
|
|
637
|
+
>
|
|
638
|
+
{#if canEdit && column.enableEditing}
|
|
639
|
+
<DataTableEditableCell {row} {column} onSave={handleCellEdit} />
|
|
640
|
+
{:else}
|
|
641
|
+
{table.getFormattedCellValue(row.original, column)}
|
|
642
|
+
{/if}
|
|
643
|
+
</td>
|
|
644
|
+
{/each}
|
|
645
|
+
</tr>
|
|
646
|
+
{/if}
|
|
647
|
+
{/each}
|
|
648
|
+
{/if}
|
|
649
|
+
</tbody>
|
|
650
|
+
</table>
|
|
651
|
+
</div>
|
|
652
|
+
{/if}
|
|
488
653
|
|
|
489
|
-
{#if enablePagination && !loading && table.rows.length > 0}
|
|
654
|
+
{#if enablePagination && !enableVirtualization && !loading && table.rows.length > 0}
|
|
490
655
|
<!-- Pagination controls -->
|
|
491
656
|
<div class="flex flex-wrap items-center justify-between gap-2 border-t px-4 py-3">
|
|
492
657
|
<div class="text-sm text-muted-foreground">
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
* />
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
import type
|
|
32
|
+
import { type Snippet } from 'svelte';
|
|
33
33
|
import type { DataTableColumn } from '../types/components.js';
|
|
34
|
-
import type { PaginationState } from './data-table/types.js';
|
|
34
|
+
import type { ColumnFiltersState, PaginationState } from './data-table/types.js';
|
|
35
35
|
declare function $$render<T extends object>(): {
|
|
36
36
|
props: {
|
|
37
37
|
/** Array of data rows */
|
|
@@ -54,13 +54,19 @@ declare function $$render<T extends object>(): {
|
|
|
54
54
|
sortColumn?: string;
|
|
55
55
|
/** Current sort direction */
|
|
56
56
|
sortDirection?: "asc" | "desc";
|
|
57
|
-
/**
|
|
58
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Callback when sort changes - when provided, enables server-side sorting.
|
|
59
|
+
* Called with `column: null` when the sort is cleared.
|
|
60
|
+
*/
|
|
61
|
+
onSort?: (column: string | null, direction: "asc" | "desc") => void;
|
|
59
62
|
/** Callback when row is clicked */
|
|
60
63
|
onRowClick?: (row: T) => void;
|
|
61
64
|
/** Enable row selection - can be boolean or function for per-row control */
|
|
62
65
|
enableRowSelection?: boolean | ((row: T) => boolean);
|
|
63
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Allow more than one selected row (default: true). When false, selecting
|
|
68
|
+
* a row replaces the previous selection and there is no select-all box.
|
|
69
|
+
*/
|
|
64
70
|
enableMultiRowSelection?: boolean;
|
|
65
71
|
/** Controlled row selection state (Set of row IDs) */
|
|
66
72
|
rowSelection?: Set<string>;
|
|
@@ -68,24 +74,33 @@ declare function $$render<T extends object>(): {
|
|
|
68
74
|
onRowSelectionChange?: (selection: Set<string>) => void;
|
|
69
75
|
/** Function to get unique row ID (defaults to the row's index in `data`) */
|
|
70
76
|
getRowId?: (row: T, index: number) => string;
|
|
71
|
-
/**
|
|
77
|
+
/** Show a "Columns" menu above the table for hiding and showing columns */
|
|
72
78
|
enableColumnVisibility?: boolean;
|
|
73
79
|
/** Controlled visibility state (Set of hidden column IDs) */
|
|
74
80
|
columnVisibility?: Set<string>;
|
|
75
81
|
/** Callback when visibility changes */
|
|
76
82
|
onColumnVisibilityChange?: (hiddenColumns: Set<string>) => void;
|
|
77
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Show a filter row under the header: a text filter per column, or a
|
|
85
|
+
* faceted (multi-select) filter for columns that define `filterOptions`
|
|
86
|
+
*/
|
|
78
87
|
enableFiltering?: boolean;
|
|
79
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Apply `globalFilter` to the rows. No search input is rendered; bind
|
|
90
|
+
* `globalFilter` to your own input.
|
|
91
|
+
*/
|
|
80
92
|
enableGlobalFilter?: boolean;
|
|
81
93
|
/** Controlled global filter value */
|
|
82
94
|
globalFilter?: string;
|
|
83
95
|
/** Callback when global filter changes */
|
|
84
96
|
onGlobalFilterChange?: (filter: string) => void;
|
|
85
|
-
/**
|
|
86
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Controlled column filters. A string value is a substring filter; a
|
|
99
|
+
* string[] value (what the faceted filter sets) matches those values exactly.
|
|
100
|
+
*/
|
|
101
|
+
columnFilters?: ColumnFiltersState;
|
|
87
102
|
/** Callback when column filters change */
|
|
88
|
-
onColumnFiltersChange?: (filters:
|
|
103
|
+
onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
|
|
89
104
|
/** Use server-side filtering (manual mode) */
|
|
90
105
|
manualFiltering?: boolean;
|
|
91
106
|
/** Enable built-in pagination */
|
|
@@ -100,13 +115,22 @@ declare function $$render<T extends object>(): {
|
|
|
100
115
|
manualPagination?: boolean;
|
|
101
116
|
/** Total page count (required for server-side pagination) */
|
|
102
117
|
pageCount?: number;
|
|
103
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Render rows with DataTableVirtual (only the visible rows are in the DOM)
|
|
120
|
+
* for large datasets (1000+ rows recommended). Pagination, `row` snippets,
|
|
121
|
+
* `hideBelow`, and inline editing do not apply in this mode.
|
|
122
|
+
*/
|
|
104
123
|
enableVirtualization?: boolean;
|
|
124
|
+
/** Height of the scrolling body when virtualized (CSS value, default: 500px) */
|
|
125
|
+
virtualHeight?: string;
|
|
105
126
|
/** Height of each row in pixels for virtualization (default: 48) */
|
|
106
127
|
virtualRowHeight?: number;
|
|
107
128
|
/** Number of rows to render outside the visible viewport (default: 10) */
|
|
108
129
|
virtualOverscan?: number;
|
|
109
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Enable inline cell editing (DataTableEditableCell) for columns that set
|
|
132
|
+
* `enableEditing: true`. Requires `onCellEdit`.
|
|
133
|
+
*/
|
|
110
134
|
enableEditing?: boolean;
|
|
111
135
|
/** Callback when a cell is edited */
|
|
112
136
|
onCellEdit?: (rowId: string, columnId: string, value: unknown, row: T) => void;
|