@classic-homes/theme-svelte 1.42.3 → 1.44.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/DataTable.svelte +2 -0
- package/dist/lib/components/MultiSelect.svelte +38 -7
- package/dist/lib/components/MultiSelect.svelte.d.ts +6 -0
- package/dist/lib/components/data-table/DataTableEditableCell.svelte +10 -2
- package/dist/lib/components/data-table/DataTableVirtual.svelte +5 -1
- package/dist/lib/types/components.d.ts +12 -1
- package/package.json +1 -1
|
@@ -637,6 +637,8 @@
|
|
|
637
637
|
>
|
|
638
638
|
{#if canEdit && column.enableEditing}
|
|
639
639
|
<DataTableEditableCell {row} {column} onSave={handleCellEdit} />
|
|
640
|
+
{:else if column.cell}
|
|
641
|
+
{@render column.cell(value, row.original)}
|
|
640
642
|
{:else}
|
|
641
643
|
{table.getFormattedCellValue(row.original, column)}
|
|
642
644
|
{/if}
|
|
@@ -47,6 +47,12 @@
|
|
|
47
47
|
onSearch?: (query: string) => void;
|
|
48
48
|
/** Debounce delay in ms for search callback (default: 300) */
|
|
49
49
|
debounceMs?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Selected tags shown before the rest fold into a "+N more" toggle
|
|
52
|
+
* (default 6), so a long selection (e.g. a select-all) doesn't push the
|
|
53
|
+
* layout down. `Infinity` always shows every tag.
|
|
54
|
+
*/
|
|
55
|
+
maxVisibleTags?: number;
|
|
50
56
|
/** Additional class for the trigger */
|
|
51
57
|
class?: string;
|
|
52
58
|
}
|
|
@@ -68,6 +74,7 @@
|
|
|
68
74
|
loading = false,
|
|
69
75
|
onSearch,
|
|
70
76
|
debounceMs = 300,
|
|
77
|
+
maxVisibleTags = 6,
|
|
71
78
|
class: className,
|
|
72
79
|
}: Props = $props();
|
|
73
80
|
|
|
@@ -85,11 +92,23 @@
|
|
|
85
92
|
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
86
93
|
let open = $state(false);
|
|
87
94
|
|
|
88
|
-
//
|
|
89
|
-
const
|
|
90
|
-
value.
|
|
95
|
+
// The selected values that have an option, with their labels (tag order = pick order)
|
|
96
|
+
const selectedTags = $derived(
|
|
97
|
+
value.flatMap((v) => {
|
|
98
|
+
const label = options.find((opt) => opt.value === v)?.label;
|
|
99
|
+
return label ? [{ value: v, label }] : [];
|
|
100
|
+
})
|
|
91
101
|
);
|
|
92
102
|
|
|
103
|
+
// Past maxVisibleTags the rest fold behind a "+N more" toggle.
|
|
104
|
+
let tagsExpanded = $state(false);
|
|
105
|
+
const tagLimit = $derived(Math.max(0, maxVisibleTags));
|
|
106
|
+
const hiddenTagCount = $derived(Math.max(0, selectedTags.length - tagLimit));
|
|
107
|
+
const visibleTags = $derived(
|
|
108
|
+
tagsExpanded || hiddenTagCount === 0 ? selectedTags : selectedTags.slice(0, tagLimit)
|
|
109
|
+
);
|
|
110
|
+
const tagsId = $derived(`${componentId}-tags`);
|
|
111
|
+
|
|
93
112
|
// Always filter options locally based on search query
|
|
94
113
|
const filteredOptions = $derived.by(() => {
|
|
95
114
|
if (!searchQuery) return options;
|
|
@@ -203,10 +222,11 @@
|
|
|
203
222
|
|
|
204
223
|
<div class={cn('w-full', className)}>
|
|
205
224
|
<!-- Selected tags -->
|
|
206
|
-
{#if
|
|
207
|
-
<div class="flex flex-wrap gap-1 mb-2">
|
|
208
|
-
{#each
|
|
209
|
-
{@const
|
|
225
|
+
{#if selectedTags.length > 0}
|
|
226
|
+
<div id={tagsId} class="flex flex-wrap gap-1 mb-2">
|
|
227
|
+
{#each visibleTags as tag (tag.value)}
|
|
228
|
+
{@const selectedValue = tag.value}
|
|
229
|
+
{@const labelText = tag.label}
|
|
210
230
|
{#if labelText}
|
|
211
231
|
<span
|
|
212
232
|
class="inline-flex items-center gap-1 rounded-md bg-muted px-2 py-1 text-xs font-medium text-foreground"
|
|
@@ -237,6 +257,17 @@
|
|
|
237
257
|
</span>
|
|
238
258
|
{/if}
|
|
239
259
|
{/each}
|
|
260
|
+
{#if hiddenTagCount > 0}
|
|
261
|
+
<button
|
|
262
|
+
type="button"
|
|
263
|
+
onclick={() => (tagsExpanded = !tagsExpanded)}
|
|
264
|
+
class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-primary underline-offset-2 hover:underline focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
|
265
|
+
aria-expanded={tagsExpanded}
|
|
266
|
+
aria-controls={tagsId}
|
|
267
|
+
>
|
|
268
|
+
{tagsExpanded ? 'Show less' : `+${hiddenTagCount} more`}
|
|
269
|
+
</button>
|
|
270
|
+
{/if}
|
|
240
271
|
</div>
|
|
241
272
|
{/if}
|
|
242
273
|
|
|
@@ -36,6 +36,12 @@ interface Props {
|
|
|
36
36
|
onSearch?: (query: string) => void;
|
|
37
37
|
/** Debounce delay in ms for search callback (default: 300) */
|
|
38
38
|
debounceMs?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Selected tags shown before the rest fold into a "+N more" toggle
|
|
41
|
+
* (default 6), so a long selection (e.g. a select-all) doesn't push the
|
|
42
|
+
* layout down. `Infinity` always shows every tag.
|
|
43
|
+
*/
|
|
44
|
+
maxVisibleTags?: number;
|
|
39
45
|
/** Additional class for the trigger */
|
|
40
46
|
class?: string;
|
|
41
47
|
}
|
|
@@ -146,12 +146,20 @@
|
|
|
146
146
|
onkeydown={(e: KeyboardEvent) => e.key === 'Enter' && startEditing()}
|
|
147
147
|
>
|
|
148
148
|
<span class="border-b border-dashed border-transparent hover:border-primary">
|
|
149
|
-
{
|
|
149
|
+
{#if column.cell}
|
|
150
|
+
{@render column.cell(getColumnValue(row.original, column), row.original)}
|
|
151
|
+
{:else}
|
|
152
|
+
{displayValue}
|
|
153
|
+
{/if}
|
|
150
154
|
</span>
|
|
151
155
|
<span class="sr-only">(double-click to edit)</span>
|
|
152
156
|
</button>
|
|
153
157
|
{:else}
|
|
154
158
|
<span class={cn('relative', className)}>
|
|
155
|
-
{
|
|
159
|
+
{#if column.cell}
|
|
160
|
+
{@render column.cell(getColumnValue(row.original, column), row.original)}
|
|
161
|
+
{:else}
|
|
162
|
+
{displayValue}
|
|
163
|
+
{/if}
|
|
156
164
|
</span>
|
|
157
165
|
{/if}
|
|
@@ -243,7 +243,11 @@
|
|
|
243
243
|
style={column.width ? `width: ${column.width}` : 'flex: 1'}
|
|
244
244
|
role="cell"
|
|
245
245
|
>
|
|
246
|
-
{
|
|
246
|
+
{#if column.cell}
|
|
247
|
+
{@render column.cell(getColumnValue(row.original, column), row.original)}
|
|
248
|
+
{:else}
|
|
249
|
+
{table.getFormattedCellValue(row.original, column)}
|
|
250
|
+
{/if}
|
|
247
251
|
</div>
|
|
248
252
|
{/each}
|
|
249
253
|
</div>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* These types are exported from components but need to be
|
|
5
5
|
* re-exported from a TypeScript file for proper type inference.
|
|
6
6
|
*/
|
|
7
|
+
import type { Snippet } from 'svelte';
|
|
7
8
|
import type { IconName } from '../components/Icon.svelte';
|
|
8
9
|
/**
|
|
9
10
|
* Dropdown Menu Types
|
|
@@ -102,8 +103,18 @@ export interface DataTableColumn<T = object> {
|
|
|
102
103
|
* mirror the guard themselves so cell counts stay in sync.
|
|
103
104
|
*/
|
|
104
105
|
hideBelow?: 'sm' | 'md' | 'lg';
|
|
105
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Custom cell text. Global search matches it, and it stays the cell's text
|
|
108
|
+
* (e.g. for `getFormattedCellValue` and exports) when `cell` renders it.
|
|
109
|
+
*/
|
|
106
110
|
format?: (value: unknown, row: T) => string;
|
|
111
|
+
/**
|
|
112
|
+
* Custom cell content (e.g. a value with its change underneath). Rendered
|
|
113
|
+
* in place of the formatted text whenever the cell is not being edited
|
|
114
|
+
* (editable cells still edit the raw value); `format` still supplies the
|
|
115
|
+
* cell's text for search and exports.
|
|
116
|
+
*/
|
|
117
|
+
cell?: Snippet<[value: unknown, row: T]>;
|
|
107
118
|
/** Filter function type or custom filter function */
|
|
108
119
|
filterFn?: DataTableFilterFn<T>;
|
|
109
120
|
/** Options for select/faceted filters */
|