@casinogate/ui 1.11.12 → 1.11.14
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 +1 -1
- package/dist/components/combobox/presets/async/combobox.async.svelte +115 -122
- package/dist/components/combobox/presets/async/combobox.async.svelte.d.ts +8 -9
- package/dist/components/combobox/presets/async/use-async-combobox-data.svelte.d.ts +36 -0
- package/dist/components/combobox/presets/async/use-async-combobox-data.svelte.js +113 -0
- package/dist/components/combobox/presets/root/combobox.svelte +23 -8
- package/dist/components/combobox/presets/use-display-value.svelte.d.ts +6 -3
- package/dist/components/combobox/presets/use-display-value.svelte.js +6 -2
- package/dist/components/combobox/presets/use-selected-items.svelte.d.ts +7 -4
- package/dist/components/combobox/presets/use-selected-items.svelte.js +6 -4
- package/dist/components/combobox/types.d.ts +35 -27
- package/package.json +1 -1
package/dist/assets/css/root.css
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
<script lang="ts" generics="TItem extends
|
|
2
|
-
import type { CommandCollection
|
|
3
|
-
import type { ComboboxAsyncProps } from '../../types.js';
|
|
1
|
+
<script lang="ts" generics="TItem extends ComboboxItem">
|
|
2
|
+
import type { CommandCollection } from '../../../command/index.js';
|
|
3
|
+
import type { ComboboxAsyncProps, ComboboxItem } from '../../types.js';
|
|
4
4
|
|
|
5
5
|
import { SELECTION_TYPE } from '../../../../internal/constants/selection-type.js';
|
|
6
6
|
import { boolAttr } from '../../../../internal/utils/attrs.js';
|
|
7
7
|
import { cn, noop } from '../../../../internal/utils/common.js';
|
|
8
|
-
import {
|
|
9
|
-
import { afterTick, boxWith
|
|
8
|
+
import { watch } from 'runed';
|
|
9
|
+
import { afterTick, boxWith } 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';
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { useDisplayValue } from '../use-display-value.svelte.js';
|
|
20
20
|
import { useGroupedItems } from '../use-grouped-items.svelte.js';
|
|
21
21
|
import { useSelectedItems } from '../use-selected-items.svelte.js';
|
|
22
|
+
import { useAsyncComboboxData } from './use-async-combobox-data.svelte.js';
|
|
22
23
|
|
|
23
24
|
let {
|
|
24
25
|
open = $bindable(false),
|
|
@@ -44,48 +45,44 @@
|
|
|
44
45
|
clearable = false,
|
|
45
46
|
|
|
46
47
|
items = $bindable([]),
|
|
48
|
+
selectedItems = $bindable([]),
|
|
47
49
|
|
|
48
50
|
maxContentHeight,
|
|
49
51
|
|
|
50
52
|
loading: loadingSnippet,
|
|
51
53
|
pageSize = 10,
|
|
52
54
|
callback,
|
|
55
|
+
loadItems,
|
|
53
56
|
loadImmediate = true,
|
|
57
|
+
debounceMs,
|
|
54
58
|
searchDebounce = 300,
|
|
55
59
|
dependsOn,
|
|
56
60
|
clearOnDependencyChange = true,
|
|
61
|
+
onDependencyChange,
|
|
57
62
|
onSelect = noop,
|
|
58
63
|
|
|
59
64
|
...restProps
|
|
60
65
|
}: ComboboxAsyncProps<TItem> = $props();
|
|
61
66
|
|
|
62
|
-
const
|
|
67
|
+
const getDefaultValue = () => (type === SELECTION_TYPE.MULTIPLE ? [] : '');
|
|
68
|
+
const isMultiple = $derived.by(() => type === SELECTION_TYPE.MULTIPLE);
|
|
63
69
|
|
|
64
70
|
// Initialize value based on type
|
|
65
71
|
if (value === undefined) {
|
|
66
|
-
|
|
67
|
-
value = defaultValue;
|
|
72
|
+
value = getDefaultValue();
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
watch.pre(
|
|
71
76
|
() => value,
|
|
72
77
|
() => {
|
|
73
78
|
if (value !== undefined) return;
|
|
74
|
-
value =
|
|
79
|
+
value = getDefaultValue();
|
|
75
80
|
}
|
|
76
81
|
);
|
|
77
82
|
|
|
78
|
-
// let items = $state<CommandItem[]>(initialItems);
|
|
79
|
-
let isLoading = $state(false);
|
|
80
|
-
let isSearching = $state(false);
|
|
81
|
-
let hasMore = $state(false);
|
|
82
|
-
let currentPage = $state(1);
|
|
83
|
-
let error = $state<string | null>(null);
|
|
84
|
-
let lastSearch = $state('');
|
|
85
|
-
|
|
86
83
|
let listRef = $state<HTMLElement | null>(null);
|
|
87
84
|
|
|
88
|
-
const createCollection = (itemList:
|
|
85
|
+
const createCollection = (itemList: ComboboxItem[]): CommandCollection => {
|
|
89
86
|
const groupOrderMap = new Map<string, number>();
|
|
90
87
|
groups.forEach((g, index) => {
|
|
91
88
|
groupOrderMap.set(g.value, g.order ?? index);
|
|
@@ -113,11 +110,13 @@
|
|
|
113
110
|
value: boxWith(() => value!),
|
|
114
111
|
collection: boxWith(() => collection),
|
|
115
112
|
placeholder: boxWith(() => placeholder),
|
|
113
|
+
selectedItems: boxWith(() => selectedItems),
|
|
116
114
|
});
|
|
117
115
|
|
|
118
|
-
const
|
|
116
|
+
const resolvedSelectedItems = useSelectedItems({
|
|
119
117
|
value: boxWith(() => value!),
|
|
120
118
|
collection: boxWith(() => collection),
|
|
119
|
+
selectedItems: boxWith(() => selectedItems),
|
|
121
120
|
});
|
|
122
121
|
|
|
123
122
|
const isSelected = (itemValue: string): boolean => {
|
|
@@ -125,6 +124,8 @@
|
|
|
125
124
|
return value === itemValue;
|
|
126
125
|
};
|
|
127
126
|
|
|
127
|
+
const getItemKey = (item: ComboboxItem) => item.key ?? item.value;
|
|
128
|
+
|
|
128
129
|
const isPlaceholder = $derived.by(() => {
|
|
129
130
|
if (Array.isArray(value)) return value.length === 0;
|
|
130
131
|
return typeof value === 'string' && value.trim() === '';
|
|
@@ -132,109 +133,61 @@
|
|
|
132
133
|
|
|
133
134
|
const hasResults = $derived(collection.size > 0);
|
|
134
135
|
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
isSearching = !append;
|
|
140
|
-
error = null;
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const result = await callback({ page, pageSize, search });
|
|
144
|
-
|
|
145
|
-
if (append) {
|
|
146
|
-
items = [...items, ...result.items];
|
|
147
|
-
} else {
|
|
148
|
-
items = result.items;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
currentPage = page;
|
|
152
|
-
hasMore = result.hasMore;
|
|
153
|
-
lastSearch = search;
|
|
154
|
-
} catch (err) {
|
|
155
|
-
error = err instanceof Error ? err.message : 'Failed to fetch data';
|
|
156
|
-
} finally {
|
|
157
|
-
isLoading = false;
|
|
158
|
-
isSearching = false;
|
|
136
|
+
const resolvedLoadItems = $derived.by(() => {
|
|
137
|
+
const loader = loadItems ?? callback;
|
|
138
|
+
if (!loader) {
|
|
139
|
+
throw new Error('Combobox.Async requires either loadItems or callback');
|
|
159
140
|
}
|
|
160
|
-
};
|
|
161
141
|
|
|
162
|
-
|
|
163
|
-
|
|
142
|
+
return loader;
|
|
143
|
+
});
|
|
164
144
|
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
145
|
+
const asyncData = useAsyncComboboxData<TItem>({
|
|
146
|
+
items: boxWith(
|
|
147
|
+
() => items,
|
|
148
|
+
(next) => {
|
|
149
|
+
items = next;
|
|
150
|
+
}
|
|
151
|
+
),
|
|
152
|
+
loadItems: boxWith(() => resolvedLoadItems),
|
|
153
|
+
search: boxWith(
|
|
154
|
+
() => searchValue,
|
|
155
|
+
(next) => {
|
|
156
|
+
searchValue = next;
|
|
157
|
+
}
|
|
158
|
+
),
|
|
159
|
+
pageSize: boxWith(() => pageSize),
|
|
160
|
+
debounceMs: boxWith(() => debounceMs ?? searchDebounce),
|
|
161
|
+
loadImmediate: boxWith(() => loadImmediate),
|
|
162
|
+
dependency: boxWith(() => dependsOn?.()),
|
|
163
|
+
onDependencyReset: (dependency) => {
|
|
164
|
+
onDependencyChange?.(dependency);
|
|
169
165
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
search(debouncedSearchValue.current);
|
|
166
|
+
if (clearOnDependencyChange) {
|
|
167
|
+
value = isMultiple ? [] : '';
|
|
168
|
+
selectedItems = [];
|
|
169
|
+
}
|
|
175
170
|
},
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const isLoading = $derived(asyncData.isLoading);
|
|
174
|
+
const isSearching = $derived(asyncData.isSearching);
|
|
175
|
+
const hasMore = $derived(asyncData.hasMore);
|
|
176
|
+
const error = $derived(asyncData.error);
|
|
180
177
|
|
|
181
|
-
// Fetch on first open
|
|
182
178
|
watch(
|
|
183
179
|
() => open,
|
|
184
180
|
() => {
|
|
185
181
|
if (!open) return;
|
|
182
|
+
if (items.length > 0 || isLoading || loadImmediate) return;
|
|
186
183
|
|
|
187
|
-
|
|
188
|
-
if (items.length === 0 && !isLoading && !loadImmediate) {
|
|
189
|
-
fetchData(1, searchValue);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// Fetch immediate data on mount
|
|
196
|
-
onMountEffect(() => {
|
|
197
|
-
if (loadImmediate) fetchData(1, searchValue);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// Reset page on unmount
|
|
201
|
-
$effect(() => {
|
|
202
|
-
return () => {
|
|
203
|
-
currentPage = 1;
|
|
204
|
-
};
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Watch for dependency changes and reset
|
|
208
|
-
let previousDependency = $state<unknown>(dependsOn?.());
|
|
209
|
-
watch(
|
|
210
|
-
() => dependsOn?.(),
|
|
211
|
-
(newValue) => {
|
|
212
|
-
if (previousDependency === undefined && newValue === undefined) return;
|
|
213
|
-
if (previousDependency === newValue) return;
|
|
214
|
-
|
|
215
|
-
previousDependency = newValue;
|
|
216
|
-
|
|
217
|
-
// Reset state
|
|
218
|
-
items = [];
|
|
219
|
-
currentPage = 1;
|
|
220
|
-
hasMore = true;
|
|
221
|
-
error = null;
|
|
222
|
-
searchValue = '';
|
|
223
|
-
lastSearch = '';
|
|
224
|
-
|
|
225
|
-
// Clear selected value if enabled
|
|
226
|
-
if (clearOnDependencyChange) {
|
|
227
|
-
value = isMultiple ? [] : '';
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Reload data
|
|
231
|
-
fetchData(1, '');
|
|
184
|
+
asyncData.reload(searchValue);
|
|
232
185
|
}
|
|
233
186
|
);
|
|
234
187
|
|
|
235
188
|
const loadMore = () => {
|
|
236
189
|
if (!isLoading && hasMore) {
|
|
237
|
-
|
|
190
|
+
asyncData.loadMore();
|
|
238
191
|
}
|
|
239
192
|
};
|
|
240
193
|
|
|
@@ -256,8 +209,7 @@
|
|
|
256
209
|
};
|
|
257
210
|
|
|
258
211
|
const retry = () => {
|
|
259
|
-
|
|
260
|
-
fetchData(currentPage, searchValue);
|
|
212
|
+
asyncData.retry();
|
|
261
213
|
};
|
|
262
214
|
|
|
263
215
|
const scrollToTop = () => {
|
|
@@ -268,24 +220,45 @@
|
|
|
268
220
|
behavior: 'smooth',
|
|
269
221
|
});
|
|
270
222
|
};
|
|
271
|
-
|
|
223
|
+
|
|
224
|
+
const cacheSelectedItem = (item: TItem) => {
|
|
225
|
+
selectedItems = [...selectedItems.filter((selectedItem) => selectedItem.value !== item.value), item];
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const removeSelectedItem = (itemValue: string) => {
|
|
229
|
+
selectedItems = selectedItems.filter((selectedItem) => selectedItem.value !== itemValue);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
function onSelectItem(item: ComboboxItem) {
|
|
233
|
+
const typedItem = item as TItem;
|
|
234
|
+
|
|
272
235
|
if (Array.isArray(value)) {
|
|
273
236
|
let result = value;
|
|
274
237
|
if (value.includes(item.value)) {
|
|
275
|
-
|
|
238
|
+
if (allowDeselect) {
|
|
239
|
+
result = value.filter((v) => v !== item.value);
|
|
240
|
+
removeSelectedItem(item.value);
|
|
241
|
+
}
|
|
276
242
|
} else {
|
|
277
243
|
result = [...value, item.value];
|
|
244
|
+
cacheSelectedItem(typedItem);
|
|
278
245
|
}
|
|
279
|
-
onSelect(result,
|
|
246
|
+
onSelect(result, typedItem);
|
|
280
247
|
value = result;
|
|
281
248
|
} else {
|
|
282
|
-
let result = value;
|
|
249
|
+
let result = typeof value === 'string' ? value : '';
|
|
283
250
|
if (item.value === value) {
|
|
284
|
-
|
|
251
|
+
if (allowDeselect) {
|
|
252
|
+
result = '';
|
|
253
|
+
selectedItems = [];
|
|
254
|
+
} else {
|
|
255
|
+
cacheSelectedItem(typedItem);
|
|
256
|
+
}
|
|
285
257
|
} else {
|
|
286
258
|
result = item.value;
|
|
259
|
+
selectedItems = [typedItem];
|
|
287
260
|
}
|
|
288
|
-
onSelect(result,
|
|
261
|
+
onSelect(result, typedItem);
|
|
289
262
|
value = result;
|
|
290
263
|
}
|
|
291
264
|
|
|
@@ -307,11 +280,15 @@
|
|
|
307
280
|
{#snippet clearBtn()}
|
|
308
281
|
{#if clearable && isNotEmpty}
|
|
309
282
|
<button
|
|
283
|
+
aria-label="Clear selection"
|
|
310
284
|
class="cgui:ms-auto cgui:text-icon-regular cgui:size-4 cgui:cursor-pointer cgui:flex cgui:items-center cgui:justify-center cgui:shrink-0"
|
|
311
285
|
type="button"
|
|
312
286
|
onclick={(event) => {
|
|
313
287
|
event.stopPropagation();
|
|
314
|
-
|
|
288
|
+
const nextValue = isMultiple ? [] : '';
|
|
289
|
+
value = nextValue;
|
|
290
|
+
selectedItems = [];
|
|
291
|
+
onSelect(nextValue, null);
|
|
315
292
|
}}
|
|
316
293
|
>
|
|
317
294
|
<Icon.Cross class="cgui:ms-auto" width={16} height={16} />
|
|
@@ -323,13 +300,15 @@
|
|
|
323
300
|
{@const triggerProps = {
|
|
324
301
|
class: cn(comboboxTriggerVariants({ variant, size, rounded, fullWidth, class: triggerClass })),
|
|
325
302
|
'data-placeholder': boolAttr(isPlaceholder),
|
|
303
|
+
'aria-expanded': open,
|
|
304
|
+
'aria-busy': isLoading,
|
|
326
305
|
}}
|
|
327
306
|
{#if labelSnippet}
|
|
328
307
|
<PopoverPrimitive.Trigger {...triggerProps}>
|
|
329
308
|
{@render labelSnippet({
|
|
330
309
|
placeholder: displayValue.current,
|
|
331
310
|
value: isMultiple ? (value as string[]) : (value as string),
|
|
332
|
-
selectedItems:
|
|
311
|
+
selectedItems: resolvedSelectedItems.current,
|
|
333
312
|
})}
|
|
334
313
|
|
|
335
314
|
{@render clearBtn()}
|
|
@@ -349,7 +328,7 @@
|
|
|
349
328
|
{/if}
|
|
350
329
|
{/snippet}
|
|
351
330
|
|
|
352
|
-
{#snippet renderItem(item:
|
|
331
|
+
{#snippet renderItem(item: ComboboxItem)}
|
|
353
332
|
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect: _, ...restItem } = item}
|
|
354
333
|
{@const itemAttrs = {
|
|
355
334
|
value: itemValue,
|
|
@@ -388,8 +367,22 @@
|
|
|
388
367
|
{/if}
|
|
389
368
|
{/snippet}
|
|
390
369
|
|
|
370
|
+
{#snippet renderEmpty()}
|
|
371
|
+
{#if typeof empty === 'string'}
|
|
372
|
+
<CommandPrimitive.Empty>{empty}</CommandPrimitive.Empty>
|
|
373
|
+
{:else if empty}
|
|
374
|
+
{@render empty({ props: {} })}
|
|
375
|
+
{:else}
|
|
376
|
+
<CommandPrimitive.Empty>No results found</CommandPrimitive.Empty>
|
|
377
|
+
{/if}
|
|
378
|
+
{/snippet}
|
|
379
|
+
|
|
391
380
|
{#snippet loadingSpinner()}
|
|
392
|
-
<div
|
|
381
|
+
<div
|
|
382
|
+
role="status"
|
|
383
|
+
aria-live="polite"
|
|
384
|
+
class="cgui:p-4 cgui:flex cgui:items-center cgui:h-full cgui:justify-center cgui:text-icon-default"
|
|
385
|
+
>
|
|
393
386
|
<Spinner />
|
|
394
387
|
</div>
|
|
395
388
|
{/snippet}
|
|
@@ -401,7 +394,7 @@
|
|
|
401
394
|
{/snippet}
|
|
402
395
|
|
|
403
396
|
{#snippet errorState()}
|
|
404
|
-
<div class="cgui:p-4 cgui:text-center cgui:text-body-2">
|
|
397
|
+
<div role="alert" class="cgui:p-4 cgui:text-center cgui:text-body-2">
|
|
405
398
|
<p class="cgui:text-destructive cgui:mb-2">{error}</p>
|
|
406
399
|
<button type="button" class="cgui:text-sm cgui:text-primary cgui:underline cgui:hover:no-underline" onclick={retry}>
|
|
407
400
|
Try again
|
|
@@ -435,7 +428,7 @@
|
|
|
435
428
|
<CommandPrimitive.GroupHeading>{group?.label ?? groupKey}</CommandPrimitive.GroupHeading>
|
|
436
429
|
|
|
437
430
|
<CommandPrimitive.GroupItems>
|
|
438
|
-
{#each groupItems as item (item
|
|
431
|
+
{#each groupItems as item (getItemKey(item))}
|
|
439
432
|
{@render renderItem(item)}
|
|
440
433
|
{/each}
|
|
441
434
|
</CommandPrimitive.GroupItems>
|
|
@@ -446,7 +439,7 @@
|
|
|
446
439
|
</CommandPrimitive.Group>
|
|
447
440
|
{:else}
|
|
448
441
|
<CommandPrimitive.Group value="__ungrouped__">
|
|
449
|
-
{#each groupItems as item (item
|
|
442
|
+
{#each groupItems as item (getItemKey(item))}
|
|
450
443
|
{@render renderItem(item)}
|
|
451
444
|
{/each}
|
|
452
445
|
</CommandPrimitive.Group>
|
|
@@ -463,7 +456,7 @@
|
|
|
463
456
|
{@render loadingMore()}
|
|
464
457
|
{/if}
|
|
465
458
|
{:else}
|
|
466
|
-
|
|
459
|
+
{@render renderEmpty()}
|
|
467
460
|
{/if}
|
|
468
461
|
</CommandPrimitive.Viewport>
|
|
469
462
|
|
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
declare function $$render<TItem extends CommandItem>(): {
|
|
1
|
+
import type { ComboboxAsyncProps, ComboboxItem } from '../../types.js';
|
|
2
|
+
declare function $$render<TItem extends ComboboxItem>(): {
|
|
4
3
|
props: ComboboxAsyncProps<TItem>;
|
|
5
4
|
exports: {};
|
|
6
|
-
bindings: "value" | "open" | "items" | "searchValue";
|
|
5
|
+
bindings: "value" | "open" | "items" | "searchValue" | "selectedItems";
|
|
7
6
|
slots: {};
|
|
8
7
|
events: {};
|
|
9
8
|
};
|
|
10
|
-
declare class __sveltets_Render<TItem extends
|
|
9
|
+
declare class __sveltets_Render<TItem extends ComboboxItem> {
|
|
11
10
|
props(): ReturnType<typeof $$render<TItem>>['props'];
|
|
12
11
|
events(): ReturnType<typeof $$render<TItem>>['events'];
|
|
13
12
|
slots(): ReturnType<typeof $$render<TItem>>['slots'];
|
|
14
|
-
bindings(): "value" | "open" | "items" | "searchValue";
|
|
13
|
+
bindings(): "value" | "open" | "items" | "searchValue" | "selectedItems";
|
|
15
14
|
exports(): {};
|
|
16
15
|
}
|
|
17
16
|
interface $$IsomorphicComponent {
|
|
18
|
-
new <TItem extends
|
|
17
|
+
new <TItem extends ComboboxItem>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TItem>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TItem>['props']>, ReturnType<__sveltets_Render<TItem>['events']>, ReturnType<__sveltets_Render<TItem>['slots']>> & {
|
|
19
18
|
$$bindings?: ReturnType<__sveltets_Render<TItem>['bindings']>;
|
|
20
19
|
} & ReturnType<__sveltets_Render<TItem>['exports']>;
|
|
21
|
-
<TItem extends
|
|
20
|
+
<TItem extends ComboboxItem>(internal: unknown, props: ReturnType<__sveltets_Render<TItem>['props']> & {}): ReturnType<__sveltets_Render<TItem>['exports']>;
|
|
22
21
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
23
22
|
}
|
|
24
23
|
declare const Combobox: $$IsomorphicComponent;
|
|
25
|
-
type Combobox<TItem extends
|
|
24
|
+
type Combobox<TItem extends ComboboxItem> = InstanceType<typeof Combobox<TItem>>;
|
|
26
25
|
export default Combobox;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ComboboxAsyncLoader, ComboboxItem } from '../../types.js';
|
|
2
|
+
import type { ReadableBoxedValues, WritableBoxedValues } from 'svelte-toolbelt';
|
|
3
|
+
export declare const ASYNC_STATUS: {
|
|
4
|
+
readonly IDLE: "idle";
|
|
5
|
+
readonly LOADING: "loading";
|
|
6
|
+
readonly SEARCHING: "searching";
|
|
7
|
+
readonly LOADING_MORE: "loadingMore";
|
|
8
|
+
readonly ERROR: "error";
|
|
9
|
+
};
|
|
10
|
+
export type AsyncStatus = (typeof ASYNC_STATUS)[keyof typeof ASYNC_STATUS];
|
|
11
|
+
type Options<TItem extends ComboboxItem> = WritableBoxedValues<{
|
|
12
|
+
items: TItem[];
|
|
13
|
+
search: string;
|
|
14
|
+
}> & ReadableBoxedValues<{
|
|
15
|
+
loadItems: ComboboxAsyncLoader<TItem>;
|
|
16
|
+
pageSize: number;
|
|
17
|
+
debounceMs: number;
|
|
18
|
+
loadImmediate: boolean;
|
|
19
|
+
dependency: unknown;
|
|
20
|
+
}> & {
|
|
21
|
+
onDependencyReset?: (value: unknown) => void;
|
|
22
|
+
};
|
|
23
|
+
export declare function useAsyncComboboxData<TItem extends ComboboxItem>(opts: Options<TItem>): {
|
|
24
|
+
readonly status: AsyncStatus;
|
|
25
|
+
readonly error: string | null;
|
|
26
|
+
readonly hasMore: boolean;
|
|
27
|
+
readonly currentPage: number;
|
|
28
|
+
readonly isLoading: boolean;
|
|
29
|
+
readonly isSearching: boolean;
|
|
30
|
+
readonly isLoadingMore: boolean;
|
|
31
|
+
reload: (search?: string) => Promise<void>;
|
|
32
|
+
loadMore: () => Promise<void>;
|
|
33
|
+
retry: () => Promise<void>;
|
|
34
|
+
abort: () => void;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Debounced, watch } from 'runed';
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
export const ASYNC_STATUS = {
|
|
4
|
+
IDLE: 'idle',
|
|
5
|
+
LOADING: 'loading',
|
|
6
|
+
SEARCHING: 'searching',
|
|
7
|
+
LOADING_MORE: 'loadingMore',
|
|
8
|
+
ERROR: 'error',
|
|
9
|
+
};
|
|
10
|
+
export function useAsyncComboboxData(opts) {
|
|
11
|
+
let status = $state(ASYNC_STATUS.IDLE);
|
|
12
|
+
let error = $state(null);
|
|
13
|
+
let hasMore = $state(false);
|
|
14
|
+
let currentPage = $state(1);
|
|
15
|
+
let committedSearch = $state('');
|
|
16
|
+
let requestId = 0;
|
|
17
|
+
let controller = null;
|
|
18
|
+
const abortCurrent = () => {
|
|
19
|
+
requestId += 1;
|
|
20
|
+
controller?.abort();
|
|
21
|
+
controller = null;
|
|
22
|
+
};
|
|
23
|
+
const run = async (page, search, append = false) => {
|
|
24
|
+
if (append && (status === ASYNC_STATUS.LOADING_MORE || !hasMore))
|
|
25
|
+
return;
|
|
26
|
+
abortCurrent();
|
|
27
|
+
const id = requestId;
|
|
28
|
+
const localController = new AbortController();
|
|
29
|
+
controller = localController;
|
|
30
|
+
status = append
|
|
31
|
+
? ASYNC_STATUS.LOADING_MORE
|
|
32
|
+
: opts.items.current.length > 0
|
|
33
|
+
? ASYNC_STATUS.SEARCHING
|
|
34
|
+
: ASYNC_STATUS.LOADING;
|
|
35
|
+
error = null;
|
|
36
|
+
try {
|
|
37
|
+
const result = await opts.loadItems.current({
|
|
38
|
+
page,
|
|
39
|
+
pageSize: opts.pageSize.current,
|
|
40
|
+
search,
|
|
41
|
+
signal: localController.signal,
|
|
42
|
+
dependency: opts.dependency.current,
|
|
43
|
+
});
|
|
44
|
+
if (localController.signal.aborted || id !== requestId)
|
|
45
|
+
return;
|
|
46
|
+
opts.items.current = append ? [...opts.items.current, ...result.items] : result.items;
|
|
47
|
+
currentPage = page;
|
|
48
|
+
committedSearch = search;
|
|
49
|
+
hasMore = result.hasMore;
|
|
50
|
+
status = ASYNC_STATUS.IDLE;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
if (localController.signal.aborted || id !== requestId)
|
|
54
|
+
return;
|
|
55
|
+
error = err instanceof Error ? err.message : 'Failed to fetch data';
|
|
56
|
+
status = ASYNC_STATUS.ERROR;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const reload = (search = opts.search.current) => run(1, search, false);
|
|
60
|
+
const loadMore = () => run(currentPage + 1, committedSearch, true);
|
|
61
|
+
const retry = () => run(currentPage, committedSearch, currentPage > 1);
|
|
62
|
+
const debouncedSearch = new Debounced(() => opts.search.current, opts.debounceMs.current);
|
|
63
|
+
watch(() => debouncedSearch.current, (search) => {
|
|
64
|
+
reload(search);
|
|
65
|
+
}, { lazy: true });
|
|
66
|
+
let previousDependency = $state(opts.dependency.current);
|
|
67
|
+
watch(() => $state.snapshot(opts.dependency.current), (dependency) => {
|
|
68
|
+
if (Object.is(previousDependency, dependency))
|
|
69
|
+
return;
|
|
70
|
+
previousDependency = dependency;
|
|
71
|
+
abortCurrent();
|
|
72
|
+
opts.items.current = [];
|
|
73
|
+
opts.search.current = '';
|
|
74
|
+
opts.onDependencyReset?.(dependency);
|
|
75
|
+
error = null;
|
|
76
|
+
hasMore = false;
|
|
77
|
+
currentPage = 1;
|
|
78
|
+
committedSearch = '';
|
|
79
|
+
reload('');
|
|
80
|
+
}, { lazy: true });
|
|
81
|
+
onMount(() => {
|
|
82
|
+
if (opts.loadImmediate.current)
|
|
83
|
+
reload(opts.search.current);
|
|
84
|
+
return () => abortCurrent();
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
get status() {
|
|
88
|
+
return status;
|
|
89
|
+
},
|
|
90
|
+
get error() {
|
|
91
|
+
return error;
|
|
92
|
+
},
|
|
93
|
+
get hasMore() {
|
|
94
|
+
return hasMore;
|
|
95
|
+
},
|
|
96
|
+
get currentPage() {
|
|
97
|
+
return currentPage;
|
|
98
|
+
},
|
|
99
|
+
get isLoading() {
|
|
100
|
+
return (status === ASYNC_STATUS.LOADING || status === ASYNC_STATUS.SEARCHING || status === ASYNC_STATUS.LOADING_MORE);
|
|
101
|
+
},
|
|
102
|
+
get isSearching() {
|
|
103
|
+
return status === ASYNC_STATUS.LOADING || status === ASYNC_STATUS.SEARCHING;
|
|
104
|
+
},
|
|
105
|
+
get isLoadingMore() {
|
|
106
|
+
return status === ASYNC_STATUS.LOADING_MORE;
|
|
107
|
+
},
|
|
108
|
+
reload,
|
|
109
|
+
loadMore,
|
|
110
|
+
retry,
|
|
111
|
+
abort: abortCurrent,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
import { afterTick, boxWith } from 'svelte-toolbelt';
|
|
7
7
|
import { cubicInOut } from 'svelte/easing';
|
|
8
8
|
import { fly } from 'svelte/transition';
|
|
9
|
-
import { CommandPrimitive
|
|
9
|
+
import { CommandPrimitive } from '../../../command/index.js';
|
|
10
10
|
import { Icon, Icon as IconComponent } from '../../../icons/index.js';
|
|
11
11
|
import { Kbd } from '../../../kbd/index.js';
|
|
12
12
|
import { PopoverPrimitive } from '../../../popover/index.js';
|
|
13
13
|
import { comboboxTriggerVariants } from '../../styles.js';
|
|
14
|
-
import type { ComboboxProps } from '../../types.js';
|
|
14
|
+
import type { ComboboxItem, ComboboxProps } from '../../types.js';
|
|
15
15
|
import { useDisplayValue } from '../use-display-value.svelte.js';
|
|
16
16
|
import { useGroupedItems } from '../use-grouped-items.svelte.js';
|
|
17
17
|
import { useSelectedItems } from '../use-selected-items.svelte.js';
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
return typeof value === 'string' && value.trim() !== '';
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
function onSelectItem(item:
|
|
87
|
+
function onSelectItem(item: ComboboxItem) {
|
|
88
88
|
if (Array.isArray(value)) {
|
|
89
89
|
let result = value;
|
|
90
90
|
if (value.includes(item.value)) {
|
|
@@ -118,16 +118,21 @@
|
|
|
118
118
|
if (Array.isArray(value)) return value.includes(itemValue);
|
|
119
119
|
return value === itemValue;
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
const getItemKey = (item: ComboboxItem) => item.key ?? item.value;
|
|
121
123
|
</script>
|
|
122
124
|
|
|
123
125
|
{#snippet clearBtn()}
|
|
124
126
|
{#if clearable && isNotEmpty}
|
|
125
127
|
<button
|
|
128
|
+
aria-label="Clear selection"
|
|
126
129
|
class="cgui:ms-auto cgui:text-icon-regular cgui:size-4 cgui:cursor-pointer cgui:flex cgui:items-center cgui:justify-center cgui:shrink-0"
|
|
127
130
|
type="button"
|
|
128
131
|
onclick={(event) => {
|
|
129
132
|
event.stopPropagation();
|
|
130
|
-
|
|
133
|
+
const nextValue = isMultiple ? [] : '';
|
|
134
|
+
value = nextValue;
|
|
135
|
+
onSelect(nextValue, null);
|
|
131
136
|
}}
|
|
132
137
|
>
|
|
133
138
|
<Icon.Cross class="cgui:ms-auto" width={16} height={16} />
|
|
@@ -166,7 +171,7 @@
|
|
|
166
171
|
{/if}
|
|
167
172
|
{/snippet}
|
|
168
173
|
|
|
169
|
-
{#snippet renderItem(item:
|
|
174
|
+
{#snippet renderItem(item: ComboboxItem)}
|
|
170
175
|
{@const { value: itemValue, label, icon: Icon, shortcut, onSelect: _, ...restItem } = item}
|
|
171
176
|
{@const itemAttrs = {
|
|
172
177
|
value: itemValue,
|
|
@@ -205,6 +210,16 @@
|
|
|
205
210
|
{/if}
|
|
206
211
|
{/snippet}
|
|
207
212
|
|
|
213
|
+
{#snippet renderEmpty()}
|
|
214
|
+
{#if typeof empty === 'string'}
|
|
215
|
+
<CommandPrimitive.Empty>{empty}</CommandPrimitive.Empty>
|
|
216
|
+
{:else if empty}
|
|
217
|
+
{@render empty({ props: {} })}
|
|
218
|
+
{:else}
|
|
219
|
+
<CommandPrimitive.Empty>No results found</CommandPrimitive.Empty>
|
|
220
|
+
{/if}
|
|
221
|
+
{/snippet}
|
|
222
|
+
|
|
208
223
|
<PopoverPrimitive.Root bind:open {...restProps}>
|
|
209
224
|
{@render renderTrigger()}
|
|
210
225
|
|
|
@@ -215,7 +230,7 @@
|
|
|
215
230
|
|
|
216
231
|
<CommandPrimitive.List class="cgui:rounded-inherit" style={{ maxHeight: maxContentHeight }}>
|
|
217
232
|
<CommandPrimitive.Viewport>
|
|
218
|
-
|
|
233
|
+
{@render renderEmpty()}
|
|
219
234
|
|
|
220
235
|
{#each groupedItems.items.current as [groupKey, items] (groupKey || '__ungrouped__')}
|
|
221
236
|
{#if groupKey}
|
|
@@ -224,7 +239,7 @@
|
|
|
224
239
|
<CommandPrimitive.GroupHeading>{group?.label ?? groupKey}</CommandPrimitive.GroupHeading>
|
|
225
240
|
|
|
226
241
|
<CommandPrimitive.GroupItems>
|
|
227
|
-
{#each items as item (item
|
|
242
|
+
{#each items as item (getItemKey(item))}
|
|
228
243
|
{@render renderItem(item)}
|
|
229
244
|
{/each}
|
|
230
245
|
</CommandPrimitive.GroupItems>
|
|
@@ -235,7 +250,7 @@
|
|
|
235
250
|
</CommandPrimitive.Group>
|
|
236
251
|
{:else}
|
|
237
252
|
<CommandPrimitive.Group value="__ungrouped__">
|
|
238
|
-
{#each items as item (item
|
|
253
|
+
{#each items as item (getItemKey(item))}
|
|
239
254
|
{@render renderItem(item)}
|
|
240
255
|
{/each}
|
|
241
256
|
</CommandPrimitive.Group>
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { CommandCollection } from '../../command/types.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { ComboboxItem } from '../types.js';
|
|
3
|
+
import { type ReadableBox, type ReadableBoxedValues } from 'svelte-toolbelt';
|
|
3
4
|
type Opts = ReadableBoxedValues<{
|
|
4
5
|
value: string | string[];
|
|
5
6
|
collection: CommandCollection;
|
|
6
7
|
placeholder: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
8
|
+
}> & {
|
|
9
|
+
selectedItems?: ReadableBox<ComboboxItem[]>;
|
|
10
|
+
};
|
|
11
|
+
export declare const useDisplayValue: (opts: Opts) => ReadableBox<string>;
|
|
9
12
|
export {};
|
|
@@ -2,14 +2,18 @@ import { boxWith } from 'svelte-toolbelt';
|
|
|
2
2
|
export const useDisplayValue = (opts) => {
|
|
3
3
|
const res = $derived.by(() => {
|
|
4
4
|
const { value, collection, placeholder } = opts;
|
|
5
|
+
const findItem = (itemValue) => {
|
|
6
|
+
return (collection.current.items.find((item) => item.value === itemValue) ??
|
|
7
|
+
opts.selectedItems?.current.find((item) => item.value === itemValue));
|
|
8
|
+
};
|
|
5
9
|
if (typeof value.current === 'string' && value.current.trim() !== '') {
|
|
6
|
-
const item =
|
|
10
|
+
const item = findItem(value.current);
|
|
7
11
|
return item?.label ?? value.current;
|
|
8
12
|
}
|
|
9
13
|
if (Array.isArray(value.current) && value.current.length > 0) {
|
|
10
14
|
return value.current
|
|
11
15
|
.map((v) => {
|
|
12
|
-
const item =
|
|
16
|
+
const item = findItem(v);
|
|
13
17
|
return item?.label ?? v;
|
|
14
18
|
})
|
|
15
19
|
.join(', ');
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type { CommandCollection
|
|
2
|
-
import {
|
|
1
|
+
import type { CommandCollection } from '../../command/types.js';
|
|
2
|
+
import type { ComboboxItem } from '../types.js';
|
|
3
|
+
import { type ReadableBox, type ReadableBoxedValues } from 'svelte-toolbelt';
|
|
3
4
|
type Opts = ReadableBoxedValues<{
|
|
4
5
|
value: string | string[];
|
|
5
6
|
collection: CommandCollection;
|
|
6
|
-
}
|
|
7
|
-
|
|
7
|
+
}> & {
|
|
8
|
+
selectedItems?: ReadableBox<ComboboxItem[]>;
|
|
9
|
+
};
|
|
10
|
+
export declare const useSelectedItems: (opts: Opts) => ReadableBox<import("../../command/types.js").CommandItem[]>;
|
|
8
11
|
export {};
|
|
@@ -2,14 +2,16 @@ import { boxWith } from 'svelte-toolbelt';
|
|
|
2
2
|
export const useSelectedItems = (opts) => {
|
|
3
3
|
const { value, collection } = opts;
|
|
4
4
|
const res = $derived.by(() => {
|
|
5
|
+
const findItem = (itemValue) => {
|
|
6
|
+
return (collection.current.items.find((item) => item.value === itemValue) ??
|
|
7
|
+
opts.selectedItems?.current.find((item) => item.value === itemValue));
|
|
8
|
+
};
|
|
5
9
|
if (typeof value.current === 'string' && value.current.trim() !== '') {
|
|
6
|
-
const item =
|
|
10
|
+
const item = findItem(value.current);
|
|
7
11
|
return item ? [item] : [];
|
|
8
12
|
}
|
|
9
13
|
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);
|
|
14
|
+
return value.current.map((v) => findItem(v)).filter((i) => i !== undefined);
|
|
13
15
|
}
|
|
14
16
|
return [];
|
|
15
17
|
});
|
|
@@ -4,6 +4,9 @@ import type { CommandGroup, CommandItem, CommandProps } from '../command/index.j
|
|
|
4
4
|
import type { PrimitivePopoverRootProps } from '../popover/types.js';
|
|
5
5
|
import type { ComboboxTriggerVariantsProps } from './styles.js';
|
|
6
6
|
export type ComboboxRootProps = PrimitivePopoverRootProps;
|
|
7
|
+
export type ComboboxItem = CommandItem & {
|
|
8
|
+
key?: string | number;
|
|
9
|
+
};
|
|
7
10
|
/**
|
|
8
11
|
* Base props shared by both single and multiple selection modes
|
|
9
12
|
*/
|
|
@@ -29,10 +32,10 @@ type ComboboxBaseProps = PrimitivePopoverRootProps & ComboboxTriggerVariantsProp
|
|
|
29
32
|
label?: Snippet<[{
|
|
30
33
|
placeholder: string;
|
|
31
34
|
value: string | string[];
|
|
32
|
-
selectedItems:
|
|
35
|
+
selectedItems: ComboboxItem[];
|
|
33
36
|
}]>;
|
|
34
37
|
/** Callback when selection changes */
|
|
35
|
-
onSelect?: (value: string | string[], item:
|
|
38
|
+
onSelect?: (value: string | string[], item: ComboboxItem | null) => void;
|
|
36
39
|
};
|
|
37
40
|
type ComboboxSingleProps = ComboboxBaseProps & {
|
|
38
41
|
type: typeof SELECTION_TYPE.SINGLE;
|
|
@@ -46,58 +49,63 @@ export type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps;
|
|
|
46
49
|
/**
|
|
47
50
|
* Async Combobox Types
|
|
48
51
|
*/
|
|
49
|
-
export type
|
|
52
|
+
export type ComboboxAsyncLoadParams = {
|
|
50
53
|
page: number;
|
|
51
54
|
pageSize: number;
|
|
52
55
|
search: string;
|
|
56
|
+
signal: AbortSignal;
|
|
57
|
+
dependency?: unknown;
|
|
53
58
|
};
|
|
54
|
-
export type
|
|
59
|
+
export type ComboboxAsyncLoadResult<TItem extends ComboboxItem> = {
|
|
55
60
|
items: TItem[];
|
|
56
61
|
hasMore: boolean;
|
|
57
62
|
};
|
|
58
|
-
export type
|
|
59
|
-
|
|
63
|
+
export type ComboboxAsyncLoader<TItem extends ComboboxItem = ComboboxItem> = (params: ComboboxAsyncLoadParams) => Promise<ComboboxAsyncLoadResult<TItem>>;
|
|
64
|
+
/** @deprecated Use ComboboxAsyncLoadParams instead. */
|
|
65
|
+
export type ComboboxAsyncCallbackParams = ComboboxAsyncLoadParams;
|
|
66
|
+
/** @deprecated Use ComboboxAsyncLoadResult instead. */
|
|
67
|
+
export type ComboboxAsyncCallbackResult<TItem extends ComboboxItem> = ComboboxAsyncLoadResult<TItem>;
|
|
68
|
+
/** @deprecated Use ComboboxAsyncLoader instead. */
|
|
69
|
+
export type ComboboxAsyncCallback<TItem extends ComboboxItem = ComboboxItem> = ComboboxAsyncLoader<TItem>;
|
|
70
|
+
type ComboboxAsyncLoadProp<TItem extends ComboboxItem> = {
|
|
71
|
+
/** Preferred async loader API */
|
|
72
|
+
loadItems: ComboboxAsyncLoader<TItem>;
|
|
73
|
+
callback?: never;
|
|
74
|
+
} | {
|
|
75
|
+
/** @deprecated Use loadItems instead. Kept for backwards compatibility. */
|
|
76
|
+
callback: ComboboxAsyncLoader<TItem>;
|
|
77
|
+
loadItems?: never;
|
|
78
|
+
};
|
|
79
|
+
type ComboboxAsyncBaseProps<TItem extends ComboboxItem> = Omit<ComboboxBaseProps, 'collection' | 'item' | 'onSelect'> & ComboboxAsyncLoadProp<TItem> & {
|
|
60
80
|
items?: TItem[];
|
|
81
|
+
/** Cache of selected item objects, useful when selected values are not in the currently loaded async page. */
|
|
82
|
+
selectedItems?: TItem[];
|
|
61
83
|
item?: Snippet<[{
|
|
62
84
|
item: TItem;
|
|
63
85
|
props: Record<string, unknown>;
|
|
64
86
|
}]>;
|
|
65
|
-
/** Callback when selection changes */
|
|
66
87
|
onSelect?: (value: string | string[], item: TItem | null) => void;
|
|
67
|
-
/** Loading state renderer */
|
|
68
88
|
loading?: Snippet;
|
|
69
|
-
/** Current page (default: 1) */
|
|
70
|
-
page?: number;
|
|
71
|
-
/** Page size for pagination (default: 20) */
|
|
72
89
|
pageSize?: number;
|
|
73
|
-
/** Async data callback */
|
|
74
|
-
callback: ComboboxAsyncCallback<TItem>;
|
|
75
|
-
/** Load immediate data when combobox is mounted (default: true) */
|
|
76
90
|
loadImmediate?: boolean;
|
|
77
|
-
/** Group definitions (for labels/ordering) */
|
|
78
91
|
groups?: CommandGroup[];
|
|
79
|
-
/**
|
|
92
|
+
/** Preferred name */
|
|
93
|
+
debounceMs?: number;
|
|
94
|
+
/** @deprecated Use debounceMs instead. */
|
|
80
95
|
searchDebounce?: number;
|
|
81
|
-
/**
|
|
82
|
-
* Getter function for dependency tracking. When the returned value changes,
|
|
83
|
-
* the component will reset items, page to 1, clear selected value, and reload data.
|
|
84
|
-
* @example dependsOn={() => parentSelectValue}
|
|
85
|
-
*/
|
|
86
96
|
dependsOn?: () => unknown;
|
|
87
|
-
/**
|
|
88
|
-
* Whether to clear the selected value when dependsOn changes **(default: true)**
|
|
89
|
-
*/
|
|
90
97
|
clearOnDependencyChange?: boolean;
|
|
98
|
+
onDependencyChange?: (value: unknown) => void;
|
|
91
99
|
};
|
|
92
|
-
type ComboboxAsyncSingleProps<TItem extends
|
|
100
|
+
type ComboboxAsyncSingleProps<TItem extends ComboboxItem> = ComboboxAsyncBaseProps<TItem> & {
|
|
93
101
|
/** Selection mode (default: 'single') */
|
|
94
102
|
type?: 'single';
|
|
95
103
|
value?: string;
|
|
96
104
|
};
|
|
97
|
-
type ComboboxAsyncMultipleProps<TItem extends
|
|
105
|
+
type ComboboxAsyncMultipleProps<TItem extends ComboboxItem> = ComboboxAsyncBaseProps<TItem> & {
|
|
98
106
|
/** Selection mode */
|
|
99
107
|
type: 'multiple';
|
|
100
108
|
value?: string[];
|
|
101
109
|
};
|
|
102
|
-
export type ComboboxAsyncProps<TItem extends
|
|
110
|
+
export type ComboboxAsyncProps<TItem extends ComboboxItem> = ComboboxAsyncSingleProps<TItem> | ComboboxAsyncMultipleProps<TItem>;
|
|
103
111
|
export {};
|