@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.
- package/dist/assets/css/root.css +43 -4
- package/dist/components/breadcrumb/breadcrumb.svelte +2 -2
- package/dist/components/breadcrumb/types.d.ts +2 -2
- package/dist/components/button/styles.d.ts +15 -0
- package/dist/components/button/styles.js +9 -4
- package/dist/components/button-group/components/button-group.root.svelte +1 -3
- package/dist/components/collapsible/components/collapsaible.svelte.js +3 -3
- package/dist/components/data-table/data-table.svelte +1 -1
- package/dist/components/data-table/utils/pagination-state.svelte.js +3 -3
- package/dist/components/data-table/utils/resize-state.svelte.js +3 -3
- package/dist/components/data-table/utils/row-selection-state.svelte.js +3 -3
- package/dist/components/dialog/components/dialog.overlay.svelte +1 -7
- package/dist/components/dropdown/components/dropdown.content.svelte +0 -2
- package/dist/components/dropdown/dropdown.svelte +127 -67
- package/dist/components/dropdown/dropdown.svelte.d.ts +3 -2
- package/dist/components/dropdown/exports.d.ts +1 -1
- package/dist/components/dropdown/exports.js +1 -1
- package/dist/components/dropdown/index.d.ts +1 -1
- package/dist/components/dropdown/styles.js +5 -3
- package/dist/components/dropdown/types.d.ts +47 -23
- package/dist/components/field/field.svelte +0 -1
- package/dist/components/segment/components/segment.root.svelte +2 -1
- package/dist/components/segment/components/segmet.svelte.d.ts +0 -1
- package/dist/components/segment/components/segmet.svelte.js +0 -1
- package/dist/components/segment/styles.d.ts +18 -0
- package/dist/components/segment/styles.js +7 -1
- package/dist/components/select/components/select.content.svelte +2 -4
- package/dist/components/select/components/select.group-heading.svelte +2 -4
- package/dist/components/select/components/select.group-heading.svelte.d.ts +2 -2
- package/dist/components/select/components/select.group.svelte.d.ts +2 -2
- package/dist/components/select/components/select.item.svelte +5 -13
- package/dist/components/select/components/select.item.svelte.d.ts +2 -2
- package/dist/components/select/components/select.root.svelte +1 -17
- package/dist/components/select/components/select.root.svelte.d.ts +1 -2
- package/dist/components/select/components/select.trigger.svelte +17 -5
- package/dist/components/select/components/select.viewport.svelte +2 -4
- package/dist/components/select/components/select.viewport.svelte.d.ts +2 -2
- package/dist/components/select/exports.d.ts +1 -1
- package/dist/components/select/exports.js +1 -1
- package/dist/components/select/select.async.svelte +146 -83
- package/dist/components/select/select.async.svelte.d.ts +1 -1
- package/dist/components/select/select.svelte +107 -62
- package/dist/components/select/select.svelte.d.ts +4 -2
- package/dist/components/select/styles.d.ts +48 -139
- package/dist/components/select/styles.js +74 -101
- package/dist/components/select/types.d.ts +69 -29
- package/dist/components/select/types.js +0 -1
- package/dist/components/select/utils/get-item-key.d.ts +1 -1
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.js +1 -0
- package/dist/internal/lib/collection/grid-collection.d.ts +3 -0
- package/dist/internal/lib/collection/grid-collection.js +2 -0
- package/dist/internal/lib/collection/index.d.ts +5 -0
- package/dist/internal/lib/collection/index.js +5 -0
- package/dist/internal/lib/collection/list-collection.d.ts +3 -0
- package/dist/internal/lib/collection/list-collection.js +2 -0
- package/dist/internal/lib/collection/tree-collection.d.ts +4 -0
- package/dist/internal/lib/collection/tree-collection.js +3 -0
- package/dist/internal/lib/collection/use-list-collection.svelte.d.ts +88 -0
- package/dist/internal/lib/collection/use-list-collection.svelte.js +94 -0
- package/dist/internal/lib/collection/use-list-selection.svelte.d.ts +92 -0
- package/dist/internal/lib/collection/use-list-selection.svelte.js +80 -0
- package/dist/internal/lib/index.d.ts +1 -0
- package/dist/internal/lib/index.js +1 -0
- package/dist/internal/utils/equal.d.ts +3 -0
- package/dist/internal/utils/equal.js +56 -0
- package/dist/internal/utils/functions.d.ts +2 -0
- package/dist/internal/utils/functions.js +4 -0
- package/dist/internal/utils/guard.d.ts +5 -0
- package/dist/internal/utils/guard.js +5 -0
- package/package.json +5 -3
- package/dist/components/app-shell/app-shell.stories.svelte +0 -107
- package/dist/components/app-shell/app-shell.stories.svelte.d.ts +0 -18
- package/dist/components/badge/badge.stories.svelte +0 -81
- package/dist/components/badge/badge.stories.svelte.d.ts +0 -19
- package/dist/components/breadcrumb/breadcrumb.stories.svelte +0 -67
- package/dist/components/breadcrumb/breadcrumb.stories.svelte.d.ts +0 -4
- package/dist/components/button/button.stories.svelte +0 -106
- package/dist/components/button/button.stories.svelte.d.ts +0 -19
- package/dist/components/button-group/button-group.stories.svelte +0 -59
- package/dist/components/button-group/button-group.stories.svelte.d.ts +0 -18
- package/dist/components/checkbox/checkbox.stories.svelte +0 -52
- package/dist/components/checkbox/checkbox.stories.svelte.d.ts +0 -18
- package/dist/components/collapsible/collapsible.stories.svelte +0 -69
- package/dist/components/collapsible/collapsible.stories.svelte.d.ts +0 -18
- package/dist/components/data-table/data-table.stories.svelte +0 -327
- package/dist/components/data-table/data-table.stories.svelte.d.ts +0 -4
- package/dist/components/dialog/dialog.stories.svelte +0 -116
- package/dist/components/dialog/dialog.stories.svelte.d.ts +0 -3
- package/dist/components/dropdown/dropdown.stories.svelte +0 -151
- package/dist/components/dropdown/dropdown.stories.svelte.d.ts +0 -19
- package/dist/components/field/field.stories.svelte +0 -56
- package/dist/components/field/field.stories.svelte.d.ts +0 -19
- package/dist/components/input/input.stories.svelte +0 -41
- package/dist/components/input/input.stories.svelte.d.ts +0 -19
- package/dist/components/navigation/navigation.stories.svelte +0 -99
- package/dist/components/navigation/navigation.stories.svelte.d.ts +0 -19
- package/dist/components/pagination/pagination.stories.svelte +0 -69
- package/dist/components/pagination/pagination.stories.svelte.d.ts +0 -19
- package/dist/components/popover/popover.stories.svelte +0 -195
- package/dist/components/popover/popover.stories.svelte.d.ts +0 -3
- package/dist/components/segment/segment.stories.svelte +0 -57
- package/dist/components/segment/segment.stories.svelte.d.ts +0 -19
- package/dist/components/select/select.stories.svelte +0 -263
- package/dist/components/select/select.stories.svelte.d.ts +0 -4
- package/dist/components/separator/separator.stories.svelte +0 -44
- package/dist/components/separator/separator.stories.svelte.d.ts +0 -19
- package/dist/components/skeleton/skeleton.stories.svelte +0 -129
- package/dist/components/skeleton/skeleton.stories.svelte.d.ts +0 -19
- package/dist/components/spinner/spinner.stories.svelte +0 -29
- package/dist/components/spinner/spinner.stories.svelte.d.ts +0 -19
- package/dist/components/switch/switch.stories.svelte +0 -55
- package/dist/components/switch/switch.stories.svelte.d.ts +0 -19
- package/dist/components/textarea/textarea.stories.svelte +0 -36
- package/dist/components/textarea/textarea.stories.svelte.d.ts +0 -19
- package/dist/components/toast/toast.stories.svelte +0 -96
- package/dist/components/toast/toast.stories.svelte.d.ts +0 -19
- package/dist/internal/utils/arrays.d.ts +0 -1
- 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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
46
|
-
return label
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
104
|
+
items = [...items, ...result.items];
|
|
68
105
|
} else {
|
|
69
|
-
|
|
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 ||
|
|
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
|
-
|
|
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
|
|
114
|
-
|
|
115
|
-
|
|
149
|
+
const retry = () => {
|
|
150
|
+
error = null;
|
|
151
|
+
fetchData(currentPage);
|
|
152
|
+
};
|
|
116
153
|
</script>
|
|
117
154
|
|
|
118
|
-
{#snippet
|
|
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
|
|
123
|
-
|
|
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
|
|
127
|
-
<
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
211
|
+
{#if triggerSnippet}
|
|
144
212
|
<Trigger>
|
|
145
213
|
{#snippet child({ props })}
|
|
146
|
-
{@render
|
|
214
|
+
{@render triggerSnippet?.({ props, label: displayLabel })}
|
|
147
215
|
{/snippet}
|
|
148
216
|
</Trigger>
|
|
149
217
|
{:else}
|
|
150
218
|
<Trigger>
|
|
151
|
-
{
|
|
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
|
-
|
|
169
|
-
{error}
|
|
170
|
-
</div>
|
|
227
|
+
{@render errorState()}
|
|
171
228
|
{:else if isLoading && !hasResults}
|
|
172
|
-
{#if
|
|
173
|
-
{@render
|
|
229
|
+
{#if loadingSnippet}
|
|
230
|
+
{@render loadingSnippet()}
|
|
174
231
|
{:else}
|
|
175
|
-
|
|
176
|
-
<Spinner />
|
|
177
|
-
</div>
|
|
232
|
+
{@render loadingSpinner()}
|
|
178
233
|
{/if}
|
|
179
234
|
{:else if hasResults}
|
|
180
|
-
{#each
|
|
181
|
-
{#if
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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 {
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
item,
|
|
23
|
-
trigger,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
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
|
|
40
|
-
return label
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
59
|
-
<Item value={item} label={item}
|
|
60
|
-
{
|
|
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
|
-
{#
|
|
63
|
-
|
|
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
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
126
|
+
{#if triggerSnippet}
|
|
84
127
|
<Trigger>
|
|
85
128
|
{#snippet child({ props })}
|
|
86
|
-
{@render
|
|
129
|
+
{@render triggerSnippet?.({ props, label: displayLabel })}
|
|
87
130
|
{/snippet}
|
|
88
131
|
</Trigger>
|
|
89
132
|
{:else}
|
|
90
133
|
<Trigger>
|
|
91
|
-
{
|
|
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
|
|
109
|
-
{#if
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
2
|
-
|
|
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;
|