sveltacular 1.0.16 → 1.0.17
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/tables/data-grid.svelte +67 -32
- package/dist/tables/data-grid.svelte.d.ts +8 -2
- package/dist/tables/table-context.svelte.d.ts +1 -1
- package/dist/tables/table-context.svelte.js +3 -3
- package/dist/tables/table-selection-cell.svelte +25 -10
- package/dist/tables/table-selection-header-cell.svelte +13 -3
- package/dist/tables/table.svelte +1 -1
- package/dist/tables/table.svelte.d.ts +1 -1
- package/package.json +1 -1
|
@@ -25,17 +25,23 @@
|
|
|
25
25
|
} from './cell-renderers.js';
|
|
26
26
|
import type { Snippet } from 'svelte';
|
|
27
27
|
import { useVirtualList } from '../helpers/use-virtual-list.svelte.js';
|
|
28
|
+
import type { ButtonVariant, FormFieldSizeOptions } from '../types/form.js';
|
|
28
29
|
|
|
29
30
|
type PaginationEvent = (pagination: PaginationProperties) => void;
|
|
30
31
|
|
|
31
32
|
interface Action {
|
|
32
33
|
text: string;
|
|
33
|
-
|
|
34
|
+
variant?: ButtonVariant;
|
|
35
|
+
href?: (row: JsonObject) => string;
|
|
36
|
+
onClick?: (row: JsonObject) => unknown;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
interface Actions {
|
|
37
40
|
text?: string;
|
|
38
|
-
type?:
|
|
41
|
+
type?: 'buttons' | 'dropdown';
|
|
42
|
+
variant?: ButtonVariant | 'default';
|
|
43
|
+
size?: FormFieldSizeOptions;
|
|
44
|
+
align?: 'left' | 'center' | 'right';
|
|
39
45
|
items: Action[];
|
|
40
46
|
}
|
|
41
47
|
|
|
@@ -113,10 +119,15 @@
|
|
|
113
119
|
let colCount = $derived(
|
|
114
120
|
Math.max(1, visibleCols.length) + (hasActionCol ? 1 : 0) + (hasSelectionCol ? 1 : 0)
|
|
115
121
|
);
|
|
116
|
-
|
|
122
|
+
let actionButtonVariant = $derived.by(() => {
|
|
123
|
+
return !actions?.variant || actions.variant === 'default' ? 'outline' : actions.variant;
|
|
124
|
+
});
|
|
125
|
+
let actionButtonSize = $derived(actions?.size ?? 'sm');
|
|
126
|
+
let actionAlign = $derived(actions?.align ?? 'center');
|
|
127
|
+
|
|
117
128
|
// Track selected count from selection change callbacks
|
|
118
129
|
let internalSelectedCount = $state(0);
|
|
119
|
-
|
|
130
|
+
|
|
120
131
|
// Sync selectedCount with internal tracking
|
|
121
132
|
$effect(() => {
|
|
122
133
|
selectedCount = internalSelectedCount;
|
|
@@ -198,10 +209,16 @@
|
|
|
198
209
|
{selectionMode}
|
|
199
210
|
{rowIdKey}
|
|
200
211
|
onSort={handleSortChange}
|
|
201
|
-
onSelectionChange={(
|
|
202
|
-
|
|
212
|
+
onSelectionChange={(selectedRowIds) => {
|
|
213
|
+
// Convert selected IDs to actual row objects
|
|
214
|
+
// Use sortedRows (before pagination) to include all selected rows, not just current page
|
|
215
|
+
const selectedRowObjects = (sortedRows ?? []).filter((row) => {
|
|
216
|
+
const id = row[rowIdKey] as string | number;
|
|
217
|
+
return id !== undefined && selectedRowIds.includes(id);
|
|
218
|
+
});
|
|
219
|
+
onSelectionChange?.(selectedRowObjects);
|
|
203
220
|
// Track selected count from the callback
|
|
204
|
-
internalSelectedCount =
|
|
221
|
+
internalSelectedCount = selectedRowIds.length;
|
|
205
222
|
}}
|
|
206
223
|
>
|
|
207
224
|
{#if children}
|
|
@@ -228,7 +245,7 @@
|
|
|
228
245
|
</TableHeaderCell>
|
|
229
246
|
{/each}
|
|
230
247
|
{#if hasActionCol}
|
|
231
|
-
<TableHeaderCell type="actions">Actions</TableHeaderCell>
|
|
248
|
+
<TableHeaderCell type="actions" align={actionAlign}>Actions</TableHeaderCell>
|
|
232
249
|
{/if}
|
|
233
250
|
</tr>
|
|
234
251
|
</TableHeader>
|
|
@@ -284,26 +301,32 @@
|
|
|
284
301
|
</TableCell>
|
|
285
302
|
{/each}
|
|
286
303
|
{#if hasActionCol && actions}
|
|
287
|
-
<TableCell type="actions">
|
|
304
|
+
<TableCell type="actions" align={actionAlign}>
|
|
288
305
|
{#if actions.type === 'dropdown'}
|
|
289
306
|
<DropdownButton text={actions.text ?? ''} variant="ghost">
|
|
290
307
|
{#each actions.items as action}
|
|
291
|
-
<DropdownItem
|
|
308
|
+
<DropdownItem
|
|
309
|
+
href={action.href ? action.href(row) : undefined}
|
|
310
|
+
onClick={action.onClick ? () => action.onClick?.(row) : undefined}
|
|
292
311
|
>{action.text}</DropdownItem
|
|
293
312
|
>
|
|
294
313
|
{/each}
|
|
295
314
|
</DropdownButton>
|
|
296
315
|
{:else}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
316
|
+
<div class="actions">
|
|
317
|
+
{#each actions.items as action}
|
|
318
|
+
{@const buttonVariant = action.variant ?? actionButtonVariant}
|
|
319
|
+
<Button
|
|
320
|
+
collapse={true}
|
|
321
|
+
type="button"
|
|
322
|
+
variant={buttonVariant}
|
|
323
|
+
size={actionButtonSize}
|
|
324
|
+
href={action.href ? action.href(row) : undefined}
|
|
325
|
+
onClick={action.onClick ? () => action.onClick?.(row) : undefined}
|
|
326
|
+
label={action.text}
|
|
327
|
+
/>
|
|
328
|
+
{/each}
|
|
329
|
+
</div>
|
|
307
330
|
{/if}
|
|
308
331
|
</TableCell>
|
|
309
332
|
{/if}
|
|
@@ -336,24 +359,31 @@
|
|
|
336
359
|
</TableCell>
|
|
337
360
|
{/each}
|
|
338
361
|
{#if hasActionCol && actions}
|
|
339
|
-
<TableCell type="actions">
|
|
362
|
+
<TableCell type="actions" align={actionAlign}>
|
|
340
363
|
{#if actions.type === 'dropdown'}
|
|
341
364
|
<DropdownButton text={actions.text ?? ''} variant="ghost">
|
|
342
365
|
{#each actions.items as action}
|
|
343
|
-
<DropdownItem
|
|
366
|
+
<DropdownItem
|
|
367
|
+
href={action.href ? action.href(row) : undefined}
|
|
368
|
+
onClick={action.onClick ? () => action.onClick?.(row) : undefined}
|
|
369
|
+
>{action.text}</DropdownItem
|
|
370
|
+
>
|
|
344
371
|
{/each}
|
|
345
372
|
</DropdownButton>
|
|
346
373
|
{:else}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
374
|
+
<div class="actions">
|
|
375
|
+
{#each actions.items as action}
|
|
376
|
+
{@const buttonVariant = action.variant ?? actionButtonVariant}
|
|
377
|
+
<Button
|
|
378
|
+
type="button"
|
|
379
|
+
variant={buttonVariant}
|
|
380
|
+
size={actionButtonSize}
|
|
381
|
+
href={action.href ? action.href(row) : undefined}
|
|
382
|
+
onClick={action.onClick ? () => action.onClick?.(row) : undefined}
|
|
383
|
+
label={action.text}
|
|
384
|
+
/>
|
|
385
|
+
{/each}
|
|
386
|
+
</div>
|
|
357
387
|
{/if}
|
|
358
388
|
</TableCell>
|
|
359
389
|
{/if}
|
|
@@ -412,4 +442,9 @@ td.footer-cell :global(.pagination) {
|
|
|
412
442
|
display: flex;
|
|
413
443
|
justify-content: center;
|
|
414
444
|
align-items: center;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.actions {
|
|
448
|
+
display: flex;
|
|
449
|
+
gap: 0.5rem;
|
|
415
450
|
}</style>
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import type { ColumnDef, JsonObject, PaginationProperties } from '../types/data.js';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { ButtonVariant, FormFieldSizeOptions } from '../types/form.js';
|
|
3
4
|
type PaginationEvent = (pagination: PaginationProperties) => void;
|
|
4
5
|
interface Action {
|
|
5
6
|
text: string;
|
|
6
|
-
|
|
7
|
+
variant?: ButtonVariant;
|
|
8
|
+
href?: (row: JsonObject) => string;
|
|
9
|
+
onClick?: (row: JsonObject) => unknown;
|
|
7
10
|
}
|
|
8
11
|
interface Actions {
|
|
9
12
|
text?: string;
|
|
10
|
-
type?:
|
|
13
|
+
type?: 'buttons' | 'dropdown';
|
|
14
|
+
variant?: ButtonVariant | 'default';
|
|
15
|
+
size?: FormFieldSizeOptions;
|
|
16
|
+
align?: 'left' | 'center' | 'right';
|
|
11
17
|
items: Action[];
|
|
12
18
|
}
|
|
13
19
|
type $$ComponentProps = {
|
|
@@ -4,7 +4,7 @@ export interface TableContextConfig<T extends JsonObject = JsonObject> {
|
|
|
4
4
|
selectionMode?: 'none' | 'single' | 'multi';
|
|
5
5
|
rowIdKey?: keyof T & string;
|
|
6
6
|
onSort?: (column: string, direction: SortDirection) => void;
|
|
7
|
-
onSelectionChange?: (
|
|
7
|
+
onSelectionChange?: (selectedRowIds: (string | number)[]) => void;
|
|
8
8
|
rows?: T[];
|
|
9
9
|
}
|
|
10
10
|
export declare class TableContext<T extends JsonObject = JsonObject> {
|
|
@@ -101,9 +101,9 @@ export class TableContext {
|
|
|
101
101
|
this.notifySelectionChange(rows);
|
|
102
102
|
}
|
|
103
103
|
notifySelectionChange(rows) {
|
|
104
|
-
if (this.config.onSelectionChange
|
|
105
|
-
const
|
|
106
|
-
this.config.onSelectionChange(
|
|
104
|
+
if (this.config.onSelectionChange) {
|
|
105
|
+
const selectedRowIds = Array.from(this.selectedIds);
|
|
106
|
+
this.config.onSelectionChange(selectedRowIds);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
selectRange(startIndex, endIndex, rows) {
|
|
@@ -78,18 +78,33 @@
|
|
|
78
78
|
// Handle cell click - toggle selection when clicking anywhere in the cell
|
|
79
79
|
function handleCellClick(event: MouseEvent) {
|
|
80
80
|
const target = event.target as HTMLElement;
|
|
81
|
+
const currentTarget = event.currentTarget as HTMLElement;
|
|
82
|
+
|
|
83
|
+
// If the click is on the label or any of its children (input, span, etc.),
|
|
84
|
+
// let the component's native handlers take care of it
|
|
85
|
+
const label = currentTarget.querySelector('label');
|
|
86
|
+
if (label && (target === label || label.contains(target))) {
|
|
87
|
+
// For single selection, we need to handle deselection ourselves
|
|
88
|
+
// since native radio buttons don't allow unchecking
|
|
89
|
+
if (selectionMode === 'single' && rowId !== undefined) {
|
|
90
|
+
// Check if already selected and toggle off if so
|
|
91
|
+
if (context?.radioGroup === String(rowId)) {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
handleRadioChange(String(rowId));
|
|
94
|
+
}
|
|
95
|
+
// If not selected, let the native handler select it
|
|
96
|
+
}
|
|
97
|
+
// For multi-selection, always let the native checkbox handler work
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
81
100
|
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// Native radio buttons don't allow unchecking, so we take control
|
|
89
|
-
if (target.tagName === 'INPUT') {
|
|
90
|
-
event.preventDefault();
|
|
101
|
+
// Only handle cell click if clicking directly on the cell (not on the label/input)
|
|
102
|
+
if (target === currentTarget) {
|
|
103
|
+
if (selectionMode === 'multi') {
|
|
104
|
+
handleCheckboxChange({ isChecked: !localChecked, value: '' });
|
|
105
|
+
} else if (selectionMode === 'single' && rowId !== undefined) {
|
|
106
|
+
handleRadioChange(String(rowId));
|
|
91
107
|
}
|
|
92
|
-
handleRadioChange(String(rowId));
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
</script>
|
|
@@ -52,11 +52,21 @@
|
|
|
52
52
|
|
|
53
53
|
// Handle header cell click - toggle select all when clicking anywhere in the cell
|
|
54
54
|
function handleCellClick(event: MouseEvent) {
|
|
55
|
-
// Don't double-toggle if the click was on the actual input element
|
|
56
55
|
const target = event.target as HTMLElement;
|
|
57
|
-
|
|
56
|
+
const currentTarget = event.currentTarget as HTMLElement;
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
// If the click is on the label or any of its children (input, span, etc.),
|
|
59
|
+
// let the component's native handlers take care of it
|
|
60
|
+
const label = currentTarget.querySelector('label');
|
|
61
|
+
if (label && (target === label || label.contains(target))) {
|
|
62
|
+
// Let the native checkbox handler work
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Only handle cell click if clicking directly on the cell (not on the label/input)
|
|
67
|
+
if (target === currentTarget) {
|
|
68
|
+
handleSelectAllChange({ isChecked: !localChecked, value: '' });
|
|
69
|
+
}
|
|
60
70
|
}
|
|
61
71
|
</script>
|
|
62
72
|
|
package/dist/tables/table.svelte
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
rowIdKey?: string;
|
|
22
22
|
stickyHeader?: boolean;
|
|
23
23
|
onSort?: (column: string, direction: 'asc' | 'desc') => void;
|
|
24
|
-
onSelectionChange?: (
|
|
24
|
+
onSelectionChange?: (selectedRowIds: (string | number)[]) => void;
|
|
25
25
|
} = $props();
|
|
26
26
|
|
|
27
27
|
// Create table context for child components
|
|
@@ -8,7 +8,7 @@ type $$ComponentProps = {
|
|
|
8
8
|
rowIdKey?: string;
|
|
9
9
|
stickyHeader?: boolean;
|
|
10
10
|
onSort?: (column: string, direction: 'asc' | 'desc') => void;
|
|
11
|
-
onSelectionChange?: (
|
|
11
|
+
onSelectionChange?: (selectedRowIds: (string | number)[]) => void;
|
|
12
12
|
};
|
|
13
13
|
declare const Table: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
14
|
type Table = ReturnType<typeof Table>;
|