@casinogate/ui 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/assets/css/root.css +43 -4
  2. package/dist/components/breadcrumb/breadcrumb.svelte +2 -2
  3. package/dist/components/breadcrumb/types.d.ts +2 -2
  4. package/dist/components/button/styles.d.ts +15 -0
  5. package/dist/components/button/styles.js +9 -4
  6. package/dist/components/button-group/components/button-group.root.svelte +1 -3
  7. package/dist/components/collapsible/components/collapsaible.svelte.js +3 -3
  8. package/dist/components/data-table/data-table.svelte +1 -1
  9. package/dist/components/data-table/utils/pagination-state.svelte.js +3 -3
  10. package/dist/components/data-table/utils/resize-state.svelte.js +3 -3
  11. package/dist/components/data-table/utils/row-selection-state.svelte.js +3 -3
  12. package/dist/components/dialog/components/dialog.overlay.svelte +1 -7
  13. package/dist/components/dropdown/components/dropdown.content.svelte +0 -2
  14. package/dist/components/dropdown/dropdown.svelte +127 -67
  15. package/dist/components/dropdown/dropdown.svelte.d.ts +3 -2
  16. package/dist/components/dropdown/exports.d.ts +1 -1
  17. package/dist/components/dropdown/exports.js +1 -1
  18. package/dist/components/dropdown/index.d.ts +1 -1
  19. package/dist/components/dropdown/styles.js +5 -3
  20. package/dist/components/dropdown/types.d.ts +47 -23
  21. package/dist/components/field/field.svelte +0 -1
  22. package/dist/components/segment/components/segment.root.svelte +2 -1
  23. package/dist/components/segment/components/segmet.svelte.d.ts +0 -1
  24. package/dist/components/segment/components/segmet.svelte.js +0 -1
  25. package/dist/components/segment/styles.d.ts +18 -0
  26. package/dist/components/segment/styles.js +7 -1
  27. package/dist/components/select/components/select.content.svelte +2 -4
  28. package/dist/components/select/components/select.group-heading.svelte +2 -4
  29. package/dist/components/select/components/select.group-heading.svelte.d.ts +2 -2
  30. package/dist/components/select/components/select.group.svelte.d.ts +2 -2
  31. package/dist/components/select/components/select.item.svelte +5 -13
  32. package/dist/components/select/components/select.item.svelte.d.ts +2 -2
  33. package/dist/components/select/components/select.root.svelte +1 -17
  34. package/dist/components/select/components/select.root.svelte.d.ts +1 -2
  35. package/dist/components/select/components/select.trigger.svelte +17 -5
  36. package/dist/components/select/components/select.viewport.svelte +2 -4
  37. package/dist/components/select/components/select.viewport.svelte.d.ts +2 -2
  38. package/dist/components/select/exports.d.ts +1 -1
  39. package/dist/components/select/exports.js +1 -1
  40. package/dist/components/select/select.async.svelte +146 -83
  41. package/dist/components/select/select.async.svelte.d.ts +1 -1
  42. package/dist/components/select/select.svelte +107 -62
  43. package/dist/components/select/select.svelte.d.ts +4 -2
  44. package/dist/components/select/styles.d.ts +48 -139
  45. package/dist/components/select/styles.js +74 -101
  46. package/dist/components/select/types.d.ts +69 -29
  47. package/dist/components/select/types.js +0 -1
  48. package/dist/components/select/utils/get-item-key.d.ts +1 -1
  49. package/dist/internal/index.d.ts +1 -0
  50. package/dist/internal/index.js +1 -0
  51. package/dist/internal/lib/collection/grid-collection.d.ts +3 -0
  52. package/dist/internal/lib/collection/grid-collection.js +2 -0
  53. package/dist/internal/lib/collection/index.d.ts +5 -0
  54. package/dist/internal/lib/collection/index.js +5 -0
  55. package/dist/internal/lib/collection/list-collection.d.ts +3 -0
  56. package/dist/internal/lib/collection/list-collection.js +2 -0
  57. package/dist/internal/lib/collection/tree-collection.d.ts +4 -0
  58. package/dist/internal/lib/collection/tree-collection.js +3 -0
  59. package/dist/internal/lib/collection/use-list-collection.svelte.d.ts +88 -0
  60. package/dist/internal/lib/collection/use-list-collection.svelte.js +94 -0
  61. package/dist/internal/lib/collection/use-list-selection.svelte.d.ts +92 -0
  62. package/dist/internal/lib/collection/use-list-selection.svelte.js +80 -0
  63. package/dist/internal/lib/index.d.ts +1 -0
  64. package/dist/internal/lib/index.js +1 -0
  65. package/dist/internal/utils/equal.d.ts +3 -0
  66. package/dist/internal/utils/equal.js +56 -0
  67. package/dist/internal/utils/functions.d.ts +2 -0
  68. package/dist/internal/utils/functions.js +4 -0
  69. package/dist/internal/utils/guard.d.ts +5 -0
  70. package/dist/internal/utils/guard.js +5 -0
  71. package/package.json +5 -3
  72. package/dist/components/app-shell/app-shell.stories.svelte +0 -107
  73. package/dist/components/app-shell/app-shell.stories.svelte.d.ts +0 -18
  74. package/dist/components/badge/badge.stories.svelte +0 -81
  75. package/dist/components/badge/badge.stories.svelte.d.ts +0 -19
  76. package/dist/components/breadcrumb/breadcrumb.stories.svelte +0 -67
  77. package/dist/components/breadcrumb/breadcrumb.stories.svelte.d.ts +0 -4
  78. package/dist/components/button/button.stories.svelte +0 -106
  79. package/dist/components/button/button.stories.svelte.d.ts +0 -19
  80. package/dist/components/button-group/button-group.stories.svelte +0 -59
  81. package/dist/components/button-group/button-group.stories.svelte.d.ts +0 -18
  82. package/dist/components/checkbox/checkbox.stories.svelte +0 -52
  83. package/dist/components/checkbox/checkbox.stories.svelte.d.ts +0 -18
  84. package/dist/components/collapsible/collapsible.stories.svelte +0 -69
  85. package/dist/components/collapsible/collapsible.stories.svelte.d.ts +0 -18
  86. package/dist/components/data-table/data-table.stories.svelte +0 -327
  87. package/dist/components/data-table/data-table.stories.svelte.d.ts +0 -4
  88. package/dist/components/dialog/dialog.stories.svelte +0 -116
  89. package/dist/components/dialog/dialog.stories.svelte.d.ts +0 -3
  90. package/dist/components/dropdown/dropdown.stories.svelte +0 -151
  91. package/dist/components/dropdown/dropdown.stories.svelte.d.ts +0 -19
  92. package/dist/components/field/field.stories.svelte +0 -56
  93. package/dist/components/field/field.stories.svelte.d.ts +0 -19
  94. package/dist/components/input/input.stories.svelte +0 -41
  95. package/dist/components/input/input.stories.svelte.d.ts +0 -19
  96. package/dist/components/navigation/navigation.stories.svelte +0 -99
  97. package/dist/components/navigation/navigation.stories.svelte.d.ts +0 -19
  98. package/dist/components/pagination/pagination.stories.svelte +0 -69
  99. package/dist/components/pagination/pagination.stories.svelte.d.ts +0 -19
  100. package/dist/components/popover/popover.stories.svelte +0 -195
  101. package/dist/components/popover/popover.stories.svelte.d.ts +0 -3
  102. package/dist/components/segment/segment.stories.svelte +0 -57
  103. package/dist/components/segment/segment.stories.svelte.d.ts +0 -19
  104. package/dist/components/select/select.stories.svelte +0 -263
  105. package/dist/components/select/select.stories.svelte.d.ts +0 -4
  106. package/dist/components/separator/separator.stories.svelte +0 -44
  107. package/dist/components/separator/separator.stories.svelte.d.ts +0 -19
  108. package/dist/components/skeleton/skeleton.stories.svelte +0 -129
  109. package/dist/components/skeleton/skeleton.stories.svelte.d.ts +0 -19
  110. package/dist/components/spinner/spinner.stories.svelte +0 -29
  111. package/dist/components/spinner/spinner.stories.svelte.d.ts +0 -19
  112. package/dist/components/switch/switch.stories.svelte +0 -55
  113. package/dist/components/switch/switch.stories.svelte.d.ts +0 -19
  114. package/dist/components/textarea/textarea.stories.svelte +0 -36
  115. package/dist/components/textarea/textarea.stories.svelte.d.ts +0 -19
  116. package/dist/components/toast/toast.stories.svelte +0 -96
  117. package/dist/components/toast/toast.stories.svelte.d.ts +0 -19
  118. package/dist/internal/utils/arrays.d.ts +0 -1
  119. package/dist/internal/utils/arrays.js +0 -30
@@ -1,6 +1,13 @@
1
1
  <script lang="ts">
2
+ import type { SelectAsyncProps, SelectItem } from './types.js';
3
+
4
+ import { Icon } from '../icons/index.js';
2
5
  import { Spinner } from '../spinner/index.js';
6
+ import { createListCollection, type ListCollection } from '../../internal/index.js';
7
+ import { cn } from '../../internal/utils/common.js';
3
8
  import { watch } from 'runed';
9
+ import { cubicInOut } from 'svelte/easing';
10
+ import { fly } from 'svelte/transition';
4
11
  import Content from './components/select.content.svelte';
5
12
  import GroupHeading from './components/select.group-heading.svelte';
6
13
  import Group from './components/select.group.svelte';
@@ -9,54 +16,84 @@
9
16
  import Root from './components/select.root.svelte';
10
17
  import Trigger from './components/select.trigger.svelte';
11
18
  import Viewport from './components/select.viewport.svelte';
12
- import type { SelectAsyncProps, SelectData, SelectItem, SelectItemGroup } from './types.js';
13
- import { getItemKey, getLabelFromValue } from './utils/index.js';
14
19
 
15
20
  let {
16
21
  value = $bindable(''),
17
22
  open = $bindable(false),
23
+
18
24
  empty = 'No results found',
19
- maxContentHeight,
20
- item,
21
- trigger,
22
- side,
23
- sideOffset,
24
- align,
25
- alignOffset,
26
- avoidCollisions,
27
- collisionPadding,
28
- customAnchor,
25
+
26
+ groups = [],
27
+ item: itemSnippet,
28
+ trigger: triggerSnippet,
29
+
30
+ contentProps,
31
+ contentClass,
32
+ contentRef = $bindable(null),
33
+
29
34
  placeholder: placeholderProp,
30
- loading,
35
+
36
+ loading: loadingSnippet,
31
37
  pageSize = 10,
32
38
  callback,
39
+ initialItems = [],
40
+
33
41
  ...restProps
34
42
  }: SelectAsyncProps = $props();
35
43
 
36
- let data = $state<SelectData>([]);
44
+ let items = $state<SelectItem[]>(initialItems);
37
45
  let isLoading = $state(false);
38
46
  let hasMore = $state(true);
39
47
  let currentPage = $state(1);
40
-
41
48
  let error = $state<string | null>(null);
42
49
 
43
- const placeholder = $derived.by(() => {
50
+ const createCollection = (itemList: SelectItem[]): ListCollection<SelectItem> => {
51
+ const groupOrderMap = new Map<string, number>();
52
+ groups.forEach((g, index) => {
53
+ groupOrderMap.set(g.key, g.order ?? index);
54
+ });
55
+
56
+ return createListCollection({
57
+ items: itemList,
58
+ itemToValue: (item) => item.value,
59
+ itemToString: (item) => item.textValue ?? item.label,
60
+ isItemDisabled: (item) => !!item.disabled,
61
+ groupBy: (item) => item.group ?? '',
62
+ groupSort: (a, b) => {
63
+ const orderA = groupOrderMap.get(a) ?? Infinity;
64
+ const orderB = groupOrderMap.get(b) ?? Infinity;
65
+ if (orderA !== orderB) return orderA - orderB;
66
+ return a.localeCompare(b);
67
+ },
68
+ });
69
+ };
70
+
71
+ const collection = $derived(createCollection(items));
72
+ const groupLabelMap = $derived(new Map(groups.map((g) => [g.key, g.label ?? g.key])));
73
+ const groupedItems = $derived(collection.group());
74
+ const hasResults = $derived(collection.size > 0);
75
+
76
+ const displayLabel = $derived.by(() => {
44
77
  if (typeof value === 'string' && value.trim() !== '') {
45
- const label = getLabelFromValue(value, data);
46
- return label ?? value;
78
+ const item = collection.find(value);
79
+ if (item) return item.label;
80
+ return value;
47
81
  }
48
82
 
49
83
  if (Array.isArray(value) && value.length > 0) {
50
- const labels = value.map((v) => getLabelFromValue(v, data) ?? v);
84
+ const labels = value.map((v) => {
85
+ const item = collection.find(v);
86
+ return item?.label ?? v;
87
+ });
51
88
  return labels.join(', ');
52
89
  }
53
90
 
54
- if (placeholderProp) return placeholderProp;
55
-
56
- return '';
91
+ return placeholderProp ?? '';
57
92
  });
58
93
 
59
94
  const fetchData = async (page: number, append = false) => {
95
+ if (isLoading) return;
96
+
60
97
  isLoading = true;
61
98
  error = null;
62
99
 
@@ -64,14 +101,13 @@
64
101
  const result = await callback({ page, pageSize });
65
102
 
66
103
  if (append) {
67
- data = [...data, ...(result.data as any)];
104
+ items = [...items, ...result.items];
68
105
  } else {
69
- data = result.data;
106
+ items = result.items;
70
107
  }
71
108
 
72
109
  currentPage = page;
73
110
  hasMore = result.hasMore;
74
- isLoading = false;
75
111
  } catch (err) {
76
112
  error = err instanceof Error ? err.message : 'Failed to fetch data';
77
113
  } finally {
@@ -79,15 +115,16 @@
79
115
  }
80
116
  };
81
117
 
118
+ // Fetch on first open
82
119
  watch(
83
120
  () => open,
84
121
  () => {
85
- if (!open || data.length > 0 || isLoading) return;
86
-
87
- fetchData(currentPage);
122
+ if (!open || items.length > 0 || isLoading) return;
123
+ fetchData(1);
88
124
  }
89
125
  );
90
126
 
127
+ // Reset page on unmount
91
128
  $effect(() => {
92
129
  return () => {
93
130
  currentPage = 1;
@@ -96,8 +133,7 @@
96
133
 
97
134
  const loadMore = () => {
98
135
  if (!isLoading && hasMore) {
99
- const nextPage = currentPage + 1;
100
- fetchData(nextPage, true);
136
+ fetchData(currentPage + 1, true);
101
137
  }
102
138
  };
103
139
 
@@ -110,88 +146,115 @@
110
146
  }
111
147
  };
112
148
 
113
- const hasResults = $derived.by(() => {
114
- return data.length > 0;
115
- });
149
+ const retry = () => {
150
+ error = null;
151
+ fetchData(currentPage);
152
+ };
116
153
  </script>
117
154
 
118
- {#snippet itemsString(item: string)}
119
- <Item value={item} label={item} />
155
+ {#snippet defaultItemRenderer(item: SelectItem)}
156
+ <Item value={item.value} label={item.label} disabled={item.disabled} data-value={item.value}>
157
+ {#snippet children({ selected })}
158
+ {#if item.icon}
159
+ <item.icon width={16} height={16} />
160
+ {/if}
161
+ {item.label}
162
+
163
+ {#if selected}
164
+ <span class="cgui:ms-auto" transition:fly={{ duration: 250, y: 4, easing: cubicInOut }}>
165
+ <Icon.Checkmark class="cgui:ms-auto" width={16} height={16} />
166
+ </span>
167
+ {/if}
168
+ {/snippet}
169
+ </Item>
120
170
  {/snippet}
121
171
 
122
- {#snippet itemBasic(item: SelectItem)}
123
- <Item value={item.value} label={item.label} />
172
+ {#snippet renderItem(item: SelectItem)}
173
+ {#if itemSnippet}
174
+ <Item value={item.value} label={item.label} disabled={item.disabled} data-value={item.value}>
175
+ {#snippet child({ selected, highlighted, props })}
176
+ {@render itemSnippet({
177
+ item,
178
+ props,
179
+ selected,
180
+ highlighted,
181
+ })}
182
+ {/snippet}
183
+ </Item>
184
+ {:else}
185
+ {@render defaultItemRenderer(item)}
186
+ {/if}
124
187
  {/snippet}
125
188
 
126
- {#snippet itemGroup(group: SelectItemGroup)}
127
- <Group>
128
- <GroupHeading>
129
- {group.group}
130
- </GroupHeading>
131
-
132
- {#each group.items as item (getItemKey(item))}
133
- {#if typeof item === 'string'}
134
- {@render itemsString(item)}
135
- {:else}
136
- {@render itemBasic(item)}
137
- {/if}
138
- {/each}
139
- </Group>
189
+ {#snippet loadingSpinner()}
190
+ <div class="cgui:p-4 cgui:flex cgui:items-center cgui:h-full cgui:justify-center cgui:text-icon-regular">
191
+ <Spinner />
192
+ </div>
193
+ {/snippet}
194
+
195
+ {#snippet loadingMore()}
196
+ <div class="cgui:p-2 cgui:flex cgui:items-center cgui:justify-center cgui:text-fg-muted">
197
+ <Spinner />
198
+ </div>
199
+ {/snippet}
200
+
201
+ {#snippet errorState()}
202
+ <div class="cgui:p-4 cgui:text-center cgui:text-body-2">
203
+ <p class="cgui:text-destructive cgui:mb-2">{error}</p>
204
+ <button type="button" class="cgui:text-sm cgui:text-primary cgui:underline cgui:hover:no-underline" onclick={retry}>
205
+ Try again
206
+ </button>
207
+ </div>
140
208
  {/snippet}
141
209
 
142
210
  <Root bind:value={value as never} bind:open {...restProps as any}>
143
- {#if trigger}
211
+ {#if triggerSnippet}
144
212
  <Trigger>
145
213
  {#snippet child({ props })}
146
- {@render trigger?.({ props, label: placeholder })}
214
+ {@render triggerSnippet?.({ props, label: displayLabel })}
147
215
  {/snippet}
148
216
  </Trigger>
149
217
  {:else}
150
218
  <Trigger>
151
- {placeholder}
219
+ {displayLabel}
152
220
  </Trigger>
153
221
  {/if}
154
222
 
155
223
  <Portal>
156
- <Content
157
- {maxContentHeight}
158
- {side}
159
- {sideOffset}
160
- {align}
161
- {alignOffset}
162
- {avoidCollisions}
163
- {collisionPadding}
164
- {customAnchor}
165
- >
224
+ <Content bind:ref={contentRef} class={cn(contentClass)} {...contentProps}>
166
225
  <Viewport onscroll={handleScroll}>
167
226
  {#if error}
168
- <div class="cgui:p-4 cgui:text-center cgui:text-body-2 cgui:text-fg-darkest cgui:text-destructive">
169
- {error}
170
- </div>
227
+ {@render errorState()}
171
228
  {:else if isLoading && !hasResults}
172
- {#if loading}
173
- {@render loading?.()}
229
+ {#if loadingSnippet}
230
+ {@render loadingSnippet()}
174
231
  {:else}
175
- <div class="cgui:p-4 cgui:flex cgui:items-center cgui:h-full cgui:justify-center cgui:text-fg-darkest">
176
- <Spinner />
177
- </div>
232
+ {@render loadingSpinner()}
178
233
  {/if}
179
234
  {:else if hasResults}
180
- {#each data as item (getItemKey(item))}
181
- {#if typeof item === 'string'}
182
- {@render itemsString(item)}
183
- {:else if 'group' in item}
184
- {@render itemGroup(item)}
235
+ {#each groupedItems as [groupKey, groupItems] (groupKey || '__ungrouped__')}
236
+ {#if groupKey}
237
+ <Group>
238
+ <GroupHeading>
239
+ {groupLabelMap.get(groupKey) ?? groupKey}
240
+ </GroupHeading>
241
+ {#each groupItems as item (item.value)}
242
+ {@render renderItem(item)}
243
+ {/each}
244
+ </Group>
185
245
  {:else}
186
- {@render itemBasic(item)}
246
+ {#each groupItems as item (item.value)}
247
+ {@render renderItem(item)}
248
+ {/each}
187
249
  {/if}
188
250
  {/each}
251
+
189
252
  {#if isLoading}
190
- <div
191
- class="cgui:p-2 cgui:flex cgui:items-center cgui:justify-center cgui:text-center cgui:text-body-2 cgui:text-fg-darkest"
192
- >
193
- <Spinner />
194
- </div>
253
+ {@render loadingMore()}
254
+ {/if}
255
+
256
+ {#if !hasMore && items.length > pageSize}
257
+ <div class="cgui:p-2 cgui:text-center cgui:text-xs cgui:text-fg-muted">End of list</div>
195
258
  {/if}
196
259
  {:else if typeof empty === 'string'}
197
260
  <div
@@ -1,4 +1,4 @@
1
1
  import type { SelectAsyncProps } from './types.js';
2
- declare const Select: import("svelte").Component<SelectAsyncProps, {}, "value" | "open">;
2
+ declare const Select: import("svelte").Component<SelectAsyncProps, {}, "value" | "open" | "contentRef">;
3
3
  type Select = ReturnType<typeof Select>;
4
4
  export default Select;
@@ -1,7 +1,38 @@
1
+ <script lang="ts" module>
2
+ export function createSelectCollection(options: SelectCollectionOptions): ListCollection<SelectItem> {
3
+ const { items, groups = [], groupSort } = options;
4
+
5
+ const groupOrderMap = new Map<string, number>();
6
+ groups.forEach((g, index) => {
7
+ groupOrderMap.set(g.key, g.order ?? index);
8
+ });
9
+
10
+ return createListCollection({
11
+ items,
12
+ itemToValue: (item) => item.value,
13
+ itemToString: (item) => item.textValue ?? item.label,
14
+ isItemDisabled: (item) => !!item.disabled,
15
+ groupBy: (item) => item.group ?? '',
16
+ groupSort:
17
+ groupSort ??
18
+ ((a, b) => {
19
+ const orderA = groupOrderMap.get(a) ?? Infinity;
20
+ const orderB = groupOrderMap.get(b) ?? Infinity;
21
+ if (orderA !== orderB) return orderA - orderB;
22
+ return a.localeCompare(b);
23
+ }),
24
+ });
25
+ }
26
+ </script>
27
+
1
28
  <script lang="ts">
2
- import type { SelectItem, SelectItemGroup, SelectProps } from './types.js';
3
- import { getItemKey, getLabelFromValue } from './utils/index.js';
29
+ import type { SelectCollectionOptions, SelectItem, SelectProps } from './types.js';
4
30
 
31
+ import { Icon } from '../icons/index.js';
32
+ import { createListCollection, type ListCollection } from '../../internal/index.js';
33
+ import { cn } from '../../internal/utils/common.js';
34
+ import { cubicInOut } from 'svelte/easing';
35
+ import { fly } from 'svelte/transition';
5
36
  import Content from './components/select.content.svelte';
6
37
  import GroupHeading from './components/select.group-heading.svelte';
7
38
  import Group from './components/select.group.svelte';
@@ -17,101 +48,115 @@
17
48
 
18
49
  empty = 'No results found',
19
50
 
20
- maxContentHeight,
21
- data,
22
- item,
23
- trigger,
24
- side,
25
- sideOffset,
26
- align,
27
- alignOffset,
28
- avoidCollisions,
29
- collisionPadding,
30
- customAnchor,
51
+ collection,
52
+ groups = [],
53
+ item: itemSnippet,
54
+ trigger: triggerSnippet,
55
+ label: labelSnippet,
56
+
57
+ contentProps,
58
+ contentClass,
59
+ contentRef = $bindable(null),
31
60
 
32
61
  placeholder: placeholderProp,
33
62
 
34
63
  ...restProps
35
64
  }: SelectProps = $props();
36
65
 
37
- const placeholder = $derived.by(() => {
66
+ const groupLabelMap = $derived(new Map(groups.map((g) => [g.key, g.label ?? g.key])));
67
+
68
+ const groupedItems = $derived(collection.group());
69
+
70
+ const hasResults = $derived(collection.size > 0);
71
+
72
+ const displayLabel = $derived.by(() => {
38
73
  if (typeof value === 'string' && value.trim() !== '') {
39
- const label = getLabelFromValue(value, data);
40
- return label ?? value;
74
+ const item = collection.find(value);
75
+ if (item) return item.label;
76
+ return value;
41
77
  }
42
78
 
43
79
  if (Array.isArray(value) && value.length > 0) {
44
- const labels = value.map((v) => getLabelFromValue(v, data) ?? v);
80
+ const labels = value.map((v) => {
81
+ const item = collection.find(v);
82
+ return item?.label ?? v;
83
+ });
45
84
  return labels.join(', ');
46
85
  }
47
86
 
48
- if (placeholderProp) return placeholderProp;
49
-
50
- return '';
51
- });
52
-
53
- const hasResults = $derived.by(() => {
54
- return data.length > 0;
87
+ return placeholderProp ?? '';
55
88
  });
56
89
  </script>
57
90
 
58
- {#snippet itemsString(item: string)}
59
- <Item value={item} label={item} />
60
- {/snippet}
91
+ {#snippet defaultItemRenderer(item: SelectItem)}
92
+ <Item value={item.value} label={item.label} disabled={item.disabled} data-value={item.value}>
93
+ {#snippet children({ selected })}
94
+ {#if item.icon}
95
+ <item.icon width={16} height={16} />
96
+ {/if}
97
+ {item.label}
61
98
 
62
- {#snippet itemBasic(item: SelectItem)}
63
- <Item value={item.value} label={item.label} />
99
+ {#if selected}
100
+ <span class="cgui:ms-auto" transition:fly={{ duration: 250, y: 4, easing: cubicInOut }}>
101
+ <Icon.Checkmark class="cgui:ms-auto" width={16} height={16} />
102
+ </span>
103
+ {/if}
104
+ {/snippet}
105
+ </Item>
64
106
  {/snippet}
65
107
 
66
- {#snippet itemGroup(group: SelectItemGroup)}
67
- <Group>
68
- <GroupHeading>
69
- {group.group}
70
- </GroupHeading>
71
-
72
- {#each group.items as item (getItemKey(item))}
73
- {#if typeof item === 'string'}
74
- {@render itemsString(item)}
75
- {:else}
76
- {@render itemBasic(item)}
77
- {/if}
78
- {/each}
79
- </Group>
108
+ {#snippet renderItem(item: SelectItem)}
109
+ {#if itemSnippet}
110
+ <Item value={item.value} label={item.label} disabled={item.disabled} data-value={item.value}>
111
+ {#snippet child({ selected, highlighted, props })}
112
+ {@render itemSnippet({
113
+ item,
114
+ props,
115
+ selected,
116
+ highlighted,
117
+ })}
118
+ {/snippet}
119
+ </Item>
120
+ {:else}
121
+ {@render defaultItemRenderer(item)}
122
+ {/if}
80
123
  {/snippet}
81
124
 
82
125
  <Root bind:value={value as never} bind:open {...restProps}>
83
- {#if trigger}
126
+ {#if triggerSnippet}
84
127
  <Trigger>
85
128
  {#snippet child({ props })}
86
- {@render trigger?.({ props, label: placeholder })}
129
+ {@render triggerSnippet?.({ props, label: displayLabel })}
87
130
  {/snippet}
88
131
  </Trigger>
89
132
  {:else}
90
133
  <Trigger>
91
- {placeholder}
134
+ {#if labelSnippet}
135
+ {@render labelSnippet?.({ placeholder: displayLabel, value })}
136
+ {:else}
137
+ {displayLabel}
138
+ {/if}
92
139
  </Trigger>
93
140
  {/if}
94
141
 
95
142
  <Portal>
96
- <Content
97
- {maxContentHeight}
98
- {side}
99
- {sideOffset}
100
- {align}
101
- {alignOffset}
102
- {avoidCollisions}
103
- {collisionPadding}
104
- {customAnchor}
105
- >
143
+ <Content bind:ref={contentRef} class={cn(contentClass)} {...contentProps}>
106
144
  <Viewport>
107
145
  {#if hasResults}
108
- {#each data as item (getItemKey(item))}
109
- {#if typeof item === 'string'}
110
- {@render itemsString(item)}
111
- {:else if 'group' in item}
112
- {@render itemGroup(item)}
146
+ {#each groupedItems as [groupKey, items] (groupKey || '__ungrouped__')}
147
+ {#if groupKey}
148
+ <Group>
149
+ <GroupHeading>
150
+ {groupLabelMap.get(groupKey) ?? groupKey}
151
+ </GroupHeading>
152
+ {#each items as item (item.value)}
153
+ {@render renderItem(item)}
154
+ {/each}
155
+ </Group>
113
156
  {:else}
114
- {@render itemBasic(item)}
157
+ {#each items as item (item.value)}
158
+ {@render renderItem(item)}
159
+ {/each}
115
160
  {/if}
116
161
  {/each}
117
162
  {:else if typeof empty === 'string'}
@@ -1,4 +1,6 @@
1
- import type { SelectProps } from './types.js';
2
- declare const Select: import("svelte").Component<SelectProps, {}, "value" | "open">;
1
+ export declare function createSelectCollection(options: SelectCollectionOptions): ListCollection<SelectItem>;
2
+ import type { SelectCollectionOptions, SelectItem, SelectProps } from './types.js';
3
+ import { type ListCollection } from '../../internal/index.js';
4
+ declare const Select: import("svelte").Component<SelectProps, {}, "value" | "open" | "contentRef">;
3
5
  type Select = ReturnType<typeof Select>;
4
6
  export default Select;