@casinogate/ui 1.10.7 → 1.10.9
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.
|
@@ -19,14 +19,16 @@
|
|
|
19
19
|
|
|
20
20
|
let {
|
|
21
21
|
open = $bindable(false),
|
|
22
|
-
value = $bindable(
|
|
22
|
+
value = $bindable(),
|
|
23
23
|
searchValue = $bindable(''),
|
|
24
|
+
type,
|
|
24
25
|
groups = [],
|
|
25
26
|
empty,
|
|
26
27
|
placeholder = 'Select an option',
|
|
27
28
|
searchPlaceholder,
|
|
28
29
|
trigger: triggerSnippet,
|
|
29
30
|
item: itemSnippet,
|
|
31
|
+
label: labelSnippet,
|
|
30
32
|
portalDisabled = false,
|
|
31
33
|
allowDeselect = true,
|
|
32
34
|
triggerClass,
|
|
@@ -34,7 +36,9 @@
|
|
|
34
36
|
variant = 'default',
|
|
35
37
|
rounded = 'default',
|
|
36
38
|
fullWidth = true,
|
|
37
|
-
closeOnSelect
|
|
39
|
+
closeOnSelect,
|
|
40
|
+
|
|
41
|
+
maxContentHeight,
|
|
38
42
|
|
|
39
43
|
loading: loadingSnippet,
|
|
40
44
|
pageSize = 10,
|
|
@@ -44,10 +48,18 @@
|
|
|
44
48
|
searchDebounce = 300,
|
|
45
49
|
dependsOn,
|
|
46
50
|
clearOnDependencyChange = true,
|
|
51
|
+
onSelect,
|
|
47
52
|
|
|
48
53
|
...restProps
|
|
49
54
|
}: ComboboxAsyncProps = $props();
|
|
50
55
|
|
|
56
|
+
const isMultiple = type === 'multiple';
|
|
57
|
+
|
|
58
|
+
// Initialize value based on type
|
|
59
|
+
if (value === undefined) {
|
|
60
|
+
value = isMultiple ? ([] as string[]) : '';
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
let items = $state<CommandItem[]>(initialItems);
|
|
52
64
|
let isLoading = $state(false);
|
|
53
65
|
let isSearching = $state(false);
|
|
@@ -80,13 +92,32 @@
|
|
|
80
92
|
const groupMap = $derived(new Map(collection.groups.map((g) => [g.value, g])));
|
|
81
93
|
const groupedItems = $derived(collection.group());
|
|
82
94
|
|
|
95
|
+
const isSelected = (itemValue: string): boolean => {
|
|
96
|
+
if (Array.isArray(value)) return value.includes(itemValue);
|
|
97
|
+
return value === itemValue;
|
|
98
|
+
};
|
|
99
|
+
|
|
83
100
|
const displayValue = $derived.by(() => {
|
|
84
|
-
|
|
85
|
-
|
|
101
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
102
|
+
const item = items.find((i) => i.value === value);
|
|
103
|
+
return item?.label ?? value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
107
|
+
return value
|
|
108
|
+
.map((v) => {
|
|
109
|
+
const item = items.find((i) => i.value === v);
|
|
110
|
+
return item?.label ?? v;
|
|
111
|
+
})
|
|
112
|
+
.join(', ');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return placeholder;
|
|
86
116
|
});
|
|
87
117
|
|
|
88
118
|
const isPlaceholder = $derived.by(() => {
|
|
89
|
-
|
|
119
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
120
|
+
return typeof value === 'string' && value.trim() === '';
|
|
90
121
|
});
|
|
91
122
|
|
|
92
123
|
const hasResults = $derived(collection.size > 0);
|
|
@@ -183,7 +214,7 @@
|
|
|
183
214
|
|
|
184
215
|
// Clear selected value if enabled
|
|
185
216
|
if (clearOnDependencyChange) {
|
|
186
|
-
value = '';
|
|
217
|
+
value = isMultiple ? [] : '';
|
|
187
218
|
}
|
|
188
219
|
|
|
189
220
|
// Reload data
|
|
@@ -191,6 +222,34 @@
|
|
|
191
222
|
}
|
|
192
223
|
);
|
|
193
224
|
|
|
225
|
+
// onSelect callback
|
|
226
|
+
let previousValue = $state<string | string[]>(
|
|
227
|
+
isMultiple ? [...((value as string[]) ?? [])] : ((value as string) ?? '')
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
watch(
|
|
231
|
+
() => value,
|
|
232
|
+
(newValue) => {
|
|
233
|
+
if (!onSelect) {
|
|
234
|
+
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (typeof newValue === 'string') {
|
|
239
|
+
const item = newValue ? (items.find((i) => i.value === newValue) ?? null) : null;
|
|
240
|
+
onSelect(newValue, item);
|
|
241
|
+
} else if (Array.isArray(newValue)) {
|
|
242
|
+
const prev = Array.isArray(previousValue) ? previousValue : [];
|
|
243
|
+
const added = newValue.find((v) => !prev.includes(v));
|
|
244
|
+
const item = added ? (items.find((i) => i.value === added) ?? null) : null;
|
|
245
|
+
onSelect(newValue, item);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
249
|
+
},
|
|
250
|
+
{ lazy: true }
|
|
251
|
+
);
|
|
252
|
+
|
|
194
253
|
const loadMore = () => {
|
|
195
254
|
if (!isLoading && hasMore) {
|
|
196
255
|
fetchData(currentPage + 1, searchValue, true);
|
|
@@ -228,16 +287,25 @@
|
|
|
228
287
|
});
|
|
229
288
|
};
|
|
230
289
|
|
|
231
|
-
const onSelectItem = (item: Omit<CommandItem, 'onSelect'>,
|
|
232
|
-
|
|
290
|
+
const onSelectItem = (item: Omit<CommandItem, 'onSelect'>, itemOnSelect?: AnyFn) => () => {
|
|
291
|
+
itemOnSelect?.();
|
|
233
292
|
|
|
234
|
-
if (
|
|
235
|
-
value
|
|
293
|
+
if (Array.isArray(value)) {
|
|
294
|
+
if (value.includes(item.value)) {
|
|
295
|
+
value = allowDeselect ? value.filter((v) => v !== item.value) : value;
|
|
296
|
+
} else {
|
|
297
|
+
value = [...value, item.value];
|
|
298
|
+
}
|
|
236
299
|
} else {
|
|
237
|
-
|
|
300
|
+
if (item.value === value) {
|
|
301
|
+
value = allowDeselect ? '' : value;
|
|
302
|
+
} else {
|
|
303
|
+
value = item.value;
|
|
304
|
+
}
|
|
238
305
|
}
|
|
239
306
|
|
|
240
|
-
|
|
307
|
+
const shouldClose = closeOnSelect ?? !isMultiple;
|
|
308
|
+
if (shouldClose) {
|
|
241
309
|
open = false;
|
|
242
310
|
}
|
|
243
311
|
};
|
|
@@ -248,7 +316,11 @@
|
|
|
248
316
|
class: cn(comboboxTriggerVariants({ variant, size, rounded, fullWidth, class: triggerClass })),
|
|
249
317
|
'data-placeholder': boolAttr(isPlaceholder),
|
|
250
318
|
}}
|
|
251
|
-
{#if
|
|
319
|
+
{#if labelSnippet}
|
|
320
|
+
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
321
|
+
{@render labelSnippet({ placeholder: displayValue, value: isMultiple ? (value as string[]) : (value as string) })}
|
|
322
|
+
</PopoverPrimitive.Trigger>
|
|
323
|
+
{:else if triggerSnippet}
|
|
252
324
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
253
325
|
{#snippet child({ props })}
|
|
254
326
|
{@render triggerSnippet?.({ props, displayValue })}
|
|
@@ -262,11 +334,11 @@
|
|
|
262
334
|
{/snippet}
|
|
263
335
|
|
|
264
336
|
{#snippet renderItem(item: CommandItem)}
|
|
265
|
-
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect, ...restItem } = item}
|
|
337
|
+
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect: itemOnSelect, ...restItem } = item}
|
|
266
338
|
{@const itemAttrs = {
|
|
267
339
|
value: itemValue,
|
|
268
|
-
onSelect: onSelectItem(item,
|
|
269
|
-
'data-selected': boolAttr(itemValue
|
|
340
|
+
onSelect: onSelectItem(item, itemOnSelect),
|
|
341
|
+
'data-selected': boolAttr(isSelected(itemValue)),
|
|
270
342
|
...restItem,
|
|
271
343
|
}}
|
|
272
344
|
|
|
@@ -281,7 +353,7 @@
|
|
|
281
353
|
{#if Icon}
|
|
282
354
|
<Icon width={16} height={16} />
|
|
283
355
|
{/if}
|
|
284
|
-
{label ??
|
|
356
|
+
{label ?? itemValue}
|
|
285
357
|
{#if shortcut && shortcut.length > 0}
|
|
286
358
|
<Kbd.Group>
|
|
287
359
|
{#each shortcut as shortcut (shortcut)}
|
|
@@ -289,7 +361,7 @@
|
|
|
289
361
|
{/each}
|
|
290
362
|
</Kbd.Group>
|
|
291
363
|
{/if}
|
|
292
|
-
{#if itemValue
|
|
364
|
+
{#if isSelected(itemValue)}
|
|
293
365
|
<span
|
|
294
366
|
class="cgui:ms-auto cgui:text-icon-regular cgui:size-4 cgui:flex cgui:items-center cgui:justify-center cgui:shrink-0"
|
|
295
367
|
transition:fly={{ duration: 250, y: 4, easing: cubicInOut }}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { boolAttr } from '../../internal/utils/attrs.js';
|
|
3
3
|
import { cn } from '../../internal/utils/common.js';
|
|
4
|
+
import { watch } from 'runed';
|
|
4
5
|
import type { AnyFn } from 'svelte-toolbelt';
|
|
5
6
|
import { cubicInOut } from 'svelte/easing';
|
|
6
7
|
import { fly } from 'svelte/transition';
|
|
@@ -13,14 +14,16 @@
|
|
|
13
14
|
|
|
14
15
|
let {
|
|
15
16
|
open = $bindable(false),
|
|
16
|
-
value = $bindable(
|
|
17
|
+
value = $bindable(),
|
|
17
18
|
searchValue = $bindable(''),
|
|
19
|
+
type,
|
|
18
20
|
collection,
|
|
19
21
|
empty,
|
|
20
22
|
placeholder = 'Select an option',
|
|
21
23
|
searchPlaceholder,
|
|
22
24
|
trigger: triggerSnippet,
|
|
23
25
|
item: itemSnippet,
|
|
26
|
+
label: labelSnippet,
|
|
24
27
|
portalDisabled = false,
|
|
25
28
|
allowDeselect = true,
|
|
26
29
|
triggerClass,
|
|
@@ -28,34 +31,97 @@
|
|
|
28
31
|
variant = 'default',
|
|
29
32
|
rounded = 'default',
|
|
30
33
|
fullWidth = true,
|
|
31
|
-
closeOnSelect
|
|
34
|
+
closeOnSelect,
|
|
32
35
|
maxContentHeight,
|
|
36
|
+
onSelect,
|
|
33
37
|
...restProps
|
|
34
38
|
}: ComboboxProps = $props();
|
|
35
39
|
|
|
40
|
+
const isMultiple = $derived(type === 'multiple');
|
|
41
|
+
|
|
42
|
+
// Initialize value based on type
|
|
43
|
+
if (value === undefined) {
|
|
44
|
+
value = isMultiple ? ([] as string[]) : '';
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
const groupMap = $derived(new Map(collection.groups.map((g) => [g.value, g])));
|
|
37
48
|
|
|
38
49
|
const groupedItems = $derived(collection.group());
|
|
39
50
|
|
|
51
|
+
const isSelected = (itemValue: string): boolean => {
|
|
52
|
+
if (Array.isArray(value)) return value.includes(itemValue);
|
|
53
|
+
return value === itemValue;
|
|
54
|
+
};
|
|
55
|
+
|
|
40
56
|
const displayValue = $derived.by(() => {
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
58
|
+
const item = collection.items.find((i) => i.value === value);
|
|
59
|
+
return item?.label ?? value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
63
|
+
return value
|
|
64
|
+
.map((v) => {
|
|
65
|
+
const item = collection.items.find((i) => i.value === v);
|
|
66
|
+
return item?.label ?? v;
|
|
67
|
+
})
|
|
68
|
+
.join(', ');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return placeholder;
|
|
43
72
|
});
|
|
44
73
|
|
|
45
74
|
const isPlaceholder = $derived.by(() => {
|
|
46
|
-
|
|
75
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
76
|
+
return typeof value === 'string' && value.trim() === '';
|
|
47
77
|
});
|
|
48
78
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
79
|
+
let previousValue = $state<string | string[]>(
|
|
80
|
+
isMultiple ? [...((value as string[]) ?? [])] : ((value as string) ?? '')
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
watch(
|
|
84
|
+
() => value,
|
|
85
|
+
(newValue) => {
|
|
86
|
+
if (!onSelect) {
|
|
87
|
+
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeof newValue === 'string') {
|
|
92
|
+
const item = newValue ? (collection.items.find((i) => i.value === newValue) ?? null) : null;
|
|
93
|
+
onSelect(newValue, item);
|
|
94
|
+
} else if (Array.isArray(newValue)) {
|
|
95
|
+
const prev = Array.isArray(previousValue) ? previousValue : [];
|
|
96
|
+
const added = newValue.find((v) => !prev.includes(v));
|
|
97
|
+
const item = added ? (collection.items.find((i) => i.value === added) ?? null) : null;
|
|
98
|
+
onSelect(newValue, item);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
102
|
+
},
|
|
103
|
+
{ lazy: true }
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const onSelectItem = (item: Omit<CommandItem, 'onSelect'>, itemOnSelect?: AnyFn) => () => {
|
|
107
|
+
itemOnSelect?.();
|
|
108
|
+
|
|
109
|
+
if (Array.isArray(value)) {
|
|
110
|
+
if (value.includes(item.value)) {
|
|
111
|
+
value = allowDeselect ? value.filter((v) => v !== item.value) : value;
|
|
112
|
+
} else {
|
|
113
|
+
value = [...value, item.value];
|
|
114
|
+
}
|
|
54
115
|
} else {
|
|
55
|
-
|
|
116
|
+
if (item.value === value) {
|
|
117
|
+
value = allowDeselect ? '' : value;
|
|
118
|
+
} else {
|
|
119
|
+
value = item.value;
|
|
120
|
+
}
|
|
56
121
|
}
|
|
57
122
|
|
|
58
|
-
|
|
123
|
+
const shouldClose = closeOnSelect ?? !isMultiple;
|
|
124
|
+
if (shouldClose) {
|
|
59
125
|
open = false;
|
|
60
126
|
}
|
|
61
127
|
};
|
|
@@ -66,7 +132,11 @@
|
|
|
66
132
|
class: cn(comboboxTriggerVariants({ variant, size, rounded, fullWidth, class: triggerClass })),
|
|
67
133
|
'data-placeholder': boolAttr(isPlaceholder),
|
|
68
134
|
}}
|
|
69
|
-
{#if
|
|
135
|
+
{#if labelSnippet}
|
|
136
|
+
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
137
|
+
{@render labelSnippet({ placeholder: displayValue, value: isMultiple ? (value as string[]) : (value as string) })}
|
|
138
|
+
</PopoverPrimitive.Trigger>
|
|
139
|
+
{:else if triggerSnippet}
|
|
70
140
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
71
141
|
{#snippet child({ props })}
|
|
72
142
|
{@render triggerSnippet?.({ props, displayValue })}
|
|
@@ -80,11 +150,11 @@
|
|
|
80
150
|
{/snippet}
|
|
81
151
|
|
|
82
152
|
{#snippet renderItem(item: CommandItem)}
|
|
83
|
-
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect, ...restItem } = item}
|
|
153
|
+
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect: itemOnSelect, ...restItem } = item}
|
|
84
154
|
{@const itemAttrs = {
|
|
85
155
|
value: itemValue,
|
|
86
|
-
onSelect: onSelectItem(item,
|
|
87
|
-
'data-selected': boolAttr(itemValue
|
|
156
|
+
onSelect: onSelectItem(item, itemOnSelect),
|
|
157
|
+
'data-selected': boolAttr(isSelected(itemValue)),
|
|
88
158
|
...restItem,
|
|
89
159
|
}}
|
|
90
160
|
|
|
@@ -99,7 +169,7 @@
|
|
|
99
169
|
{#if Icon}
|
|
100
170
|
<Icon width={16} height={16} />
|
|
101
171
|
{/if}
|
|
102
|
-
{label ??
|
|
172
|
+
{label ?? itemValue}
|
|
103
173
|
{#if shortcut && shortcut.length > 0}
|
|
104
174
|
<Kbd.Group>
|
|
105
175
|
{#each shortcut as shortcut (shortcut)}
|
|
@@ -107,7 +177,7 @@
|
|
|
107
177
|
{/each}
|
|
108
178
|
</Kbd.Group>
|
|
109
179
|
{/if}
|
|
110
|
-
{#if itemValue
|
|
180
|
+
{#if isSelected(itemValue)}
|
|
111
181
|
<span
|
|
112
182
|
class="cgui:ms-auto cgui:text-icon-regular cgui:size-4 cgui:flex cgui:items-center cgui:justify-center cgui:shrink-0"
|
|
113
183
|
transition:fly={{ duration: 250, y: 4, easing: cubicInOut }}
|
|
@@ -3,9 +3,11 @@ import type { CommandGroup, CommandItem, CommandProps } from '../command/index.j
|
|
|
3
3
|
import type { PrimitivePopoverRootProps } from '../popover/types.js';
|
|
4
4
|
import type { ComboboxTriggerVariantsProps } from './styles.js';
|
|
5
5
|
export type ComboboxRootProps = PrimitivePopoverRootProps;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Base props shared by both single and multiple selection modes
|
|
8
|
+
*/
|
|
9
|
+
type ComboboxBaseProps = PrimitivePopoverRootProps & ComboboxTriggerVariantsProps & {
|
|
7
10
|
collection: CommandProps['collection'];
|
|
8
|
-
value?: string;
|
|
9
11
|
empty?: CommandProps['empty'];
|
|
10
12
|
placeholder?: string;
|
|
11
13
|
searchPlaceholder?: string;
|
|
@@ -20,7 +22,25 @@ export type ComboboxProps = PrimitivePopoverRootProps & ComboboxTriggerVariantsP
|
|
|
20
22
|
displayValue: string;
|
|
21
23
|
}]>;
|
|
22
24
|
item?: CommandProps['item'];
|
|
25
|
+
/** Custom label renderer for trigger display */
|
|
26
|
+
label?: Snippet<[{
|
|
27
|
+
placeholder: string;
|
|
28
|
+
value: string | string[];
|
|
29
|
+
}]>;
|
|
30
|
+
/** Callback when selection changes */
|
|
31
|
+
onSelect?: (value: string | string[], item: CommandItem | null) => void;
|
|
32
|
+
};
|
|
33
|
+
type ComboboxSingleProps = ComboboxBaseProps & {
|
|
34
|
+
/** Selection mode (default: 'single') */
|
|
35
|
+
type?: 'single';
|
|
36
|
+
value?: string;
|
|
37
|
+
};
|
|
38
|
+
type ComboboxMultipleProps = ComboboxBaseProps & {
|
|
39
|
+
/** Selection mode */
|
|
40
|
+
type: 'multiple';
|
|
41
|
+
value?: string[];
|
|
23
42
|
};
|
|
43
|
+
export type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps;
|
|
24
44
|
/**
|
|
25
45
|
* Async Combobox Types
|
|
26
46
|
*/
|
|
@@ -34,7 +54,7 @@ export type ComboboxAsyncCallbackResult = {
|
|
|
34
54
|
hasMore: boolean;
|
|
35
55
|
};
|
|
36
56
|
export type ComboboxAsyncCallback = (params: ComboboxAsyncCallbackParams) => Promise<ComboboxAsyncCallbackResult>;
|
|
37
|
-
|
|
57
|
+
type ComboboxAsyncBaseProps = Omit<ComboboxBaseProps, 'collection'> & {
|
|
38
58
|
/** Loading state renderer */
|
|
39
59
|
loading?: Snippet;
|
|
40
60
|
/** Current page (default: 1) */
|
|
@@ -62,3 +82,15 @@ export type ComboboxAsyncProps = Omit<ComboboxProps, 'collection'> & {
|
|
|
62
82
|
*/
|
|
63
83
|
clearOnDependencyChange?: boolean;
|
|
64
84
|
};
|
|
85
|
+
type ComboboxAsyncSingleProps = ComboboxAsyncBaseProps & {
|
|
86
|
+
/** Selection mode (default: 'single') */
|
|
87
|
+
type?: 'single';
|
|
88
|
+
value?: string;
|
|
89
|
+
};
|
|
90
|
+
type ComboboxAsyncMultipleProps = ComboboxAsyncBaseProps & {
|
|
91
|
+
/** Selection mode */
|
|
92
|
+
type: 'multiple';
|
|
93
|
+
value?: string[];
|
|
94
|
+
};
|
|
95
|
+
export type ComboboxAsyncProps = ComboboxAsyncSingleProps | ComboboxAsyncMultipleProps;
|
|
96
|
+
export {};
|