@placeholderco/placeholder-ui 3.0.0 → 3.0.2
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/form/Autocomplete.svelte +2 -0
- package/dist/form/AutocompleteMulti.svelte +1 -0
- package/dist/form/Select.svelte +10 -2
- package/dist/form/SelectMulti.svelte +10 -2
- package/dist/form/comboBoxCore.svelte.d.ts +7 -1
- package/dist/form/comboBoxCore.svelte.js +31 -4
- package/dist/models/ComboBoxItem.d.ts +6 -1
- package/package.json +7 -7
|
@@ -242,6 +242,7 @@
|
|
|
242
242
|
|
|
243
243
|
function onFilterChange(filterValue: string) {
|
|
244
244
|
if (filterValue.length === 0) {
|
|
245
|
+
core.cancelSearch();
|
|
245
246
|
core.filterLocal('');
|
|
246
247
|
open = true;
|
|
247
248
|
return;
|
|
@@ -250,6 +251,7 @@
|
|
|
250
251
|
if (searchFunction && filterValue) {
|
|
251
252
|
core.search(filterValue, () => (open = true));
|
|
252
253
|
} else {
|
|
254
|
+
core.cancelSearch();
|
|
253
255
|
core.filterLocal(filterValue);
|
|
254
256
|
open = true;
|
|
255
257
|
}
|
package/dist/form/Select.svelte
CHANGED
|
@@ -170,7 +170,14 @@
|
|
|
170
170
|
|
|
171
171
|
function focus() {
|
|
172
172
|
open = true;
|
|
173
|
-
tick().then(
|
|
173
|
+
tick().then(focusTextbox);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Select any leftover filter text on reopen so typing replaces it
|
|
177
|
+
function focusTextbox() {
|
|
178
|
+
const input = textboxElement as HTMLInputElement | undefined;
|
|
179
|
+
input?.focus();
|
|
180
|
+
input?.select();
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
$effect(() => {
|
|
@@ -205,6 +212,7 @@
|
|
|
205
212
|
if (searchFunction && filterValue) {
|
|
206
213
|
core.search(filterValue, () => (open = true));
|
|
207
214
|
} else {
|
|
215
|
+
core.cancelSearch();
|
|
208
216
|
core.filterLocal(filterValue);
|
|
209
217
|
}
|
|
210
218
|
}
|
|
@@ -319,7 +327,7 @@
|
|
|
319
327
|
onclick={() => {
|
|
320
328
|
open = !open;
|
|
321
329
|
if (open && computedAllowSearch) {
|
|
322
|
-
tick().then(
|
|
330
|
+
tick().then(focusTextbox);
|
|
323
331
|
}
|
|
324
332
|
}}
|
|
325
333
|
onkeydown={(e) => onKeyDown(e)}
|
|
@@ -181,7 +181,14 @@
|
|
|
181
181
|
|
|
182
182
|
function focus() {
|
|
183
183
|
open = true;
|
|
184
|
-
tick().then(
|
|
184
|
+
tick().then(focusTextbox);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Select any leftover filter text on reopen so typing replaces it
|
|
188
|
+
function focusTextbox() {
|
|
189
|
+
const input = textboxElement as HTMLInputElement | undefined;
|
|
190
|
+
input?.focus();
|
|
191
|
+
input?.select();
|
|
185
192
|
}
|
|
186
193
|
|
|
187
194
|
$effect(() => {
|
|
@@ -240,6 +247,7 @@
|
|
|
240
247
|
hideCombobox = false;
|
|
241
248
|
});
|
|
242
249
|
} else {
|
|
250
|
+
core.cancelSearch();
|
|
243
251
|
core.filterLocal(value);
|
|
244
252
|
hideCombobox = false;
|
|
245
253
|
}
|
|
@@ -294,7 +302,7 @@
|
|
|
294
302
|
onclick={() => {
|
|
295
303
|
open = !open;
|
|
296
304
|
if (open && computedAllowSearch) {
|
|
297
|
-
tick().then(
|
|
305
|
+
tick().then(focusTextbox);
|
|
298
306
|
}
|
|
299
307
|
}}
|
|
300
308
|
onkeydown={(e) => onKeyDown(e)}
|
|
@@ -82,8 +82,14 @@ export declare abstract class ComboBoxDataCore<TConfig extends ComboBoxDataConfi
|
|
|
82
82
|
abstract preloadValues(): boolean;
|
|
83
83
|
convertOptions(options: ComboBoxItem[]): void;
|
|
84
84
|
convertGroupOptions(groups: ComboBoxGroup[]): void;
|
|
85
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Debounced remote search via searchFunction. Each call supersedes the previous one:
|
|
87
|
+
* a pending debounce is dropped and an in-flight request is aborted, so only the
|
|
88
|
+
* latest query's results are ever applied.
|
|
89
|
+
*/
|
|
86
90
|
search(filterValue: string, onComplete?: () => void): void;
|
|
91
|
+
/** Drop any pending or in-flight remote search, e.g. when the input switches to local filtering. */
|
|
92
|
+
cancelSearch(): void;
|
|
87
93
|
/** Case-insensitive local filter over already-loaded options. */
|
|
88
94
|
filterLocal(filterValue: string): void;
|
|
89
95
|
}
|
|
@@ -21,6 +21,7 @@ export class ComboBoxDataCore {
|
|
|
21
21
|
searching = $state(false);
|
|
22
22
|
config;
|
|
23
23
|
#timeout;
|
|
24
|
+
#searchController;
|
|
24
25
|
#fetchInitialized = false;
|
|
25
26
|
#lastOptionsRef;
|
|
26
27
|
#lastGroupedRef;
|
|
@@ -62,7 +63,7 @@ export class ComboBoxDataCore {
|
|
|
62
63
|
this.preloadValues();
|
|
63
64
|
cfg.onOptionsChanged?.();
|
|
64
65
|
});
|
|
65
|
-
$effect(() => () =>
|
|
66
|
+
$effect(() => () => this.#abortPendingSearch());
|
|
66
67
|
}
|
|
67
68
|
convertOptions(options) {
|
|
68
69
|
const items = options.map((option) => ({ ...option, selected: this.isSelected(option) }));
|
|
@@ -80,18 +81,44 @@ export class ComboBoxDataCore {
|
|
|
80
81
|
}));
|
|
81
82
|
this.filteredGroups = [...this.allGroups];
|
|
82
83
|
}
|
|
83
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Debounced remote search via searchFunction. Each call supersedes the previous one:
|
|
86
|
+
* a pending debounce is dropped and an in-flight request is aborted, so only the
|
|
87
|
+
* latest query's results are ever applied.
|
|
88
|
+
*/
|
|
84
89
|
search(filterValue, onComplete) {
|
|
85
|
-
|
|
90
|
+
this.#abortPendingSearch();
|
|
86
91
|
this.searching = true;
|
|
87
92
|
this.#timeout = setTimeout(() => {
|
|
88
|
-
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
this.#searchController = controller;
|
|
95
|
+
this.config.getSearchFunction()(filterValue, controller.signal).then((response) => {
|
|
96
|
+
if (controller.signal.aborted)
|
|
97
|
+
return;
|
|
98
|
+
this.#searchController = undefined;
|
|
89
99
|
this.convertOptions(response.object ?? []);
|
|
90
100
|
this.searching = false;
|
|
91
101
|
onComplete?.();
|
|
102
|
+
}, (error) => {
|
|
103
|
+
// Aborted requests reject by design (e.g. fetch's AbortError) — swallow those.
|
|
104
|
+
if (controller.signal.aborted)
|
|
105
|
+
return;
|
|
106
|
+
this.#searchController = undefined;
|
|
107
|
+
this.searching = false;
|
|
108
|
+
throw error;
|
|
92
109
|
});
|
|
93
110
|
}, 300);
|
|
94
111
|
}
|
|
112
|
+
/** Drop any pending or in-flight remote search, e.g. when the input switches to local filtering. */
|
|
113
|
+
cancelSearch() {
|
|
114
|
+
this.#abortPendingSearch();
|
|
115
|
+
this.searching = false;
|
|
116
|
+
}
|
|
117
|
+
#abortPendingSearch() {
|
|
118
|
+
clearTimeout(this.#timeout);
|
|
119
|
+
this.#searchController?.abort();
|
|
120
|
+
this.#searchController = undefined;
|
|
121
|
+
}
|
|
95
122
|
/** Case-insensitive local filter over already-loaded options. */
|
|
96
123
|
filterLocal(filterValue) {
|
|
97
124
|
if (filterValue.length === 0) {
|
|
@@ -23,5 +23,10 @@ export interface ComboBoxItem {
|
|
|
23
23
|
isAddNew?: boolean;
|
|
24
24
|
}
|
|
25
25
|
export type FetchFunctionType = () => Promise<NotifyModel<ComboBoxItem[]>>;
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Remote search. `signal` is aborted when a newer search supersedes this one (the user
|
|
28
|
+
* kept typing, cleared the input, or the component unmounted) — pass it to `fetch` to
|
|
29
|
+
* cancel the request. Results from superseded searches are discarded either way.
|
|
30
|
+
*/
|
|
31
|
+
export type SearchFunctionType = (query: string, signal?: AbortSignal) => Promise<NotifyModel<ComboBoxItem[]>>;
|
|
27
32
|
export type RetrieveLabelFunctionType = (value: any) => Promise<NotifyModel<string | undefined>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@placeholderco/placeholder-ui",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "A modern, customizable Svelte 5 UI component library with comprehensive theming support. Re-brand it with a single config object.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Placeholderco",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@sveltejs/adapter-static": "^4.0.0",
|
|
56
|
-
"@sveltejs/kit": "^3.0.
|
|
56
|
+
"@sveltejs/kit": "^3.0.1",
|
|
57
57
|
"@sveltejs/package": "^3.0.0",
|
|
58
58
|
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
59
59
|
"@types/node": "^26.6.4",
|
|
@@ -62,10 +62,10 @@
|
|
|
62
62
|
"prettier-plugin-svelte": "^4.1.1",
|
|
63
63
|
"publint": "^0.3.25",
|
|
64
64
|
"rimraf": "^6.1.3",
|
|
65
|
-
"svelte": "^5.57.
|
|
65
|
+
"svelte": "^5.57.2",
|
|
66
66
|
"svelte-check": "^4.7.6",
|
|
67
67
|
"typescript": "^6.0.3",
|
|
68
|
-
"vite": "^8.3.
|
|
68
|
+
"vite": "^8.3.3"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"@codemirror/autocomplete": "^6.20.3",
|
|
@@ -76,13 +76,13 @@
|
|
|
76
76
|
"@codemirror/lang-json": "^6.0.2",
|
|
77
77
|
"@codemirror/lang-markdown": "^6.5.2",
|
|
78
78
|
"@codemirror/lang-sql": "^6.10.0",
|
|
79
|
-
"@codemirror/language": "^6.
|
|
80
|
-
"@codemirror/legacy-modes": "^6.5.
|
|
79
|
+
"@codemirror/language": "^6.13.1",
|
|
80
|
+
"@codemirror/legacy-modes": "^6.5.5",
|
|
81
81
|
"@codemirror/lint": "^6.9.7",
|
|
82
82
|
"@codemirror/search": "^6.7.2",
|
|
83
83
|
"@codemirror/state": "^6.7.6",
|
|
84
84
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
85
|
-
"@codemirror/view": "^6.43.
|
|
85
|
+
"@codemirror/view": "^6.43.14",
|
|
86
86
|
"@lezer/highlight": "^1.2.5",
|
|
87
87
|
"@tiptap/core": "^3.31.4",
|
|
88
88
|
"@tiptap/extension-bubble-menu": "^3.31.4",
|