@casinogate/ui 1.10.15 → 1.10.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/assets/css/root.css +13 -8
- package/dist/components/combobox/index.d.ts +1 -1
- package/dist/components/combobox/index.js +1 -1
- package/dist/components/combobox/{combobox.async.svelte → presets/async/combobox.async.svelte} +74 -102
- package/dist/components/combobox/{combobox.async.svelte.d.ts → presets/async/combobox.async.svelte.d.ts} +1 -1
- package/dist/components/combobox/{exports.d.ts → presets/async/index.d.ts} +0 -1
- package/dist/components/combobox/{exports.js → presets/async/index.js} +0 -1
- package/dist/components/combobox/presets/index.d.ts +2 -0
- package/dist/components/combobox/presets/index.js +2 -0
- package/dist/components/combobox/{combobox.svelte → presets/root/combobox.svelte} +78 -102
- package/dist/components/combobox/{combobox.svelte.d.ts → presets/root/combobox.svelte.d.ts} +1 -1
- package/dist/components/combobox/presets/root/index.d.ts +1 -0
- package/dist/components/combobox/presets/root/index.js +1 -0
- package/dist/components/combobox/presets/use-display-value.svelte.d.ts +9 -0
- package/dist/components/combobox/presets/use-display-value.svelte.js +20 -0
- package/dist/components/combobox/presets/use-grouped-items.svelte.d.ts +10 -0
- package/dist/components/combobox/presets/use-grouped-items.svelte.js +10 -0
- package/dist/components/combobox/presets/use-selected-items.svelte.d.ts +8 -0
- package/dist/components/combobox/presets/use-selected-items.svelte.js +17 -0
- package/dist/components/combobox/types.d.ts +3 -4
- package/dist/components/command/styles.js +1 -0
- package/dist/components/select/select.async.svelte +7 -9
- package/dist/internal/constants/selection-type.d.ts +5 -0
- package/dist/internal/constants/selection-type.js +4 -0
- package/package.json +1 -1
package/dist/assets/css/root.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! tailwindcss v4.1
|
|
1
|
+
/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */
|
|
2
2
|
@layer properties;
|
|
3
3
|
@layer theme, base, components, utilities;
|
|
4
4
|
@layer theme {
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
top: calc(var(--cgui-spacing) * 0);
|
|
205
205
|
}
|
|
206
206
|
.cgui\:top-1\/2 {
|
|
207
|
-
top: calc(1/2 * 100%);
|
|
207
|
+
top: calc(1 / 2 * 100%);
|
|
208
208
|
}
|
|
209
209
|
.cgui\:right-0 {
|
|
210
210
|
right: calc(var(--cgui-spacing) * 0);
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
left: calc(var(--cgui-spacing) * 0);
|
|
223
223
|
}
|
|
224
224
|
.cgui\:left-1\/2 {
|
|
225
|
-
left: calc(1/2 * 100%);
|
|
225
|
+
left: calc(1 / 2 * 100%);
|
|
226
226
|
}
|
|
227
227
|
.cgui\:z-\(--cg-ui-z-index-dialog\) {
|
|
228
228
|
z-index: var(--cg-ui-z-index-dialog);
|
|
@@ -324,8 +324,8 @@
|
|
|
324
324
|
display: inline-flex;
|
|
325
325
|
}
|
|
326
326
|
.cgui\:size-1\/2 {
|
|
327
|
-
width: calc(1/2 * 100%);
|
|
328
|
-
height: calc(1/2 * 100%);
|
|
327
|
+
width: calc(1 / 2 * 100%);
|
|
328
|
+
height: calc(1 / 2 * 100%);
|
|
329
329
|
}
|
|
330
330
|
.cgui\:size-3 {
|
|
331
331
|
width: calc(var(--cgui-spacing) * 3);
|
|
@@ -623,11 +623,11 @@
|
|
|
623
623
|
transform-origin: var(--bits-select-content-transform-origin);
|
|
624
624
|
}
|
|
625
625
|
.cgui\:-translate-x-1\/2 {
|
|
626
|
-
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
|
|
626
|
+
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
|
|
627
627
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
628
628
|
}
|
|
629
629
|
.cgui\:-translate-y-1\/2 {
|
|
630
|
-
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
|
|
630
|
+
--tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
|
|
631
631
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
632
632
|
}
|
|
633
633
|
.cgui\:-scale-x-\[1\] {
|
|
@@ -1628,6 +1628,11 @@
|
|
|
1628
1628
|
background-color: var(--cg-ui-color-surface-lightest);
|
|
1629
1629
|
}
|
|
1630
1630
|
}
|
|
1631
|
+
.cgui\:data-selected\:text-fg-primary {
|
|
1632
|
+
&[data-selected] {
|
|
1633
|
+
color: var(--cg-ui-color-fg-primary);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1631
1636
|
.cgui\:data-\[active\=\"\"\]\:bg-surface-white {
|
|
1632
1637
|
&[data-active=""] {
|
|
1633
1638
|
background-color: var(--cg-ui-color-surface-white);
|
|
@@ -2169,7 +2174,7 @@
|
|
|
2169
2174
|
.cgui\:active\:\[\&_span\]\:w-2\/3 {
|
|
2170
2175
|
&:active {
|
|
2171
2176
|
& span {
|
|
2172
|
-
width: calc(2/3 * 100%);
|
|
2177
|
+
width: calc(2 / 3 * 100%);
|
|
2173
2178
|
}
|
|
2174
2179
|
}
|
|
2175
2180
|
}
|
package/dist/components/combobox/{combobox.async.svelte → presets/async/combobox.async.svelte}
RENAMED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { CommandCollection, CommandItem } from '
|
|
3
|
-
import type { ComboboxAsyncProps } from '
|
|
2
|
+
import type { CommandCollection, CommandItem } from '../../../command/index.js';
|
|
3
|
+
import type { ComboboxAsyncProps } from '../../types.js';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { SELECTION_TYPE } from '../../../../internal/constants/selection-type.js';
|
|
6
|
+
import { boolAttr } from '../../../../internal/utils/attrs.js';
|
|
7
|
+
import { cn, noop } from '../../../../internal/utils/common.js';
|
|
7
8
|
import { Debounced, watch } from 'runed';
|
|
8
|
-
import
|
|
9
|
-
import { onMountEffect } from 'svelte-toolbelt';
|
|
9
|
+
import { afterTick, boxWith, onMountEffect } from 'svelte-toolbelt';
|
|
10
10
|
import type { Attachment } from 'svelte/attachments';
|
|
11
11
|
import { cubicInOut } from 'svelte/easing';
|
|
12
12
|
import { fly } from 'svelte/transition';
|
|
13
|
-
import { CommandApi, CommandPrimitive } from '
|
|
14
|
-
import { Icon, Icon as IconComponent } from '
|
|
15
|
-
import { Kbd } from '
|
|
16
|
-
import { PopoverPrimitive } from '
|
|
17
|
-
import { Spinner } from '
|
|
18
|
-
import { comboboxTriggerVariants } from '
|
|
13
|
+
import { CommandApi, CommandPrimitive } from '../../../command/index.js';
|
|
14
|
+
import { Icon, Icon as IconComponent } from '../../../icons/index.js';
|
|
15
|
+
import { Kbd } from '../../../kbd/index.js';
|
|
16
|
+
import { PopoverPrimitive } from '../../../popover/index.js';
|
|
17
|
+
import { Spinner } from '../../../spinner/index.js';
|
|
18
|
+
import { comboboxTriggerVariants } from '../../styles.js';
|
|
19
|
+
import { useDisplayValue } from '../use-display-value.svelte.js';
|
|
20
|
+
import { useGroupedItems } from '../use-grouped-items.svelte.js';
|
|
21
|
+
import { useSelectedItems } from '../use-selected-items.svelte.js';
|
|
19
22
|
|
|
20
23
|
let {
|
|
21
24
|
open = $bindable(false),
|
|
@@ -50,7 +53,7 @@
|
|
|
50
53
|
searchDebounce = 300,
|
|
51
54
|
dependsOn,
|
|
52
55
|
clearOnDependencyChange = true,
|
|
53
|
-
onSelect,
|
|
56
|
+
onSelect = noop,
|
|
54
57
|
|
|
55
58
|
...restProps
|
|
56
59
|
}: ComboboxAsyncProps = $props();
|
|
@@ -59,9 +62,18 @@
|
|
|
59
62
|
|
|
60
63
|
// Initialize value based on type
|
|
61
64
|
if (value === undefined) {
|
|
62
|
-
|
|
65
|
+
const defaultValue = type === SELECTION_TYPE.SINGLE ? '' : [];
|
|
66
|
+
value = defaultValue;
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
watch.pre(
|
|
70
|
+
() => value,
|
|
71
|
+
() => {
|
|
72
|
+
if (value !== undefined) return;
|
|
73
|
+
value = type === SELECTION_TYPE.SINGLE ? '' : [];
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
65
77
|
// let items = $state<CommandItem[]>(initialItems);
|
|
66
78
|
let isLoading = $state(false);
|
|
67
79
|
let isSearching = $state(false);
|
|
@@ -91,45 +103,27 @@
|
|
|
91
103
|
};
|
|
92
104
|
|
|
93
105
|
const collection = $derived(createCollection(items));
|
|
94
|
-
const groupMap = $derived(new Map(collection.groups.map((g) => [g.value, g])));
|
|
95
|
-
const groupedItems = $derived(collection.group());
|
|
96
|
-
|
|
97
|
-
const isSelected = (itemValue: string): boolean => {
|
|
98
|
-
if (Array.isArray(value)) return value.includes(itemValue);
|
|
99
|
-
return value === itemValue;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const displayValue = $derived.by(() => {
|
|
103
|
-
if (typeof value === 'string' && value.trim() !== '') {
|
|
104
|
-
const item = items.find((i) => i.value === value);
|
|
105
|
-
return item?.label ?? value;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
109
|
-
return value
|
|
110
|
-
.map((v) => {
|
|
111
|
-
const item = items.find((i) => i.value === v);
|
|
112
|
-
return item?.label ?? v;
|
|
113
|
-
})
|
|
114
|
-
.join(', ');
|
|
115
|
-
}
|
|
116
106
|
|
|
117
|
-
|
|
107
|
+
const groupedItems = useGroupedItems({
|
|
108
|
+
collection: boxWith(() => collection),
|
|
118
109
|
});
|
|
119
110
|
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
127
|
-
return value.map((v) => items.find((i) => i.value === v)).filter((i): i is CommandItem => i !== undefined);
|
|
128
|
-
}
|
|
111
|
+
const displayValue = useDisplayValue({
|
|
112
|
+
value: boxWith(() => value!),
|
|
113
|
+
collection: boxWith(() => collection),
|
|
114
|
+
placeholder: boxWith(() => placeholder),
|
|
115
|
+
});
|
|
129
116
|
|
|
130
|
-
|
|
117
|
+
const selectedItems = useSelectedItems({
|
|
118
|
+
value: boxWith(() => value!),
|
|
119
|
+
collection: boxWith(() => collection),
|
|
131
120
|
});
|
|
132
121
|
|
|
122
|
+
const isSelected = (itemValue: string): boolean => {
|
|
123
|
+
if (Array.isArray(value)) return value.includes(itemValue);
|
|
124
|
+
return value === itemValue;
|
|
125
|
+
};
|
|
126
|
+
|
|
133
127
|
const isPlaceholder = $derived.by(() => {
|
|
134
128
|
if (Array.isArray(value)) return value.length === 0;
|
|
135
129
|
return typeof value === 'string' && value.trim() === '';
|
|
@@ -237,34 +231,6 @@
|
|
|
237
231
|
}
|
|
238
232
|
);
|
|
239
233
|
|
|
240
|
-
// onSelect callback
|
|
241
|
-
let previousValue = $state<string | string[]>(
|
|
242
|
-
isMultiple ? [...((value as string[]) ?? [])] : ((value as string) ?? '')
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
watch(
|
|
246
|
-
() => value,
|
|
247
|
-
(newValue) => {
|
|
248
|
-
if (!onSelect) {
|
|
249
|
-
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (typeof newValue === 'string') {
|
|
254
|
-
const item = newValue ? (items.find((i) => i.value === newValue) ?? null) : null;
|
|
255
|
-
onSelect(newValue, item);
|
|
256
|
-
} else if (Array.isArray(newValue)) {
|
|
257
|
-
const prev = Array.isArray(previousValue) ? previousValue : [];
|
|
258
|
-
const added = newValue.find((v) => !prev.includes(v));
|
|
259
|
-
const item = added ? (items.find((i) => i.value === added) ?? null) : null;
|
|
260
|
-
onSelect(newValue, item);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
264
|
-
},
|
|
265
|
-
{ lazy: true }
|
|
266
|
-
);
|
|
267
|
-
|
|
268
234
|
const loadMore = () => {
|
|
269
235
|
if (!isLoading && hasMore) {
|
|
270
236
|
fetchData(currentPage + 1, searchValue, true);
|
|
@@ -301,30 +267,35 @@
|
|
|
301
267
|
behavior: 'smooth',
|
|
302
268
|
});
|
|
303
269
|
};
|
|
304
|
-
|
|
305
|
-
const onSelectItem = (item: Omit<CommandItem, 'onSelect'>, itemOnSelect?: AnyFn) => () => {
|
|
306
|
-
itemOnSelect?.();
|
|
307
|
-
|
|
270
|
+
function onSelectItem(item: Omit<CommandItem, 'onSelect'>) {
|
|
308
271
|
if (Array.isArray(value)) {
|
|
272
|
+
let result = value;
|
|
309
273
|
if (value.includes(item.value)) {
|
|
310
|
-
|
|
274
|
+
result = allowDeselect ? value.filter((v) => v !== item.value) : value;
|
|
311
275
|
} else {
|
|
312
|
-
|
|
276
|
+
result = [...value, item.value];
|
|
313
277
|
}
|
|
278
|
+
onSelect(result, item);
|
|
279
|
+
value = result;
|
|
314
280
|
} else {
|
|
281
|
+
let result = value;
|
|
315
282
|
if (item.value === value) {
|
|
316
|
-
|
|
283
|
+
result = allowDeselect ? '' : value;
|
|
317
284
|
} else {
|
|
318
|
-
|
|
285
|
+
result = item.value;
|
|
319
286
|
}
|
|
287
|
+
onSelect(result, item);
|
|
288
|
+
value = result;
|
|
320
289
|
}
|
|
321
290
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
open = false;
|
|
325
|
-
}
|
|
326
|
-
};
|
|
291
|
+
afterTick(() => {
|
|
292
|
+
const shouldClose = closeOnSelect ?? !isMultiple;
|
|
327
293
|
|
|
294
|
+
if (shouldClose) {
|
|
295
|
+
open = false;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
}
|
|
328
299
|
const isNotEmpty = $derived.by(() => {
|
|
329
300
|
if (Array.isArray(value)) return value.length > 0;
|
|
330
301
|
|
|
@@ -355,9 +326,9 @@
|
|
|
355
326
|
{#if labelSnippet}
|
|
356
327
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
357
328
|
{@render labelSnippet({
|
|
358
|
-
placeholder: displayValue,
|
|
329
|
+
placeholder: displayValue.current,
|
|
359
330
|
value: isMultiple ? (value as string[]) : (value as string),
|
|
360
|
-
selectedItems,
|
|
331
|
+
selectedItems: selectedItems.current,
|
|
361
332
|
})}
|
|
362
333
|
|
|
363
334
|
{@render clearBtn()}
|
|
@@ -365,12 +336,12 @@
|
|
|
365
336
|
{:else if triggerSnippet}
|
|
366
337
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
367
338
|
{#snippet child({ props })}
|
|
368
|
-
{@render triggerSnippet?.({ props, displayValue })}
|
|
339
|
+
{@render triggerSnippet?.({ props, displayValue: displayValue.current })}
|
|
369
340
|
{/snippet}
|
|
370
341
|
</PopoverPrimitive.Trigger>
|
|
371
342
|
{:else}
|
|
372
343
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
373
|
-
{displayValue}
|
|
344
|
+
{displayValue.current}
|
|
374
345
|
|
|
375
346
|
{@render clearBtn()}
|
|
376
347
|
</PopoverPrimitive.Trigger>
|
|
@@ -378,22 +349,21 @@
|
|
|
378
349
|
{/snippet}
|
|
379
350
|
|
|
380
351
|
{#snippet renderItem(item: CommandItem)}
|
|
381
|
-
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect:
|
|
352
|
+
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect: _, ...restItem } = item}
|
|
382
353
|
{@const itemAttrs = {
|
|
383
354
|
value: itemValue,
|
|
384
|
-
onSelect: onSelectItem(item
|
|
385
|
-
'data-selected': boolAttr(isSelected(itemValue)),
|
|
355
|
+
onSelect: () => onSelectItem(item),
|
|
386
356
|
...restItem,
|
|
387
357
|
}}
|
|
388
358
|
|
|
389
359
|
{#if itemSnippet}
|
|
390
|
-
<CommandPrimitive.Item {...itemAttrs}>
|
|
360
|
+
<CommandPrimitive.Item {...itemAttrs} data-selected={boolAttr(isSelected(itemValue))}>
|
|
391
361
|
{#snippet child({ props })}
|
|
392
362
|
{@render itemSnippet?.({ item, props })}
|
|
393
363
|
{/snippet}
|
|
394
364
|
</CommandPrimitive.Item>
|
|
395
365
|
{:else}
|
|
396
|
-
<CommandPrimitive.Item {...itemAttrs}>
|
|
366
|
+
<CommandPrimitive.Item {...itemAttrs} data-selected={boolAttr(isSelected(itemValue))}>
|
|
397
367
|
{#if Icon}
|
|
398
368
|
<Icon width={16} height={16} />
|
|
399
369
|
{/if}
|
|
@@ -457,9 +427,9 @@
|
|
|
457
427
|
{@render loadingSpinner()}
|
|
458
428
|
{/if}
|
|
459
429
|
{:else if hasResults}
|
|
460
|
-
{#each groupedItems as [groupKey, groupItems] (groupKey || '__ungrouped__')}
|
|
430
|
+
{#each groupedItems.items.current as [groupKey, groupItems] (groupKey || '__ungrouped__')}
|
|
461
431
|
{#if groupKey}
|
|
462
|
-
{@const group =
|
|
432
|
+
{@const group = groupedItems.map.current.get(groupKey)}
|
|
463
433
|
<CommandPrimitive.Group value={group?.value}>
|
|
464
434
|
<CommandPrimitive.GroupHeading>{group?.label ?? groupKey}</CommandPrimitive.GroupHeading>
|
|
465
435
|
|
|
@@ -474,9 +444,11 @@
|
|
|
474
444
|
{/if}
|
|
475
445
|
</CommandPrimitive.Group>
|
|
476
446
|
{:else}
|
|
477
|
-
|
|
478
|
-
{
|
|
479
|
-
|
|
447
|
+
<CommandPrimitive.Group value="__ungrouped__">
|
|
448
|
+
{#each groupItems as item (item.value)}
|
|
449
|
+
{@render renderItem(item)}
|
|
450
|
+
{/each}
|
|
451
|
+
</CommandPrimitive.Group>
|
|
480
452
|
{/if}
|
|
481
453
|
{/each}
|
|
482
454
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComboboxAsyncProps } from '
|
|
1
|
+
import type { ComboboxAsyncProps } from '../../types.js';
|
|
2
2
|
declare const Combobox: import("svelte").Component<ComboboxAsyncProps, {}, "value" | "open" | "items" | "searchValue">;
|
|
3
3
|
type Combobox = ReturnType<typeof Combobox>;
|
|
4
4
|
export default Combobox;
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { SELECTION_TYPE } from '../../../../internal/constants/selection-type.js';
|
|
3
|
+
import { boolAttr } from '../../../../internal/utils/attrs.js';
|
|
4
|
+
import { cn, noop } from '../../../../internal/utils/common.js';
|
|
4
5
|
import { watch } from 'runed';
|
|
5
|
-
import
|
|
6
|
+
import { afterTick, boxWith } from 'svelte-toolbelt';
|
|
6
7
|
import { cubicInOut } from 'svelte/easing';
|
|
7
8
|
import { fly } from 'svelte/transition';
|
|
8
|
-
import { CommandPrimitive, type CommandItem } from '
|
|
9
|
-
import { Icon, Icon as IconComponent } from '
|
|
10
|
-
import { Kbd } from '
|
|
11
|
-
import { PopoverPrimitive } from '
|
|
12
|
-
import { comboboxTriggerVariants } from '
|
|
13
|
-
import type { ComboboxProps } from '
|
|
9
|
+
import { CommandPrimitive, type CommandItem } from '../../../command/index.js';
|
|
10
|
+
import { Icon, Icon as IconComponent } from '../../../icons/index.js';
|
|
11
|
+
import { Kbd } from '../../../kbd/index.js';
|
|
12
|
+
import { PopoverPrimitive } from '../../../popover/index.js';
|
|
13
|
+
import { comboboxTriggerVariants } from '../../styles.js';
|
|
14
|
+
import type { ComboboxProps } from '../../types.js';
|
|
15
|
+
import { useDisplayValue } from '../use-display-value.svelte.js';
|
|
16
|
+
import { useGroupedItems } from '../use-grouped-items.svelte.js';
|
|
17
|
+
import { useSelectedItems } from '../use-selected-items.svelte.js';
|
|
14
18
|
|
|
15
19
|
let {
|
|
16
20
|
open = $bindable(false),
|
|
@@ -34,57 +38,38 @@
|
|
|
34
38
|
fullWidth = true,
|
|
35
39
|
closeOnSelect,
|
|
36
40
|
maxContentHeight,
|
|
37
|
-
onSelect,
|
|
41
|
+
onSelect = noop,
|
|
38
42
|
...restProps
|
|
39
43
|
}: ComboboxProps = $props();
|
|
40
44
|
|
|
41
|
-
const isMultiple = $derived(type ===
|
|
45
|
+
const isMultiple = $derived.by(() => type === SELECTION_TYPE.MULTIPLE);
|
|
42
46
|
|
|
43
|
-
// Initialize value based on type
|
|
44
47
|
if (value === undefined) {
|
|
45
|
-
|
|
48
|
+
const defaultValue = type === SELECTION_TYPE.SINGLE ? '' : [];
|
|
49
|
+
value = defaultValue;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (Array.isArray(value)) return value.includes(itemValue);
|
|
54
|
-
return value === itemValue;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const displayValue = $derived.by(() => {
|
|
58
|
-
if (typeof value === 'string' && value.trim() !== '') {
|
|
59
|
-
const item = collection.items.find((i) => i.value === value);
|
|
60
|
-
return item?.label ?? value;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
64
|
-
return value
|
|
65
|
-
.map((v) => {
|
|
66
|
-
const item = collection.items.find((i) => i.value === v);
|
|
67
|
-
return item?.label ?? v;
|
|
68
|
-
})
|
|
69
|
-
.join(', ');
|
|
52
|
+
watch.pre(
|
|
53
|
+
() => value,
|
|
54
|
+
() => {
|
|
55
|
+
if (value !== undefined) return;
|
|
56
|
+
value = type === SELECTION_TYPE.SINGLE ? '' : [];
|
|
70
57
|
}
|
|
58
|
+
);
|
|
71
59
|
|
|
72
|
-
|
|
60
|
+
const groupedItems = useGroupedItems({
|
|
61
|
+
collection: boxWith(() => collection),
|
|
73
62
|
});
|
|
74
63
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
82
|
-
return value
|
|
83
|
-
.map((v) => collection.items.find((i) => i.value === v))
|
|
84
|
-
.filter((i): i is CommandItem => i !== undefined);
|
|
85
|
-
}
|
|
64
|
+
const displayValue = useDisplayValue({
|
|
65
|
+
value: boxWith(() => value!),
|
|
66
|
+
collection: boxWith(() => collection),
|
|
67
|
+
placeholder: boxWith(() => placeholder),
|
|
68
|
+
});
|
|
86
69
|
|
|
87
|
-
|
|
70
|
+
const selectedItems = useSelectedItems({
|
|
71
|
+
value: boxWith(() => value!),
|
|
72
|
+
collection: boxWith(() => collection),
|
|
88
73
|
});
|
|
89
74
|
|
|
90
75
|
const isPlaceholder = $derived.by(() => {
|
|
@@ -92,61 +77,46 @@
|
|
|
92
77
|
return typeof value === 'string' && value.trim() === '';
|
|
93
78
|
});
|
|
94
79
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
watch(
|
|
100
|
-
() => value,
|
|
101
|
-
(newValue) => {
|
|
102
|
-
if (!onSelect) {
|
|
103
|
-
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (typeof newValue === 'string') {
|
|
108
|
-
const item = newValue ? (collection.items.find((i) => i.value === newValue) ?? null) : null;
|
|
109
|
-
onSelect(newValue, item);
|
|
110
|
-
} else if (Array.isArray(newValue)) {
|
|
111
|
-
const prev = Array.isArray(previousValue) ? previousValue : [];
|
|
112
|
-
const added = newValue.find((v) => !prev.includes(v));
|
|
113
|
-
const item = added ? (collection.items.find((i) => i.value === added) ?? null) : null;
|
|
114
|
-
onSelect(newValue, item);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
previousValue = isMultiple ? [...((newValue as string[]) ?? [])] : ((newValue as string) ?? '');
|
|
118
|
-
},
|
|
119
|
-
{ lazy: true }
|
|
120
|
-
);
|
|
80
|
+
const isNotEmpty = $derived.by(() => {
|
|
81
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
121
82
|
|
|
122
|
-
|
|
123
|
-
|
|
83
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
84
|
+
});
|
|
124
85
|
|
|
86
|
+
function onSelectItem(item: Omit<CommandItem, 'onSelect'>) {
|
|
125
87
|
if (Array.isArray(value)) {
|
|
88
|
+
let result = value;
|
|
126
89
|
if (value.includes(item.value)) {
|
|
127
|
-
|
|
90
|
+
result = allowDeselect ? value.filter((v) => v !== item.value) : value;
|
|
128
91
|
} else {
|
|
129
|
-
|
|
92
|
+
result = [...value, item.value];
|
|
130
93
|
}
|
|
94
|
+
onSelect(result, item);
|
|
95
|
+
value = result;
|
|
131
96
|
} else {
|
|
97
|
+
let result = value;
|
|
132
98
|
if (item.value === value) {
|
|
133
|
-
|
|
99
|
+
result = allowDeselect ? '' : value;
|
|
134
100
|
} else {
|
|
135
|
-
|
|
101
|
+
result = item.value;
|
|
136
102
|
}
|
|
103
|
+
onSelect(result, item);
|
|
104
|
+
value = result;
|
|
137
105
|
}
|
|
138
106
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
open = false;
|
|
142
|
-
}
|
|
143
|
-
};
|
|
107
|
+
afterTick(() => {
|
|
108
|
+
const shouldClose = closeOnSelect ?? !isMultiple;
|
|
144
109
|
|
|
145
|
-
|
|
146
|
-
|
|
110
|
+
if (shouldClose) {
|
|
111
|
+
open = false;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
147
115
|
|
|
148
|
-
|
|
149
|
-
|
|
116
|
+
function isSelected(itemValue: string): boolean {
|
|
117
|
+
if (Array.isArray(value)) return value.includes(itemValue);
|
|
118
|
+
return value === itemValue;
|
|
119
|
+
}
|
|
150
120
|
</script>
|
|
151
121
|
|
|
152
122
|
{#snippet clearBtn()}
|
|
@@ -168,22 +138,27 @@
|
|
|
168
138
|
{@const triggerProps = {
|
|
169
139
|
class: cn(comboboxTriggerVariants({ variant, size, rounded, fullWidth, class: triggerClass })),
|
|
170
140
|
'data-placeholder': boolAttr(isPlaceholder),
|
|
141
|
+
'aria-expanded': open,
|
|
171
142
|
}}
|
|
172
143
|
{#if labelSnippet}
|
|
173
144
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
174
|
-
{@render labelSnippet({
|
|
145
|
+
{@render labelSnippet({
|
|
146
|
+
placeholder: displayValue.current,
|
|
147
|
+
value: isMultiple ? (value as string[]) : (value as string),
|
|
148
|
+
selectedItems: selectedItems.current,
|
|
149
|
+
})}
|
|
175
150
|
|
|
176
151
|
{@render clearBtn()}
|
|
177
152
|
</PopoverPrimitive.Trigger>
|
|
178
153
|
{:else if triggerSnippet}
|
|
179
154
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
180
155
|
{#snippet child({ props })}
|
|
181
|
-
{@render triggerSnippet?.({ props, displayValue })}
|
|
156
|
+
{@render triggerSnippet?.({ props, displayValue: displayValue.current })}
|
|
182
157
|
{/snippet}
|
|
183
158
|
</PopoverPrimitive.Trigger>
|
|
184
159
|
{:else}
|
|
185
160
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
186
|
-
{displayValue}
|
|
161
|
+
{displayValue.current}
|
|
187
162
|
|
|
188
163
|
{@render clearBtn()}
|
|
189
164
|
</PopoverPrimitive.Trigger>
|
|
@@ -191,22 +166,21 @@
|
|
|
191
166
|
{/snippet}
|
|
192
167
|
|
|
193
168
|
{#snippet renderItem(item: CommandItem)}
|
|
194
|
-
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect:
|
|
169
|
+
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect: _, ...restItem } = item}
|
|
195
170
|
{@const itemAttrs = {
|
|
196
171
|
value: itemValue,
|
|
197
|
-
onSelect: onSelectItem(item
|
|
198
|
-
'data-selected': boolAttr(isSelected(itemValue)),
|
|
172
|
+
onSelect: () => onSelectItem(item),
|
|
199
173
|
...restItem,
|
|
200
174
|
}}
|
|
201
175
|
|
|
202
176
|
{#if itemSnippet}
|
|
203
|
-
<CommandPrimitive.Item {...itemAttrs}>
|
|
177
|
+
<CommandPrimitive.Item {...itemAttrs} data-selected={boolAttr(isSelected(itemValue))}>
|
|
204
178
|
{#snippet child({ props })}
|
|
205
179
|
{@render itemSnippet?.({ item, props })}
|
|
206
180
|
{/snippet}
|
|
207
181
|
</CommandPrimitive.Item>
|
|
208
182
|
{:else}
|
|
209
|
-
<CommandPrimitive.Item {...itemAttrs}>
|
|
183
|
+
<CommandPrimitive.Item {...itemAttrs} data-selected={boolAttr(isSelected(itemValue))}>
|
|
210
184
|
{#if Icon}
|
|
211
185
|
<Icon width={16} height={16} />
|
|
212
186
|
{/if}
|
|
@@ -242,9 +216,9 @@
|
|
|
242
216
|
<CommandPrimitive.Viewport>
|
|
243
217
|
<CommandPrimitive.Empty>No results found</CommandPrimitive.Empty>
|
|
244
218
|
|
|
245
|
-
{#each groupedItems as [groupKey, items] (groupKey || '__ungrouped__')}
|
|
219
|
+
{#each groupedItems.items.current as [groupKey, items] (groupKey || '__ungrouped__')}
|
|
246
220
|
{#if groupKey}
|
|
247
|
-
{@const group =
|
|
221
|
+
{@const group = groupedItems.map.current.get(groupKey)}
|
|
248
222
|
<CommandPrimitive.Group value={group?.value}>
|
|
249
223
|
<CommandPrimitive.GroupHeading>{group?.label ?? groupKey}</CommandPrimitive.GroupHeading>
|
|
250
224
|
|
|
@@ -259,9 +233,11 @@
|
|
|
259
233
|
{/if}
|
|
260
234
|
</CommandPrimitive.Group>
|
|
261
235
|
{:else}
|
|
262
|
-
|
|
263
|
-
{
|
|
264
|
-
|
|
236
|
+
<CommandPrimitive.Group value="__ungrouped__">
|
|
237
|
+
{#each items as item (item.value)}
|
|
238
|
+
{@render renderItem(item)}
|
|
239
|
+
{/each}
|
|
240
|
+
</CommandPrimitive.Group>
|
|
265
241
|
{/if}
|
|
266
242
|
{/each}
|
|
267
243
|
</CommandPrimitive.Viewport>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComboboxProps } from '
|
|
1
|
+
import type { ComboboxProps } from '../../types.js';
|
|
2
2
|
declare const Combobox: import("svelte").Component<ComboboxProps, {}, "value" | "open" | "searchValue">;
|
|
3
3
|
type Combobox = ReturnType<typeof Combobox>;
|
|
4
4
|
export default Combobox;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Root } from './combobox.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Root } from './combobox.svelte';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommandCollection } from '../../command/types.js';
|
|
2
|
+
import { type ReadableBoxedValues } from 'svelte-toolbelt';
|
|
3
|
+
type Opts = ReadableBoxedValues<{
|
|
4
|
+
value: string | string[];
|
|
5
|
+
collection: CommandCollection;
|
|
6
|
+
placeholder: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const useDisplayValue: (opts: Opts) => import("svelte-toolbelt").ReadableBox<string>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { boxWith } from 'svelte-toolbelt';
|
|
2
|
+
export const useDisplayValue = (opts) => {
|
|
3
|
+
const res = $derived.by(() => {
|
|
4
|
+
const { value, collection, placeholder } = opts;
|
|
5
|
+
if (typeof value.current === 'string' && value.current.trim() !== '') {
|
|
6
|
+
const item = collection.current.items.find((i) => i.value === value.current);
|
|
7
|
+
return item?.label ?? value.current;
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(value.current) && value.current.length > 0) {
|
|
10
|
+
return value.current
|
|
11
|
+
.map((v) => {
|
|
12
|
+
const item = collection.current.items.find((i) => i.value === v);
|
|
13
|
+
return item?.label ?? v;
|
|
14
|
+
})
|
|
15
|
+
.join(', ');
|
|
16
|
+
}
|
|
17
|
+
return placeholder?.current;
|
|
18
|
+
});
|
|
19
|
+
return boxWith(() => res);
|
|
20
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CommandCollection } from '../../command/types.js';
|
|
2
|
+
import { type ReadableBoxedValues } from 'svelte-toolbelt';
|
|
3
|
+
type Opts = ReadableBoxedValues<{
|
|
4
|
+
collection: CommandCollection;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const useGroupedItems: (opts: Opts) => {
|
|
7
|
+
map: import("svelte-toolbelt").ReadableBox<Map<string, import("../../command/types.js").CommandGroup>>;
|
|
8
|
+
items: import("svelte-toolbelt").ReadableBox<[string, import("../../command/types.js").CommandItem[]][]>;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { boxWith } from 'svelte-toolbelt';
|
|
2
|
+
export const useGroupedItems = (opts) => {
|
|
3
|
+
const { collection } = opts;
|
|
4
|
+
const groupedItems = $derived.by(() => collection.current.group());
|
|
5
|
+
const groupedMap = $derived.by(() => new Map(collection.current.groups.map((g) => [g.value, g])));
|
|
6
|
+
return {
|
|
7
|
+
map: boxWith(() => groupedMap),
|
|
8
|
+
items: boxWith(() => groupedItems),
|
|
9
|
+
};
|
|
10
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CommandCollection, CommandItem } from '../../command/types.js';
|
|
2
|
+
import { type ReadableBoxedValues } from 'svelte-toolbelt';
|
|
3
|
+
type Opts = ReadableBoxedValues<{
|
|
4
|
+
value: string | string[];
|
|
5
|
+
collection: CommandCollection;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const useSelectedItems: (opts: Opts) => import("svelte-toolbelt").ReadableBox<CommandItem[]>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { boxWith } from 'svelte-toolbelt';
|
|
2
|
+
export const useSelectedItems = (opts) => {
|
|
3
|
+
const { value, collection } = opts;
|
|
4
|
+
const res = $derived.by(() => {
|
|
5
|
+
if (typeof value.current === 'string' && value.current.trim() !== '') {
|
|
6
|
+
const item = collection.current.items.find((i) => i.value === value.current);
|
|
7
|
+
return item ? [item] : [];
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(value.current) && value.current.length > 0) {
|
|
10
|
+
return value.current
|
|
11
|
+
.map((v) => collection.current.items.find((i) => i.value === v))
|
|
12
|
+
.filter((i) => i !== undefined);
|
|
13
|
+
}
|
|
14
|
+
return [];
|
|
15
|
+
});
|
|
16
|
+
return boxWith(() => res);
|
|
17
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SELECTION_TYPE } from '../../internal/constants/selection-type.js';
|
|
1
2
|
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { CommandGroup, CommandItem, CommandProps } from '../command/index.js';
|
|
3
4
|
import type { PrimitivePopoverRootProps } from '../popover/types.js';
|
|
@@ -33,13 +34,11 @@ type ComboboxBaseProps = PrimitivePopoverRootProps & ComboboxTriggerVariantsProp
|
|
|
33
34
|
onSelect?: (value: string | string[], item: CommandItem | null) => void;
|
|
34
35
|
};
|
|
35
36
|
type ComboboxSingleProps = ComboboxBaseProps & {
|
|
36
|
-
|
|
37
|
-
type?: 'single';
|
|
37
|
+
type: typeof SELECTION_TYPE.SINGLE;
|
|
38
38
|
value?: string;
|
|
39
39
|
};
|
|
40
40
|
type ComboboxMultipleProps = ComboboxBaseProps & {
|
|
41
|
-
|
|
42
|
-
type: 'multiple';
|
|
41
|
+
type: typeof SELECTION_TYPE.MULTIPLE;
|
|
43
42
|
value?: string[];
|
|
44
43
|
};
|
|
45
44
|
export type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps;
|
|
@@ -57,6 +57,7 @@ export const commandItemVariants = tv({
|
|
|
57
57
|
base: [
|
|
58
58
|
'cgui:flex cgui:items-center cgui:gap-2 cgui:px-2 cgui:py-1.5',
|
|
59
59
|
'cgui:aria-selected:bg-surface-lightest cgui:aria-selected:text-fg-primary',
|
|
60
|
+
'cgui:data-selected:text-fg-primary',
|
|
60
61
|
'cgui:cursor-default cgui:text-body cgui:select-none cgui:outline-hidden cgui:rounded-xs',
|
|
61
62
|
'cgui:transition-all cgui:duration-250 cgui:ease-in-out',
|
|
62
63
|
'cgui:[&_svg:not([class*="text-"])]:text-muted-foreground cgui:relative',
|
|
@@ -111,9 +111,7 @@
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
if (Array.isArray(value) && value.length > 0) {
|
|
114
|
-
return value
|
|
115
|
-
.map((v) => collection.find(v))
|
|
116
|
-
.filter((i): i is SelectItem => i !== undefined);
|
|
114
|
+
return value.map((v) => collection.find(v)).filter((i): i is SelectItem => i !== undefined);
|
|
117
115
|
}
|
|
118
116
|
|
|
119
117
|
return [];
|
|
@@ -322,12 +320,6 @@
|
|
|
322
320
|
</div>
|
|
323
321
|
{/snippet}
|
|
324
322
|
|
|
325
|
-
{#if hasMore}
|
|
326
|
-
{#key items.length}
|
|
327
|
-
<div class="cgui:h-1 cgui:w-full" {@attach anchorAttachment}></div>
|
|
328
|
-
{/key}
|
|
329
|
-
{/if}
|
|
330
|
-
|
|
331
323
|
{#snippet loadingMore()}
|
|
332
324
|
<div class="cgui:p-2 cgui:flex cgui:items-center cgui:justify-center cgui:text-icon-default">
|
|
333
325
|
<Spinner />
|
|
@@ -395,6 +387,12 @@
|
|
|
395
387
|
{@render loadingMore()}
|
|
396
388
|
{/if}
|
|
397
389
|
|
|
390
|
+
{#if hasMore}
|
|
391
|
+
{#key items.length}
|
|
392
|
+
<div class="cgui:h-1 cgui:w-full" {@attach anchorAttachment}></div>
|
|
393
|
+
{/key}
|
|
394
|
+
{/if}
|
|
395
|
+
|
|
398
396
|
{#if !hasMore && items.length > pageSize}
|
|
399
397
|
<button
|
|
400
398
|
class="cgui:w-full cgui:flex cgui:justify-center cgui:cursor-pointer cgui:items-center cgui:text-xs cgui:text-icon-default cgui:underline cgui:hover:no-underline"
|