@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.
Files changed (25) hide show
  1. package/dist/assets/css/root.css +13 -8
  2. package/dist/components/combobox/index.d.ts +1 -1
  3. package/dist/components/combobox/index.js +1 -1
  4. package/dist/components/combobox/{combobox.async.svelte → presets/async/combobox.async.svelte} +74 -102
  5. package/dist/components/combobox/{combobox.async.svelte.d.ts → presets/async/combobox.async.svelte.d.ts} +1 -1
  6. package/dist/components/combobox/{exports.d.ts → presets/async/index.d.ts} +0 -1
  7. package/dist/components/combobox/{exports.js → presets/async/index.js} +0 -1
  8. package/dist/components/combobox/presets/index.d.ts +2 -0
  9. package/dist/components/combobox/presets/index.js +2 -0
  10. package/dist/components/combobox/{combobox.svelte → presets/root/combobox.svelte} +78 -102
  11. package/dist/components/combobox/{combobox.svelte.d.ts → presets/root/combobox.svelte.d.ts} +1 -1
  12. package/dist/components/combobox/presets/root/index.d.ts +1 -0
  13. package/dist/components/combobox/presets/root/index.js +1 -0
  14. package/dist/components/combobox/presets/use-display-value.svelte.d.ts +9 -0
  15. package/dist/components/combobox/presets/use-display-value.svelte.js +20 -0
  16. package/dist/components/combobox/presets/use-grouped-items.svelte.d.ts +10 -0
  17. package/dist/components/combobox/presets/use-grouped-items.svelte.js +10 -0
  18. package/dist/components/combobox/presets/use-selected-items.svelte.d.ts +8 -0
  19. package/dist/components/combobox/presets/use-selected-items.svelte.js +17 -0
  20. package/dist/components/combobox/types.d.ts +3 -4
  21. package/dist/components/command/styles.js +1 -0
  22. package/dist/components/select/select.async.svelte +7 -9
  23. package/dist/internal/constants/selection-type.d.ts +5 -0
  24. package/dist/internal/constants/selection-type.js +4 -0
  25. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
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
  }
@@ -1,3 +1,3 @@
1
- export * as Combobox from './exports.js';
1
+ export * as Combobox from './presets/index.js';
2
2
  export * as ComboboxApi from './api/index.js';
3
3
  export * from './types.js';
@@ -1,3 +1,3 @@
1
- export * as Combobox from './exports.js';
1
+ export * as Combobox from './presets/index.js';
2
2
  export * as ComboboxApi from './api/index.js';
3
3
  export * from './types.js';
@@ -1,21 +1,24 @@
1
1
  <script lang="ts">
2
- import type { CommandCollection, CommandItem } from '../command/index.js';
3
- import type { ComboboxAsyncProps } from './types.js';
2
+ import type { CommandCollection, CommandItem } from '../../../command/index.js';
3
+ import type { ComboboxAsyncProps } from '../../types.js';
4
4
 
5
- import { boolAttr } from '../../internal/utils/attrs.js';
6
- import { cn } from '../../internal/utils/common.js';
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 type { AnyFn } from 'svelte-toolbelt';
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 '../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';
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
- value = isMultiple ? ([] as string[]) : '';
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
- return placeholder;
107
+ const groupedItems = useGroupedItems({
108
+ collection: boxWith(() => collection),
118
109
  });
119
110
 
120
- const selectedItems = $derived.by(() => {
121
- if (typeof value === 'string' && value.trim() !== '') {
122
- const item = items.find((i) => i.value === value);
123
- return item ? [item] : [];
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
- return [];
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
- value = allowDeselect ? value.filter((v) => v !== item.value) : value;
274
+ result = allowDeselect ? value.filter((v) => v !== item.value) : value;
311
275
  } else {
312
- value = [...value, item.value];
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
- value = allowDeselect ? '' : value;
283
+ result = allowDeselect ? '' : value;
317
284
  } else {
318
- value = item.value;
285
+ result = item.value;
319
286
  }
287
+ onSelect(result, item);
288
+ value = result;
320
289
  }
321
290
 
322
- const shouldClose = closeOnSelect ?? !isMultiple;
323
- if (shouldClose) {
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: itemOnSelect, ...restItem } = item}
352
+ {@const { value: itemValue, label, icon: Icon, shortcut, onSelect: _, ...restItem } = item}
382
353
  {@const itemAttrs = {
383
354
  value: itemValue,
384
- onSelect: onSelectItem(item, itemOnSelect),
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 = groupMap.get(groupKey)}
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
- {#each groupItems as item (item.value)}
478
- {@render renderItem(item)}
479
- {/each}
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 './types.js';
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,2 +1 @@
1
1
  export { default as Async } from './combobox.async.svelte';
2
- export { default as Root } from './combobox.svelte';
@@ -1,2 +1 @@
1
1
  export { default as Async } from './combobox.async.svelte';
2
- export { default as Root } from './combobox.svelte';
@@ -0,0 +1,2 @@
1
+ export * from './async/index.js';
2
+ export * from './root/index.js';
@@ -0,0 +1,2 @@
1
+ export * from './async/index.js';
2
+ export * from './root/index.js';
@@ -1,16 +1,20 @@
1
1
  <script lang="ts">
2
- import { boolAttr } from '../../internal/utils/attrs.js';
3
- import { cn } from '../../internal/utils/common.js';
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 type { AnyFn } from 'svelte-toolbelt';
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 '../command/index.js';
9
- import { Icon, Icon as IconComponent } from '../icons/index.js';
10
- import { Kbd } from '../kbd/index.js';
11
- import { PopoverPrimitive } from '../popover/index.js';
12
- import { comboboxTriggerVariants } from './styles.js';
13
- import type { ComboboxProps } from './types.js';
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 === 'multiple');
45
+ const isMultiple = $derived.by(() => type === SELECTION_TYPE.MULTIPLE);
42
46
 
43
- // Initialize value based on type
44
47
  if (value === undefined) {
45
- value = isMultiple ? ([] as string[]) : '';
48
+ const defaultValue = type === SELECTION_TYPE.SINGLE ? '' : [];
49
+ value = defaultValue;
46
50
  }
47
51
 
48
- const groupMap = $derived(new Map(collection.groups.map((g) => [g.value, g])));
49
-
50
- const groupedItems = $derived(collection.group());
51
-
52
- const isSelected = (itemValue: string): boolean => {
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
- return placeholder;
60
+ const groupedItems = useGroupedItems({
61
+ collection: boxWith(() => collection),
73
62
  });
74
63
 
75
- const selectedItems = $derived.by(() => {
76
- if (typeof value === 'string' && value.trim() !== '') {
77
- const item = collection.items.find((i) => i.value === value);
78
- return item ? [item] : [];
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
- return [];
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
- let previousValue = $state<string | string[]>(
96
- isMultiple ? [...((value as string[]) ?? [])] : ((value as string) ?? '')
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
- const onSelectItem = (item: Omit<CommandItem, 'onSelect'>, itemOnSelect?: AnyFn) => () => {
123
- itemOnSelect?.();
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
- value = allowDeselect ? value.filter((v) => v !== item.value) : value;
90
+ result = allowDeselect ? value.filter((v) => v !== item.value) : value;
128
91
  } else {
129
- value = [...value, item.value];
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
- value = allowDeselect ? '' : value;
99
+ result = allowDeselect ? '' : value;
134
100
  } else {
135
- value = item.value;
101
+ result = item.value;
136
102
  }
103
+ onSelect(result, item);
104
+ value = result;
137
105
  }
138
106
 
139
- const shouldClose = closeOnSelect ?? !isMultiple;
140
- if (shouldClose) {
141
- open = false;
142
- }
143
- };
107
+ afterTick(() => {
108
+ const shouldClose = closeOnSelect ?? !isMultiple;
144
109
 
145
- const isNotEmpty = $derived.by(() => {
146
- if (Array.isArray(value)) return value.length > 0;
110
+ if (shouldClose) {
111
+ open = false;
112
+ }
113
+ });
114
+ }
147
115
 
148
- return typeof value === 'string' && value.trim() !== '';
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({ placeholder: displayValue, value: isMultiple ? (value as string[]) : (value as string), selectedItems })}
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: itemOnSelect, ...restItem } = item}
169
+ {@const { value: itemValue, label, icon: Icon, shortcut, onSelect: _, ...restItem } = item}
195
170
  {@const itemAttrs = {
196
171
  value: itemValue,
197
- onSelect: onSelectItem(item, itemOnSelect),
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 = groupMap.get(groupKey)}
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
- {#each items as item (item.value)}
263
- {@render renderItem(item)}
264
- {/each}
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 './types.js';
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
- /** Selection mode (default: 'single') */
37
- type?: 'single';
37
+ type: typeof SELECTION_TYPE.SINGLE;
38
38
  value?: string;
39
39
  };
40
40
  type ComboboxMultipleProps = ComboboxBaseProps & {
41
- /** Selection mode */
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"
@@ -0,0 +1,5 @@
1
+ export declare const SELECTION_TYPE: {
2
+ readonly SINGLE: "single";
3
+ readonly MULTIPLE: "multiple";
4
+ };
5
+ export type SelectionType = (typeof SELECTION_TYPE)[keyof typeof SELECTION_TYPE];
@@ -0,0 +1,4 @@
1
+ export const SELECTION_TYPE = {
2
+ SINGLE: 'single',
3
+ MULTIPLE: 'multiple',
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@casinogate/ui",
3
- "version": "1.10.15",
3
+ "version": "1.10.17",
4
4
  "svelte": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",