@ibobbyts/svelte-ui-utils 0.1.2 → 0.2.1
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/README.md +112 -15
- package/dist/data-table/BaseDataTable.svelte +70 -41
- package/dist/data-table/BaseDataTable.svelte.d.ts +1 -0
- package/dist/data-table/BaseDataTable.svelte.d.ts.map +1 -1
- package/dist/data-table/DataTable.svelte +4 -0
- package/dist/data-table/DataTable.svelte.d.ts +1 -0
- package/dist/data-table/DataTable.svelte.d.ts.map +1 -1
- package/dist/data-table/DateRangeFilter.svelte +27 -0
- package/dist/data-table/DateRangeFilter.svelte.d.ts +1 -0
- package/dist/data-table/DateRangeFilter.svelte.d.ts.map +1 -1
- package/dist/data-table/FilterControl.svelte +3 -0
- package/dist/data-table/FilterControl.svelte.d.ts.map +1 -1
- package/dist/data-table/FilterTable.svelte +2 -0
- package/dist/data-table/FilterTable.svelte.d.ts.map +1 -1
- package/dist/data-table/NumberRangeFilter.svelte +2 -0
- package/dist/data-table/NumberRangeFilter.svelte.d.ts.map +1 -1
- package/dist/data-table/index.d.ts +1 -1
- package/dist/data-table/index.d.ts.map +1 -1
- package/dist/data-table/types.d.ts +8 -2
- package/dist/data-table/types.d.ts.map +1 -1
- package/dist/dropdown/Dropdown.svelte +2 -0
- package/dist/dropdown/Dropdown.svelte.d.ts.map +1 -1
- package/dist/dropdown-search/DropdownSearch.svelte +358 -38
- package/dist/dropdown-search/DropdownSearch.svelte.d.ts +8 -1
- package/dist/dropdown-search/DropdownSearch.svelte.d.ts.map +1 -1
- package/dist/dropdown-search/DropdownSearchMultiSelect.svelte +92 -0
- package/dist/dropdown-search/DropdownSearchMultiSelect.svelte.d.ts +53 -0
- package/dist/dropdown-search/DropdownSearchMultiSelect.svelte.d.ts.map +1 -0
- package/dist/dropdown-search/index.d.ts +3 -2
- package/dist/dropdown-search/index.d.ts.map +1 -1
- package/dist/dropdown-search/index.js +2 -1
- package/dist/dropdown-search/state.d.ts +2 -0
- package/dist/dropdown-search/state.d.ts.map +1 -1
- package/dist/dropdown-search/state.js +7 -0
- package/dist/dropdown-search/types.d.ts +8 -0
- package/dist/dropdown-search/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/modal/ConfirmDialog.svelte +69 -0
- package/dist/modal/ConfirmDialog.svelte.d.ts +44 -0
- package/dist/modal/ConfirmDialog.svelte.d.ts.map +1 -0
- package/dist/modal/Modal.svelte +131 -0
- package/dist/modal/Modal.svelte.d.ts +43 -0
- package/dist/modal/Modal.svelte.d.ts.map +1 -0
- package/dist/modal/index.d.ts +4 -0
- package/dist/modal/index.d.ts.map +1 -0
- package/dist/modal/index.js +2 -0
- package/dist/modal/types.d.ts +3 -0
- package/dist/modal/types.d.ts.map +1 -0
- package/dist/modal/types.js +1 -0
- package/dist/pagination/Pagination.svelte +2 -0
- package/dist/pagination/Pagination.svelte.d.ts.map +1 -1
- package/dist/style.css +295 -4
- package/dist/toast/Toast.svelte +2 -0
- package/dist/toast/Toast.svelte.d.ts.map +1 -1
- package/dist/toast/ToastManager.svelte +2 -0
- package/dist/toast/ToastManager.svelte.d.ts.map +1 -1
- package/package.json +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/Dropdown.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"Dropdown.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/Dropdown.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAyI1G,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,QAAQ;SAV8I,MAAM,GAAG,SAAS;YAAU,aAAa;cAAY,cAAc,EAAE;gBAAc,MAAM,GAAG,SAAS;gBAAc,iBAAiB;;eAAyC,qBAAqB,GAAG,SAAS;;;kBAU7Q,CAAC;AAC5E,KAAK,QAAQ,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,CAAC;AAChD,eAAe,QAAQ,CAAC"}
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
+
<svelte:options runes={false} />
|
|
2
|
+
|
|
1
3
|
<script lang="ts">
|
|
2
4
|
import { onDestroy, onMount } from 'svelte';
|
|
3
5
|
import { getUiMessages, type UiLanguage } from '../i18n.js';
|
|
4
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
formatParamDict,
|
|
8
|
+
normalizeDropdownSearchValue,
|
|
9
|
+
resolveDropdownSearchStatus,
|
|
10
|
+
} from './state.js';
|
|
5
11
|
import type {
|
|
6
12
|
DropdownSearchChangeDetail,
|
|
13
|
+
DropdownSearchEnterDetail,
|
|
7
14
|
DropdownSearchItem,
|
|
8
15
|
DropdownSearchItemValueGetter,
|
|
9
16
|
DropdownSearchLoadOptions,
|
|
10
|
-
|
|
17
|
+
DropdownSearchSelectedItemLabelGetter,
|
|
18
|
+
DropdownSearchSelectedItemsChangeHandler,
|
|
19
|
+
DropdownSearchStatus,
|
|
11
20
|
} from './types.js';
|
|
12
21
|
|
|
13
22
|
export let value = '';
|
|
14
23
|
export let selectedItem: DropdownSearchItem | null = null;
|
|
24
|
+
export let selectedItems: DropdownSearchItem[] = [];
|
|
15
25
|
export let status: DropdownSearchStatus = 'empty';
|
|
26
|
+
export let multiselect = false;
|
|
16
27
|
export let placeholder = '';
|
|
17
28
|
export let debounceMs = 500;
|
|
18
29
|
export let limit = 10;
|
|
@@ -32,37 +43,67 @@
|
|
|
32
43
|
export let width: string | undefined = undefined;
|
|
33
44
|
export let minWidth: string | undefined = undefined;
|
|
34
45
|
export let maxWidth: string | undefined = undefined;
|
|
46
|
+
export let selectedItemsLabel = 'Selected items';
|
|
47
|
+
export let removeSelectedItemLabel:
|
|
48
|
+
| DropdownSearchSelectedItemLabelGetter
|
|
49
|
+
| undefined = undefined;
|
|
35
50
|
export let getItemValue: DropdownSearchItemValueGetter = (item) => item.title;
|
|
36
|
-
export let onChange:
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
export let onChange:
|
|
52
|
+
| ((detail: DropdownSearchChangeDetail) => void)
|
|
53
|
+
| undefined = undefined;
|
|
54
|
+
export let onSelect: ((item: DropdownSearchItem) => void) | undefined =
|
|
55
|
+
undefined;
|
|
56
|
+
export let onDeselect: ((item: DropdownSearchItem) => void) | undefined =
|
|
57
|
+
undefined;
|
|
58
|
+
export let onSelectedItemsChange:
|
|
59
|
+
| DropdownSearchSelectedItemsChangeHandler
|
|
60
|
+
| undefined = undefined;
|
|
61
|
+
export let onStatusChange:
|
|
62
|
+
| ((status: DropdownSearchStatus) => void)
|
|
63
|
+
| undefined = undefined;
|
|
39
64
|
export let onInputBlur: ((event: FocusEvent) => void) | undefined = undefined;
|
|
65
|
+
export let onEnter: ((detail: DropdownSearchEnterDetail) => void) | undefined =
|
|
66
|
+
undefined;
|
|
40
67
|
|
|
41
68
|
let options: DropdownSearchItem[] = [];
|
|
42
69
|
let exactMatch: DropdownSearchItem | null = null;
|
|
43
70
|
let focused = false;
|
|
71
|
+
let inputValue = value;
|
|
44
72
|
let inputElement: HTMLInputElement | undefined;
|
|
45
73
|
let searchTimer: ReturnType<typeof setTimeout> | undefined;
|
|
74
|
+
let blurTimer: ReturnType<typeof setTimeout> | undefined;
|
|
46
75
|
let activeController: AbortController | undefined;
|
|
47
76
|
let requestId = 0;
|
|
48
77
|
let lastHandledValue = value;
|
|
49
78
|
|
|
50
79
|
$: messages = getUiMessages(language);
|
|
51
|
-
$: resolvedNoResultsText = noResultsText?.trim()
|
|
52
|
-
|
|
53
|
-
|
|
80
|
+
$: resolvedNoResultsText = noResultsText?.trim()
|
|
81
|
+
? noResultsText
|
|
82
|
+
: messages.dropdownSearch.noResultsText;
|
|
83
|
+
$: resolvedLoadingText = loadingText?.trim()
|
|
84
|
+
? loadingText
|
|
85
|
+
: messages.dropdownSearch.loadingText;
|
|
86
|
+
$: resolvedClearLabel = clearLabel?.trim()
|
|
87
|
+
? clearLabel
|
|
88
|
+
: messages.dropdownSearch.clearLabel;
|
|
54
89
|
$: resolvedListboxId = listboxId ?? (id ? `${id}-options` : undefined);
|
|
55
90
|
$: hasQuery = normalizeDropdownSearchValue(value).length > 0;
|
|
56
|
-
$: showOptions =
|
|
91
|
+
$: showOptions =
|
|
92
|
+
focused &&
|
|
93
|
+
!disabled &&
|
|
94
|
+
(hasQuery || (multiselect && options.length > 0)) &&
|
|
95
|
+
(options.length > 0 ||
|
|
96
|
+
status === 'loading' ||
|
|
97
|
+
(validate && hasQuery && options.length === 0));
|
|
57
98
|
$: if (!validate && status !== 'empty' && status !== 'loading') {
|
|
58
99
|
setStatus('empty');
|
|
59
100
|
}
|
|
60
|
-
$: if (
|
|
61
|
-
|
|
101
|
+
$: if (value !== lastHandledValue && value !== inputValue) {
|
|
102
|
+
handleIncomingValue(value);
|
|
62
103
|
}
|
|
63
104
|
|
|
64
105
|
function emitChange() {
|
|
65
|
-
const detail = { value, selectedItem, status };
|
|
106
|
+
const detail = { value, selectedItem, selectedItems, status };
|
|
66
107
|
onChange?.(detail);
|
|
67
108
|
}
|
|
68
109
|
|
|
@@ -76,7 +117,9 @@
|
|
|
76
117
|
function resetSearchState(nextValue: string) {
|
|
77
118
|
lastHandledValue = nextValue;
|
|
78
119
|
value = nextValue;
|
|
79
|
-
|
|
120
|
+
if (!multiselect) {
|
|
121
|
+
selectedItem = null;
|
|
122
|
+
}
|
|
80
123
|
exactMatch = null;
|
|
81
124
|
options = [];
|
|
82
125
|
const trimmed = normalizeDropdownSearchValue(nextValue);
|
|
@@ -85,7 +128,17 @@
|
|
|
85
128
|
setStatus('loading');
|
|
86
129
|
} else {
|
|
87
130
|
abortActiveSearch();
|
|
88
|
-
setStatus(
|
|
131
|
+
setStatus(
|
|
132
|
+
resolveDropdownSearchStatus({
|
|
133
|
+
value,
|
|
134
|
+
selectedItem,
|
|
135
|
+
selectedItems,
|
|
136
|
+
exactMatch,
|
|
137
|
+
minLength,
|
|
138
|
+
validate,
|
|
139
|
+
multiselect,
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
89
142
|
}
|
|
90
143
|
emitChange();
|
|
91
144
|
}
|
|
@@ -97,6 +150,13 @@
|
|
|
97
150
|
}
|
|
98
151
|
}
|
|
99
152
|
|
|
153
|
+
function clearBlurTimer() {
|
|
154
|
+
if (blurTimer) {
|
|
155
|
+
clearTimeout(blurTimer);
|
|
156
|
+
blurTimer = undefined;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
100
160
|
function abortActiveSearch() {
|
|
101
161
|
if (activeController) {
|
|
102
162
|
activeController.abort();
|
|
@@ -111,7 +171,15 @@
|
|
|
111
171
|
abortActiveSearch();
|
|
112
172
|
options = [];
|
|
113
173
|
exactMatch = null;
|
|
114
|
-
setStatus(
|
|
174
|
+
setStatus(
|
|
175
|
+
resolveDropdownSearchStatus({
|
|
176
|
+
value: query,
|
|
177
|
+
selectedItems,
|
|
178
|
+
minLength,
|
|
179
|
+
validate,
|
|
180
|
+
multiselect,
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
115
183
|
emitChange();
|
|
116
184
|
return;
|
|
117
185
|
}
|
|
@@ -124,7 +192,10 @@
|
|
|
124
192
|
emitChange();
|
|
125
193
|
|
|
126
194
|
try {
|
|
127
|
-
const result = await loadOptions(trimmed, {
|
|
195
|
+
const result = await loadOptions(trimmed, {
|
|
196
|
+
limit,
|
|
197
|
+
signal: controller.signal,
|
|
198
|
+
});
|
|
128
199
|
|
|
129
200
|
if (controller.signal.aborted || currentRequestId !== requestId) {
|
|
130
201
|
return;
|
|
@@ -132,8 +203,21 @@
|
|
|
132
203
|
|
|
133
204
|
options = result.options ?? [];
|
|
134
205
|
exactMatch = validate ? (result.exactMatch ?? null) : null;
|
|
135
|
-
selectedItem =
|
|
136
|
-
|
|
206
|
+
selectedItem =
|
|
207
|
+
!multiselect && validate && exactMatch && !exactMatch.disabled
|
|
208
|
+
? exactMatch
|
|
209
|
+
: null;
|
|
210
|
+
setStatus(
|
|
211
|
+
resolveDropdownSearchStatus({
|
|
212
|
+
value,
|
|
213
|
+
selectedItem,
|
|
214
|
+
selectedItems,
|
|
215
|
+
exactMatch: multiselect ? null : exactMatch,
|
|
216
|
+
minLength,
|
|
217
|
+
validate,
|
|
218
|
+
multiselect,
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
137
221
|
emitChange();
|
|
138
222
|
} catch (error) {
|
|
139
223
|
if (controller.signal.aborted || currentRequestId !== requestId) {
|
|
@@ -143,7 +227,16 @@
|
|
|
143
227
|
options = [];
|
|
144
228
|
exactMatch = null;
|
|
145
229
|
selectedItem = null;
|
|
146
|
-
setStatus(
|
|
230
|
+
setStatus(
|
|
231
|
+
resolveDropdownSearchStatus({
|
|
232
|
+
value: query,
|
|
233
|
+
selectedItems,
|
|
234
|
+
errored: true,
|
|
235
|
+
minLength,
|
|
236
|
+
validate,
|
|
237
|
+
multiselect,
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
147
240
|
emitChange();
|
|
148
241
|
} finally {
|
|
149
242
|
if (activeController === controller) {
|
|
@@ -159,10 +252,57 @@
|
|
|
159
252
|
}, debounceMs);
|
|
160
253
|
}
|
|
161
254
|
|
|
255
|
+
function handleInputValue(nextValue: string) {
|
|
256
|
+
clearBlurTimer();
|
|
257
|
+
focused = true;
|
|
258
|
+
inputValue = nextValue;
|
|
259
|
+
resetSearchState(nextValue);
|
|
260
|
+
scheduleSearch(nextValue);
|
|
261
|
+
}
|
|
262
|
+
|
|
162
263
|
function handleInput(event: Event) {
|
|
163
264
|
const input = event.currentTarget as HTMLInputElement;
|
|
164
|
-
|
|
165
|
-
|
|
265
|
+
handleInputValue(input.value);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function handleBeforeInput() {
|
|
269
|
+
setTimeout(() => {
|
|
270
|
+
if (!inputElement || inputElement.value === lastHandledValue) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
handleInputValue(inputElement.value);
|
|
275
|
+
}, 0);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function handleFocus() {
|
|
279
|
+
clearBlurTimer();
|
|
280
|
+
focused = true;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function handleBlur(event: FocusEvent) {
|
|
284
|
+
onInputBlur?.(event);
|
|
285
|
+
clearBlurTimer();
|
|
286
|
+
blurTimer = setTimeout(() => {
|
|
287
|
+
focused = document.activeElement === inputElement;
|
|
288
|
+
blurTimer = undefined;
|
|
289
|
+
}, 120);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
293
|
+
if (event.key !== 'Enter' || multiselect) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
onEnter?.({
|
|
298
|
+
event,
|
|
299
|
+
value,
|
|
300
|
+
selectedItem,
|
|
301
|
+
selectedItems,
|
|
302
|
+
status,
|
|
303
|
+
exactMatch,
|
|
304
|
+
options,
|
|
305
|
+
});
|
|
166
306
|
}
|
|
167
307
|
|
|
168
308
|
function selectItem(item: DropdownSearchItem) {
|
|
@@ -170,11 +310,17 @@
|
|
|
170
310
|
return;
|
|
171
311
|
}
|
|
172
312
|
|
|
313
|
+
if (multiselect) {
|
|
314
|
+
toggleSelectedItem(item);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
173
318
|
clearSearchTimer();
|
|
174
319
|
abortActiveSearch();
|
|
175
320
|
selectedItem = item;
|
|
176
321
|
exactMatch = validate ? item : null;
|
|
177
322
|
value = getItemValue(item);
|
|
323
|
+
inputValue = value;
|
|
178
324
|
lastHandledValue = value;
|
|
179
325
|
setStatus(validate ? 'valid' : 'empty');
|
|
180
326
|
focused = false;
|
|
@@ -182,6 +328,85 @@
|
|
|
182
328
|
onSelect?.(item);
|
|
183
329
|
}
|
|
184
330
|
|
|
331
|
+
function selectedItemKey(item: DropdownSearchItem): string {
|
|
332
|
+
return String(item.id);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function isSelectedItem(item: DropdownSearchItem): boolean {
|
|
336
|
+
const key = selectedItemKey(item);
|
|
337
|
+
return selectedItems.some((selected) => selectedItemKey(selected) === key);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function resolveRemoveSelectedItemLabel(item: DropdownSearchItem): string {
|
|
341
|
+
return removeSelectedItemLabel?.(item) ?? `Remove ${item.title}`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function setSelectedItems(nextItems: DropdownSearchItem[]) {
|
|
345
|
+
selectedItems = nextItems;
|
|
346
|
+
void onSelectedItemsChange?.(selectedItems);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function toggleSelectedItem(item: DropdownSearchItem) {
|
|
350
|
+
const selected = isSelectedItem(item);
|
|
351
|
+
const nextItems = selected
|
|
352
|
+
? selectedItems.filter(
|
|
353
|
+
(selectedItem) =>
|
|
354
|
+
selectedItemKey(selectedItem) !== selectedItemKey(item),
|
|
355
|
+
)
|
|
356
|
+
: [...selectedItems, item];
|
|
357
|
+
|
|
358
|
+
clearSearchTimer();
|
|
359
|
+
abortActiveSearch();
|
|
360
|
+
setSelectedItems(nextItems);
|
|
361
|
+
selectedItem = null;
|
|
362
|
+
exactMatch = null;
|
|
363
|
+
value = '';
|
|
364
|
+
inputValue = value;
|
|
365
|
+
lastHandledValue = value;
|
|
366
|
+
setStatus(
|
|
367
|
+
resolveDropdownSearchStatus({
|
|
368
|
+
value,
|
|
369
|
+
selectedItems,
|
|
370
|
+
minLength,
|
|
371
|
+
validate,
|
|
372
|
+
multiselect,
|
|
373
|
+
}),
|
|
374
|
+
);
|
|
375
|
+
focused = true;
|
|
376
|
+
emitChange();
|
|
377
|
+
|
|
378
|
+
if (selected) {
|
|
379
|
+
onDeselect?.(item);
|
|
380
|
+
} else {
|
|
381
|
+
onSelect?.(item);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
inputElement?.focus();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function removeSelectedItem(item: DropdownSearchItem) {
|
|
388
|
+
if (disabled || !multiselect) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const nextItems = selectedItems.filter(
|
|
393
|
+
(selectedItem) => selectedItemKey(selectedItem) !== selectedItemKey(item),
|
|
394
|
+
);
|
|
395
|
+
setSelectedItems(nextItems);
|
|
396
|
+
setStatus(
|
|
397
|
+
resolveDropdownSearchStatus({
|
|
398
|
+
value,
|
|
399
|
+
selectedItems,
|
|
400
|
+
minLength,
|
|
401
|
+
validate,
|
|
402
|
+
multiselect,
|
|
403
|
+
}),
|
|
404
|
+
);
|
|
405
|
+
emitChange();
|
|
406
|
+
onDeselect?.(item);
|
|
407
|
+
inputElement?.focus();
|
|
408
|
+
}
|
|
409
|
+
|
|
185
410
|
function clearSearch() {
|
|
186
411
|
if (!hasQuery || disabled) {
|
|
187
412
|
return;
|
|
@@ -190,25 +415,65 @@
|
|
|
190
415
|
clearSearchTimer();
|
|
191
416
|
abortActiveSearch();
|
|
192
417
|
value = '';
|
|
418
|
+
inputValue = value;
|
|
193
419
|
lastHandledValue = value;
|
|
194
|
-
|
|
420
|
+
if (!multiselect) {
|
|
421
|
+
selectedItem = null;
|
|
422
|
+
}
|
|
195
423
|
exactMatch = null;
|
|
196
424
|
options = [];
|
|
197
|
-
setStatus(
|
|
425
|
+
setStatus(
|
|
426
|
+
resolveDropdownSearchStatus({
|
|
427
|
+
value,
|
|
428
|
+
selectedItem,
|
|
429
|
+
selectedItems,
|
|
430
|
+
exactMatch,
|
|
431
|
+
minLength,
|
|
432
|
+
validate,
|
|
433
|
+
multiselect,
|
|
434
|
+
}),
|
|
435
|
+
);
|
|
198
436
|
focused = true;
|
|
199
437
|
emitChange();
|
|
200
438
|
inputElement?.focus();
|
|
201
439
|
}
|
|
202
440
|
|
|
441
|
+
function handleIncomingValue(nextValue: string) {
|
|
442
|
+
inputValue = nextValue;
|
|
443
|
+
|
|
444
|
+
if (searchOnExternalValueChange) {
|
|
445
|
+
handleExternalValue(nextValue);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
lastHandledValue = nextValue;
|
|
450
|
+
}
|
|
451
|
+
|
|
203
452
|
function handleExternalValue(nextValue: string) {
|
|
204
453
|
lastHandledValue = nextValue;
|
|
205
|
-
|
|
454
|
+
inputValue = nextValue;
|
|
455
|
+
if (!multiselect) {
|
|
456
|
+
selectedItem = null;
|
|
457
|
+
}
|
|
206
458
|
exactMatch = null;
|
|
207
459
|
options = [];
|
|
208
460
|
|
|
209
461
|
clearSearchTimer();
|
|
210
|
-
if (
|
|
211
|
-
|
|
462
|
+
if (
|
|
463
|
+
!normalizeDropdownSearchValue(nextValue) ||
|
|
464
|
+
normalizeDropdownSearchValue(nextValue).length < minLength
|
|
465
|
+
) {
|
|
466
|
+
setStatus(
|
|
467
|
+
resolveDropdownSearchStatus({
|
|
468
|
+
value: nextValue,
|
|
469
|
+
selectedItem,
|
|
470
|
+
selectedItems,
|
|
471
|
+
exactMatch,
|
|
472
|
+
minLength,
|
|
473
|
+
validate,
|
|
474
|
+
multiselect,
|
|
475
|
+
}),
|
|
476
|
+
);
|
|
212
477
|
emitChange();
|
|
213
478
|
return;
|
|
214
479
|
}
|
|
@@ -218,6 +483,7 @@
|
|
|
218
483
|
|
|
219
484
|
onDestroy(() => {
|
|
220
485
|
clearSearchTimer();
|
|
486
|
+
clearBlurTimer();
|
|
221
487
|
abortActiveSearch();
|
|
222
488
|
});
|
|
223
489
|
|
|
@@ -230,14 +496,47 @@
|
|
|
230
496
|
|
|
231
497
|
<div
|
|
232
498
|
class={`suu-dropdown-search suu-dropdown-search--${status}`}
|
|
499
|
+
class:suu-dropdown-search--multi={multiselect}
|
|
233
500
|
class:suu-dropdown-search--has-clear={hasQuery && !disabled}
|
|
234
501
|
data-status={status}
|
|
502
|
+
data-multiselect={multiselect}
|
|
235
503
|
style:width
|
|
236
504
|
style:min-width={minWidth}
|
|
237
505
|
style:max-width={maxWidth}
|
|
238
506
|
>
|
|
507
|
+
{#if multiselect && selectedItems.length > 0}
|
|
508
|
+
<div
|
|
509
|
+
class="suu-dropdown-search__selected-items"
|
|
510
|
+
aria-label={selectedItemsLabel}
|
|
511
|
+
>
|
|
512
|
+
{#each selectedItems as item (item.id)}
|
|
513
|
+
<span class="suu-dropdown-search__selected-item">
|
|
514
|
+
<span class="suu-dropdown-search__selected-title">{item.title}</span>
|
|
515
|
+
{#if !disabled}
|
|
516
|
+
<button
|
|
517
|
+
type="button"
|
|
518
|
+
class="suu-dropdown-search__selected-remove"
|
|
519
|
+
aria-label={resolveRemoveSelectedItemLabel(item)}
|
|
520
|
+
title={resolveRemoveSelectedItemLabel(item)}
|
|
521
|
+
on:mousedown|preventDefault
|
|
522
|
+
on:click={() => removeSelectedItem(item)}
|
|
523
|
+
>
|
|
524
|
+
<svg viewBox="0 0 20 20" aria-hidden="true">
|
|
525
|
+
<path d="m6 6 8 8M14 6l-8 8"></path>
|
|
526
|
+
</svg>
|
|
527
|
+
</button>
|
|
528
|
+
{/if}
|
|
529
|
+
</span>
|
|
530
|
+
{/each}
|
|
531
|
+
</div>
|
|
532
|
+
{/if}
|
|
533
|
+
|
|
239
534
|
<div class="suu-dropdown-search__field">
|
|
240
|
-
<svg
|
|
535
|
+
<svg
|
|
536
|
+
class="suu-dropdown-search__icon"
|
|
537
|
+
viewBox="0 0 24 24"
|
|
538
|
+
aria-hidden="true"
|
|
539
|
+
>
|
|
241
540
|
<circle cx="11" cy="11" r="7"></circle>
|
|
242
541
|
<path d="m16 16 4 4"></path>
|
|
243
542
|
</svg>
|
|
@@ -246,7 +545,7 @@
|
|
|
246
545
|
class="suu-dropdown-search__input"
|
|
247
546
|
{id}
|
|
248
547
|
{name}
|
|
249
|
-
{
|
|
548
|
+
bind:value={inputValue}
|
|
250
549
|
{placeholder}
|
|
251
550
|
aria-label={ariaLabel}
|
|
252
551
|
{disabled}
|
|
@@ -255,12 +554,11 @@
|
|
|
255
554
|
aria-controls={resolvedListboxId}
|
|
256
555
|
aria-expanded={showOptions}
|
|
257
556
|
aria-invalid={status === 'invalid' || status === 'error'}
|
|
557
|
+
on:beforeinput={handleBeforeInput}
|
|
258
558
|
on:input={handleInput}
|
|
259
|
-
on:
|
|
260
|
-
on:
|
|
261
|
-
|
|
262
|
-
setTimeout(() => (focused = false), 120);
|
|
263
|
-
}}
|
|
559
|
+
on:keydown={handleKeydown}
|
|
560
|
+
on:focus={handleFocus}
|
|
561
|
+
on:blur={handleBlur}
|
|
264
562
|
/>
|
|
265
563
|
{#if hasQuery && !disabled}
|
|
266
564
|
<button
|
|
@@ -280,10 +578,19 @@
|
|
|
280
578
|
|
|
281
579
|
{#if showOptions}
|
|
282
580
|
<div class="suu-dropdown-search__menu">
|
|
283
|
-
<div
|
|
581
|
+
<div
|
|
582
|
+
class="suu-dropdown-search__menu-panel"
|
|
583
|
+
id={resolvedListboxId}
|
|
584
|
+
role="listbox"
|
|
585
|
+
aria-multiselectable={multiselect}
|
|
586
|
+
>
|
|
284
587
|
{#if status === 'loading'}
|
|
285
|
-
<div
|
|
286
|
-
|
|
588
|
+
<div
|
|
589
|
+
class="suu-dropdown-search__empty suu-dropdown-search__empty--loading"
|
|
590
|
+
aria-live="polite"
|
|
591
|
+
>
|
|
592
|
+
<span class="suu-dropdown-search__spinner" aria-hidden="true"
|
|
593
|
+
></span>
|
|
287
594
|
<span>{resolvedLoadingText}</span>
|
|
288
595
|
</div>
|
|
289
596
|
{:else if options.length > 0}
|
|
@@ -292,14 +599,27 @@
|
|
|
292
599
|
type="button"
|
|
293
600
|
class="suu-dropdown-search__option"
|
|
294
601
|
class:suu-dropdown-search__option--disabled={option.disabled}
|
|
602
|
+
class:suu-dropdown-search__option--selected={multiselect &&
|
|
603
|
+
isSelectedItem(option)}
|
|
295
604
|
role="option"
|
|
296
|
-
aria-selected={
|
|
605
|
+
aria-selected={multiselect
|
|
606
|
+
? isSelectedItem(option)
|
|
607
|
+
: selectedItem?.id === option.id}
|
|
297
608
|
disabled={option.disabled}
|
|
298
609
|
on:mousedown|preventDefault={() => selectItem(option)}
|
|
299
610
|
>
|
|
611
|
+
{#if multiselect}
|
|
612
|
+
<span class="suu-dropdown-search__check" aria-hidden="true">
|
|
613
|
+
<svg viewBox="0 0 20 20">
|
|
614
|
+
<path d="m4.5 10.5 3.5 3.5 7.5-8"></path>
|
|
615
|
+
</svg>
|
|
616
|
+
</span>
|
|
617
|
+
{/if}
|
|
300
618
|
<span class="suu-dropdown-search__title">{option.title}</span>
|
|
301
619
|
{#if formatParamDict(option.param_dict).length > 0}
|
|
302
|
-
<span class="suu-dropdown-search__meta"
|
|
620
|
+
<span class="suu-dropdown-search__meta"
|
|
621
|
+
>{formatParamDict(option.param_dict).join(' · ')}</span
|
|
622
|
+
>
|
|
303
623
|
{/if}
|
|
304
624
|
</button>
|
|
305
625
|
{/each}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type UiLanguage } from '../i18n.js';
|
|
2
|
-
import type { DropdownSearchChangeDetail, DropdownSearchItem, DropdownSearchItemValueGetter, DropdownSearchLoadOptions, DropdownSearchStatus } from './types.js';
|
|
2
|
+
import type { DropdownSearchChangeDetail, DropdownSearchEnterDetail, DropdownSearchItem, DropdownSearchItemValueGetter, DropdownSearchLoadOptions, DropdownSearchSelectedItemLabelGetter, DropdownSearchSelectedItemsChangeHandler, DropdownSearchStatus } from './types.js';
|
|
3
3
|
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
4
4
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
5
5
|
$$bindings?: Bindings;
|
|
@@ -16,7 +16,9 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
|
|
|
16
16
|
declare const DropdownSearch: $$__sveltets_2_IsomorphicComponent<{
|
|
17
17
|
value?: string;
|
|
18
18
|
selectedItem?: DropdownSearchItem | null;
|
|
19
|
+
selectedItems?: DropdownSearchItem[];
|
|
19
20
|
status?: DropdownSearchStatus;
|
|
21
|
+
multiselect?: boolean;
|
|
20
22
|
placeholder?: string;
|
|
21
23
|
debounceMs?: number;
|
|
22
24
|
limit?: number;
|
|
@@ -36,11 +38,16 @@ declare const DropdownSearch: $$__sveltets_2_IsomorphicComponent<{
|
|
|
36
38
|
width?: string | undefined;
|
|
37
39
|
minWidth?: string | undefined;
|
|
38
40
|
maxWidth?: string | undefined;
|
|
41
|
+
selectedItemsLabel?: string;
|
|
42
|
+
removeSelectedItemLabel?: DropdownSearchSelectedItemLabelGetter | undefined;
|
|
39
43
|
getItemValue?: DropdownSearchItemValueGetter;
|
|
40
44
|
onChange?: ((detail: DropdownSearchChangeDetail) => void) | undefined;
|
|
41
45
|
onSelect?: ((item: DropdownSearchItem) => void) | undefined;
|
|
46
|
+
onDeselect?: ((item: DropdownSearchItem) => void) | undefined;
|
|
47
|
+
onSelectedItemsChange?: DropdownSearchSelectedItemsChangeHandler | undefined;
|
|
42
48
|
onStatusChange?: ((status: DropdownSearchStatus) => void) | undefined;
|
|
43
49
|
onInputBlur?: ((event: FocusEvent) => void) | undefined;
|
|
50
|
+
onEnter?: ((detail: DropdownSearchEnterDetail) => void) | undefined;
|
|
44
51
|
}, {
|
|
45
52
|
mousedown: MouseEvent;
|
|
46
53
|
} & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownSearch.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown-search/DropdownSearch.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"DropdownSearch.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown-search/DropdownSearch.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAM5D,OAAO,KAAK,EACR,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,EAClB,6BAA6B,EAC7B,yBAAyB,EACzB,qCAAqC,EACrC,wCAAwC,EACxC,oBAAoB,EACrB,MAAM,YAAY,CAAC;AA+iBtB,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,cAAc;;mBAdq5B,kBAAkB,GAAG,IAAI;oBAAkB,kBAAkB,EAAE;aAAW,oBAAoB;;;;;;;iBAAmM,yBAAyB;SAAO,MAAM,GAAG,SAAS;WAAS,MAAM,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;;eAAyC,UAAU;oBAAkB,MAAM,GAAG,SAAS;kBAAgB,MAAM,GAAG,SAAS;iBAAe,MAAM,GAAG,SAAS;;YAA4E,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;;8BAA8E,qCAAqC,GAC1vD,SAAS;mBAAiB,6BAA6B;eAAe,CAAC,CAAC,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC,GACpH,SAAS;eAAa,CAAC,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC,GAAG,SAAS;iBAAe,CAAC,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC,GAAG,SAAS;4BAA4B,wCAAwC,GACzM,SAAS;qBAAqB,CAAC,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC,GACtE,SAAS;kBAAgB,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,SAAS;cAAY,CAAC,CAAC,MAAM,EAAE,yBAAyB,KAAK,IAAI,CAAC,GAAG,SAAS;;;;;kBAU1C,CAAC;AAClF,KAAK,cAAc,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC;AAC5D,eAAe,cAAc,CAAC"}
|