@casinogate/ui 1.10.8 → 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,7 @@
|
|
|
34
36
|
variant = 'default',
|
|
35
37
|
rounded = 'default',
|
|
36
38
|
fullWidth = true,
|
|
37
|
-
closeOnSelect
|
|
39
|
+
closeOnSelect,
|
|
38
40
|
|
|
39
41
|
maxContentHeight,
|
|
40
42
|
|
|
@@ -46,10 +48,18 @@
|
|
|
46
48
|
searchDebounce = 300,
|
|
47
49
|
dependsOn,
|
|
48
50
|
clearOnDependencyChange = true,
|
|
51
|
+
onSelect,
|
|
49
52
|
|
|
50
53
|
...restProps
|
|
51
54
|
}: ComboboxAsyncProps = $props();
|
|
52
55
|
|
|
56
|
+
const isMultiple = type === 'multiple';
|
|
57
|
+
|
|
58
|
+
// Initialize value based on type
|
|
59
|
+
if (value === undefined) {
|
|
60
|
+
value = isMultiple ? ([] as string[]) : '';
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
let items = $state<CommandItem[]>(initialItems);
|
|
54
64
|
let isLoading = $state(false);
|
|
55
65
|
let isSearching = $state(false);
|
|
@@ -82,13 +92,32 @@
|
|
|
82
92
|
const groupMap = $derived(new Map(collection.groups.map((g) => [g.value, g])));
|
|
83
93
|
const groupedItems = $derived(collection.group());
|
|
84
94
|
|
|
95
|
+
const isSelected = (itemValue: string): boolean => {
|
|
96
|
+
if (Array.isArray(value)) return value.includes(itemValue);
|
|
97
|
+
return value === itemValue;
|
|
98
|
+
};
|
|
99
|
+
|
|
85
100
|
const displayValue = $derived.by(() => {
|
|
86
|
-
|
|
87
|
-
|
|
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;
|
|
88
116
|
});
|
|
89
117
|
|
|
90
118
|
const isPlaceholder = $derived.by(() => {
|
|
91
|
-
|
|
119
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
120
|
+
return typeof value === 'string' && value.trim() === '';
|
|
92
121
|
});
|
|
93
122
|
|
|
94
123
|
const hasResults = $derived(collection.size > 0);
|
|
@@ -185,7 +214,7 @@
|
|
|
185
214
|
|
|
186
215
|
// Clear selected value if enabled
|
|
187
216
|
if (clearOnDependencyChange) {
|
|
188
|
-
value = '';
|
|
217
|
+
value = isMultiple ? [] : '';
|
|
189
218
|
}
|
|
190
219
|
|
|
191
220
|
// Reload data
|
|
@@ -193,6 +222,34 @@
|
|
|
193
222
|
}
|
|
194
223
|
);
|
|
195
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
|
+
|
|
196
253
|
const loadMore = () => {
|
|
197
254
|
if (!isLoading && hasMore) {
|
|
198
255
|
fetchData(currentPage + 1, searchValue, true);
|
|
@@ -230,16 +287,25 @@
|
|
|
230
287
|
});
|
|
231
288
|
};
|
|
232
289
|
|
|
233
|
-
const onSelectItem = (item: Omit<CommandItem, 'onSelect'>,
|
|
234
|
-
|
|
290
|
+
const onSelectItem = (item: Omit<CommandItem, 'onSelect'>, itemOnSelect?: AnyFn) => () => {
|
|
291
|
+
itemOnSelect?.();
|
|
235
292
|
|
|
236
|
-
if (
|
|
237
|
-
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
|
+
}
|
|
238
299
|
} else {
|
|
239
|
-
|
|
300
|
+
if (item.value === value) {
|
|
301
|
+
value = allowDeselect ? '' : value;
|
|
302
|
+
} else {
|
|
303
|
+
value = item.value;
|
|
304
|
+
}
|
|
240
305
|
}
|
|
241
306
|
|
|
242
|
-
|
|
307
|
+
const shouldClose = closeOnSelect ?? !isMultiple;
|
|
308
|
+
if (shouldClose) {
|
|
243
309
|
open = false;
|
|
244
310
|
}
|
|
245
311
|
};
|
|
@@ -250,7 +316,11 @@
|
|
|
250
316
|
class: cn(comboboxTriggerVariants({ variant, size, rounded, fullWidth, class: triggerClass })),
|
|
251
317
|
'data-placeholder': boolAttr(isPlaceholder),
|
|
252
318
|
}}
|
|
253
|
-
{#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}
|
|
254
324
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
255
325
|
{#snippet child({ props })}
|
|
256
326
|
{@render triggerSnippet?.({ props, displayValue })}
|
|
@@ -264,11 +334,11 @@
|
|
|
264
334
|
{/snippet}
|
|
265
335
|
|
|
266
336
|
{#snippet renderItem(item: CommandItem)}
|
|
267
|
-
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect, ...restItem } = item}
|
|
337
|
+
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect: itemOnSelect, ...restItem } = item}
|
|
268
338
|
{@const itemAttrs = {
|
|
269
339
|
value: itemValue,
|
|
270
|
-
onSelect: onSelectItem(item,
|
|
271
|
-
'data-selected': boolAttr(itemValue
|
|
340
|
+
onSelect: onSelectItem(item, itemOnSelect),
|
|
341
|
+
'data-selected': boolAttr(isSelected(itemValue)),
|
|
272
342
|
...restItem,
|
|
273
343
|
}}
|
|
274
344
|
|
|
@@ -283,7 +353,7 @@
|
|
|
283
353
|
{#if Icon}
|
|
284
354
|
<Icon width={16} height={16} />
|
|
285
355
|
{/if}
|
|
286
|
-
{label ??
|
|
356
|
+
{label ?? itemValue}
|
|
287
357
|
{#if shortcut && shortcut.length > 0}
|
|
288
358
|
<Kbd.Group>
|
|
289
359
|
{#each shortcut as shortcut (shortcut)}
|
|
@@ -291,7 +361,7 @@
|
|
|
291
361
|
{/each}
|
|
292
362
|
</Kbd.Group>
|
|
293
363
|
{/if}
|
|
294
|
-
{#if itemValue
|
|
364
|
+
{#if isSelected(itemValue)}
|
|
295
365
|
<span
|
|
296
366
|
class="cgui:ms-auto cgui:text-icon-regular cgui:size-4 cgui:flex cgui:items-center cgui:justify-center cgui:shrink-0"
|
|
297
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 {};
|