@placeholderco/placeholder-ui 1.0.10 → 1.0.12
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 +307 -125
- package/dist/form/Autocomplete.svelte.d.ts +59 -9
- package/dist/form/Checkbox.svelte +85 -9
- package/dist/form/Checkbox.svelte.d.ts +15 -0
- package/dist/form/Chips.svelte +18 -4
- package/dist/form/Chips.svelte.d.ts +8 -1
- package/dist/form/DatePicker.svelte +43 -10
- package/dist/form/DatePicker.svelte.d.ts +17 -2
- package/dist/form/DateRangePicker.svelte +772 -0
- package/dist/form/DateRangePicker.svelte.d.ts +23 -0
- package/dist/form/DateTimePicker.svelte +1 -1
- package/dist/form/Number.svelte +28 -9
- package/dist/form/Number.svelte.d.ts +4 -1
- package/dist/form/PasswordInput.svelte +4 -3
- package/dist/form/PasswordInput.svelte.d.ts +2 -1
- package/dist/form/Radio.svelte +174 -0
- package/dist/form/Radio.svelte.d.ts +21 -0
- package/dist/form/RadioGroup.svelte +33 -130
- package/dist/form/RadioGroup.svelte.d.ts +6 -0
- package/dist/form/SegmentedControl.svelte +41 -15
- package/dist/form/SegmentedControl.svelte.d.ts +5 -0
- package/dist/form/Select.svelte +18 -4
- package/dist/form/Select.svelte.d.ts +6 -1
- package/dist/form/SelectMulti.svelte +4 -1
- package/dist/form/SelectMulti.svelte.d.ts +2 -0
- package/dist/form/Slider.svelte +3 -0
- package/dist/form/Slider.svelte.d.ts +2 -0
- package/dist/form/Switch.svelte +62 -97
- package/dist/form/Switch.svelte.d.ts +0 -1
- package/dist/form/TextArea.svelte +34 -19
- package/dist/form/TextArea.svelte.d.ts +13 -8
- package/dist/form/Textbox.svelte +51 -19
- package/dist/form/Textbox.svelte.d.ts +9 -4
- package/dist/icon/cube-send.svg +1 -0
- package/dist/icon/index.d.ts +6 -0
- package/dist/icon/index.js +6 -0
- package/dist/icon/layout-sidebar-left-collapse.svg +1 -0
- package/dist/icon/layout-sidebar-left-expand.svg +1 -0
- package/dist/icon/paperclip.svg +1 -0
- package/dist/icon/thumb-down.svg +1 -0
- package/dist/icon/thumb-up.svg +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +4 -0
- package/dist/layout/Navbar.svelte +52 -21
- package/dist/layout/Navbar.svelte.d.ts +6 -4
- package/dist/styles/tokens.css +2 -2
- package/dist/ui/Badge.svelte +21 -4
- package/dist/ui/Badge.svelte.d.ts +4 -0
- package/dist/ui/Dialog.svelte +8 -1
- package/dist/ui/Dialog.svelte.d.ts +2 -0
- package/dist/ui/Pagination.svelte +3 -3
- package/dist/ui/ProgressBar.svelte +171 -0
- package/dist/ui/ProgressBar.svelte.d.ts +30 -0
- package/dist/ui/ProgressBarVariant.d.ts +1 -0
- package/dist/ui/ProgressBarVariant.js +1 -0
- package/dist/ui/Table.svelte +144 -6
- package/dist/ui/Table.svelte.d.ts +22 -1
- package/dist/util/interceptLinkClick.d.ts +13 -0
- package/dist/util/interceptLinkClick.js +33 -0
- package/package.json +1 -1
|
@@ -1,181 +1,331 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import ComboBox from "./ComboBox.svelte";
|
|
3
|
-
import { clickOutside } from "../actions/ClickOutside.js";
|
|
4
|
-
import Textbox from "./Textbox.svelte";
|
|
5
|
-
import type { NotifyModel } from "../models/NotifyModel.js";
|
|
1
|
+
<script module lang="ts">
|
|
6
2
|
import type {
|
|
3
|
+
ComboBoxGroup,
|
|
7
4
|
ComboBoxItem,
|
|
8
5
|
FetchFunctionType,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
RetrieveLabelFunctionType,
|
|
7
|
+
SearchFunctionType
|
|
8
|
+
} from '../models/ComboBoxItem.js';
|
|
9
|
+
import type { Snippet } from 'svelte';
|
|
12
10
|
|
|
13
|
-
interface
|
|
11
|
+
export interface AutocompleteProps {
|
|
12
|
+
/** HTML name attribute for the input */
|
|
13
|
+
name?: string;
|
|
14
|
+
/** Label text displayed above the input */
|
|
14
15
|
label?: string;
|
|
16
|
+
/** Current input value (bindable) */
|
|
15
17
|
value?: string;
|
|
18
|
+
/** Full selected item object (bindable) */
|
|
19
|
+
rawValue?: ComboBoxItem;
|
|
20
|
+
/** Placeholder text when input is empty */
|
|
16
21
|
placeholder?: string;
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
/** Mark field as required (shows asterisk) */
|
|
23
|
+
required?: boolean;
|
|
24
|
+
/** CSS classes for the input element */
|
|
25
|
+
class?: string;
|
|
26
|
+
/** CSS classes for the container element */
|
|
27
|
+
containerClass?: string;
|
|
28
|
+
/** Disable the input */
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
/** Show error state styling */
|
|
31
|
+
showError?: boolean;
|
|
32
|
+
/** Error message to display */
|
|
33
|
+
errorText?: string;
|
|
34
|
+
/** Auto-focus the input on mount */
|
|
19
35
|
autofocus?: boolean;
|
|
20
|
-
|
|
36
|
+
/** Auto-focus on every dialog open */
|
|
37
|
+
autofocusDialog?: boolean;
|
|
38
|
+
/** Hide "no results" message when search finds nothing */
|
|
39
|
+
hideNoResults?: boolean;
|
|
40
|
+
/** Prevent updating value when an option is selected */
|
|
21
41
|
preventChangeOnSelection?: boolean;
|
|
22
|
-
|
|
42
|
+
/** Text shown while loading options */
|
|
23
43
|
loadingText?: string;
|
|
44
|
+
/** CSS classes for the FormGroup wrapper */
|
|
24
45
|
groupClass?: string;
|
|
46
|
+
/** SVG icon displayed on the left side */
|
|
25
47
|
leftIconSvg?: string;
|
|
48
|
+
/** Static list of autocomplete options */
|
|
26
49
|
options?: ComboBoxItem[];
|
|
27
|
-
|
|
28
|
-
|
|
50
|
+
/** Options organized into groups */
|
|
51
|
+
groupedOptions?: ComboBoxGroup[];
|
|
52
|
+
/** Position of the tooltip */
|
|
53
|
+
tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
|
|
54
|
+
/** Async function to fetch all options */
|
|
55
|
+
fetchFunction?: FetchFunctionType | null;
|
|
56
|
+
/** Async function to search/filter options based on input */
|
|
57
|
+
searchFunction?: SearchFunctionType | null;
|
|
58
|
+
/** Async function to retrieve label for a value */
|
|
59
|
+
retrieveLabelFunction?: RetrieveLabelFunctionType | null;
|
|
60
|
+
/** Rich tooltip content using a Svelte snippet */
|
|
61
|
+
tooltipContent?: Snippet;
|
|
62
|
+
/** Simple tooltip text */
|
|
63
|
+
tooltipText?: string;
|
|
64
|
+
/** Callback when input value changes */
|
|
29
65
|
onchange?: (e: string) => void;
|
|
66
|
+
/** Callback when Enter key is pressed */
|
|
30
67
|
enterPressed?: (e: string) => void;
|
|
68
|
+
/** Callback when an option is selected */
|
|
31
69
|
onSelect?: (e: ComboBoxItem) => void;
|
|
70
|
+
/** Callback when selection changes (returns full item object) */
|
|
71
|
+
onchangeRaw?: (e: ComboBoxItem | undefined) => void;
|
|
72
|
+
/** Allow creating new options not in the list */
|
|
73
|
+
allowCreate?: boolean;
|
|
74
|
+
/** Show a "no results" message when no options match */
|
|
75
|
+
showNoResultsMessage?: boolean;
|
|
76
|
+
/** Show required indicator (alias for required) */
|
|
77
|
+
showRequired?: boolean;
|
|
78
|
+
/** Callback when a new option is created (requires allowCreate) */
|
|
32
79
|
onCreate?: (value: string) => void;
|
|
33
80
|
}
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<script lang="ts">
|
|
84
|
+
import ComboBoxMulti from './ComboBoxMulti.svelte';
|
|
85
|
+
import { clickOutside } from '../actions/ClickOutside.js';
|
|
86
|
+
import Textbox from './Textbox.svelte';
|
|
87
|
+
import type { NotifyModel } from '../models/NotifyModel.js';
|
|
88
|
+
import { onMount, tick } from 'svelte';
|
|
89
|
+
import { useDialogEvents } from '../ui/DialogEvents.svelte.js';
|
|
34
90
|
|
|
35
91
|
let {
|
|
92
|
+
name,
|
|
36
93
|
label,
|
|
37
|
-
value = $bindable(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
94
|
+
value = $bindable(''),
|
|
95
|
+
rawValue = $bindable(undefined),
|
|
96
|
+
placeholder = 'Start typing to search...',
|
|
97
|
+
required = false,
|
|
98
|
+
class: classes = '',
|
|
99
|
+
containerClass = '',
|
|
100
|
+
disabled = false,
|
|
101
|
+
showError = false,
|
|
102
|
+
errorText = '',
|
|
41
103
|
autofocus = false,
|
|
42
|
-
|
|
104
|
+
autofocusDialog = false,
|
|
105
|
+
hideNoResults = false,
|
|
43
106
|
preventChangeOnSelection = false,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
groupClass = "",
|
|
107
|
+
loadingText = '',
|
|
108
|
+
groupClass = '',
|
|
47
109
|
leftIconSvg,
|
|
48
|
-
options,
|
|
49
|
-
|
|
50
|
-
|
|
110
|
+
options = [],
|
|
111
|
+
groupedOptions = [],
|
|
112
|
+
tooltipLocation = 'top',
|
|
113
|
+
fetchFunction = null,
|
|
114
|
+
searchFunction = null,
|
|
115
|
+
retrieveLabelFunction = null,
|
|
116
|
+
tooltipContent,
|
|
117
|
+
tooltipText,
|
|
51
118
|
onchange = undefined,
|
|
52
119
|
enterPressed = undefined,
|
|
53
120
|
onSelect = undefined,
|
|
54
|
-
|
|
55
|
-
|
|
121
|
+
onchangeRaw = undefined,
|
|
122
|
+
allowCreate = false,
|
|
123
|
+
showNoResultsMessage = false,
|
|
124
|
+
showRequired = false,
|
|
125
|
+
onCreate = undefined
|
|
126
|
+
}: AutocompleteProps = $props();
|
|
56
127
|
|
|
57
128
|
let preloading = $state(false);
|
|
58
129
|
let searching = $state(false);
|
|
59
|
-
let timeout
|
|
130
|
+
let timeout: number;
|
|
60
131
|
|
|
61
|
-
let
|
|
62
|
-
let
|
|
132
|
+
let allGroups: ComboBoxGroup[] = $state([]);
|
|
133
|
+
let filteredGroups: ComboBoxGroup[] = $state([]);
|
|
63
134
|
|
|
64
135
|
let open = $state(false);
|
|
136
|
+
let internalValue = $state(value);
|
|
65
137
|
|
|
66
138
|
onMount(() => {
|
|
67
139
|
if (fetchFunction) {
|
|
68
140
|
preloading = true;
|
|
69
141
|
fetchFunction().then((response: NotifyModel<ComboBoxItem[]>) => {
|
|
70
142
|
convertOptions(response.object!);
|
|
143
|
+
preloadValues();
|
|
71
144
|
preloading = false;
|
|
72
145
|
});
|
|
73
146
|
} else {
|
|
74
|
-
if (
|
|
147
|
+
if (groupedOptions.length) {
|
|
148
|
+
convertGroupOptions(groupedOptions);
|
|
149
|
+
} else if (options.length) {
|
|
150
|
+
convertOptions(options);
|
|
151
|
+
}
|
|
152
|
+
preloadValues();
|
|
75
153
|
}
|
|
76
154
|
});
|
|
77
155
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
156
|
+
useDialogEvents({
|
|
157
|
+
onFirstOpen: () => {
|
|
158
|
+
if (autofocus) {
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
doFocus();
|
|
161
|
+
}, 150);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
onOpen: () => {
|
|
165
|
+
if (autofocusDialog) {
|
|
166
|
+
setTimeout(() => {
|
|
167
|
+
doFocus();
|
|
168
|
+
}, 150);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
function doFocus() {
|
|
174
|
+
open = true;
|
|
175
|
+
tick().then(() => textboxElement?.focus());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
$effect(() => {
|
|
179
|
+
if (value !== internalValue) {
|
|
180
|
+
internalValue = value;
|
|
181
|
+
rawValue =
|
|
182
|
+
allGroups.flatMap((x) => x.items).find((x) => x.value === value) ?? undefined;
|
|
183
|
+
onchangeRaw?.(rawValue);
|
|
184
|
+
preloadValues();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
function preloadValues() {
|
|
189
|
+
if (!value) {
|
|
190
|
+
rawValue = undefined;
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const foundOption = allGroups.flatMap((x) => x.items).find((x) => x.value === value);
|
|
195
|
+
|
|
196
|
+
if (foundOption !== undefined) {
|
|
197
|
+
rawValue = foundOption;
|
|
198
|
+
} else if (searchFunction && retrieveLabelFunction) {
|
|
199
|
+
preloading = true;
|
|
200
|
+
retrieveLabelFunction(value).then((response) => {
|
|
201
|
+
rawValue = {
|
|
202
|
+
label: response.object ?? value!.toString(),
|
|
203
|
+
value: value!
|
|
204
|
+
};
|
|
205
|
+
preloading = false;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function convertOptions(opts: ComboBoxItem[]) {
|
|
211
|
+
const allOptions = opts.map((option) => ({
|
|
212
|
+
...option,
|
|
213
|
+
selected: option.value === value
|
|
214
|
+
}));
|
|
215
|
+
allGroups = [{ label: '', items: [...allOptions] }];
|
|
216
|
+
filteredGroups = [{ label: '', items: [...allOptions] }];
|
|
88
217
|
}
|
|
89
218
|
|
|
90
|
-
|
|
219
|
+
function convertGroupOptions(groups: ComboBoxGroup[]) {
|
|
220
|
+
allGroups = groups.map((group) => ({
|
|
221
|
+
...group,
|
|
222
|
+
items: group.items.map((option) => ({
|
|
223
|
+
...option,
|
|
224
|
+
selected: option.value === value,
|
|
225
|
+
groupName: group.label
|
|
226
|
+
}))
|
|
227
|
+
}));
|
|
228
|
+
filteredGroups = [...allGroups];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let selectionJustMade = false;
|
|
91
232
|
|
|
92
233
|
function onSelection(selectedValue: ComboBoxItem) {
|
|
234
|
+
selectionJustMade = true;
|
|
93
235
|
open = false;
|
|
94
236
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
if (!preventChangeOnSelection) value = newValue;
|
|
105
|
-
onSelect?.(newItem);
|
|
106
|
-
onCreate?.(newValue);
|
|
237
|
+
// Handle "Create" items from allowCreate
|
|
238
|
+
if (allowCreate && selectedValue.label.startsWith('Create "')) {
|
|
239
|
+
const created = selectedValue.value;
|
|
240
|
+
value = created;
|
|
241
|
+
internalValue = value;
|
|
242
|
+
rawValue = { value: created, label: created };
|
|
243
|
+
onCreate?.(created);
|
|
244
|
+
onchangeRaw?.(rawValue);
|
|
107
245
|
return;
|
|
108
246
|
}
|
|
109
247
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
248
|
+
allGroups.flatMap((x) => x.items).forEach((option) => {
|
|
249
|
+
option.selected = option.value === selectedValue.value;
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (!preventChangeOnSelection) {
|
|
253
|
+
value = selectedValue.value;
|
|
254
|
+
internalValue = value;
|
|
114
255
|
}
|
|
115
256
|
|
|
116
|
-
|
|
257
|
+
rawValue = selectedValue;
|
|
117
258
|
|
|
118
259
|
onSelect?.(selectedValue);
|
|
260
|
+
onchangeRaw?.(selectedValue);
|
|
119
261
|
}
|
|
120
262
|
|
|
121
|
-
function
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
263
|
+
function onFilterChange(filterValue: string) {
|
|
264
|
+
if (filterValue.length === 0) {
|
|
265
|
+
filteredGroups = allGroups;
|
|
266
|
+
open = true;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
128
269
|
|
|
129
|
-
|
|
130
|
-
if (searchFunction && value) {
|
|
270
|
+
if (searchFunction && filterValue) {
|
|
131
271
|
clearTimeout(timeout);
|
|
132
272
|
searching = true;
|
|
133
|
-
timeout = setTimeout(() => {
|
|
134
|
-
searchFunction(
|
|
135
|
-
(response
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
},
|
|
140
|
-
);
|
|
273
|
+
timeout = window.setTimeout(() => {
|
|
274
|
+
searchFunction(filterValue).then((response: NotifyModel<ComboBoxItem[]>) => {
|
|
275
|
+
convertOptions(response.object!);
|
|
276
|
+
searching = false;
|
|
277
|
+
open = true;
|
|
278
|
+
});
|
|
141
279
|
}, 300);
|
|
142
|
-
} else if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
280
|
+
} else if (allGroups.length) {
|
|
281
|
+
filteredGroups = allGroups
|
|
282
|
+
.map((group) => ({
|
|
283
|
+
...group,
|
|
284
|
+
items: group.items.filter((x) =>
|
|
285
|
+
x.label.toLowerCase().includes(filterValue.toLowerCase())
|
|
286
|
+
)
|
|
287
|
+
}))
|
|
288
|
+
.filter((group) => group.items.length > 0);
|
|
147
289
|
open = true;
|
|
148
290
|
}
|
|
149
291
|
|
|
150
|
-
|
|
292
|
+
if (allowCreate && filterValue) {
|
|
293
|
+
const hasExact = filteredGroups
|
|
294
|
+
.flatMap((g) => g.items)
|
|
295
|
+
.some((x) => x.label.toLowerCase() === filterValue.toLowerCase());
|
|
296
|
+
if (!hasExact) {
|
|
297
|
+
const createItem: ComboBoxItem = { value: filterValue, label: `Create "${filterValue}"` };
|
|
298
|
+
filteredGroups = [
|
|
299
|
+
...filteredGroups,
|
|
300
|
+
{ label: '', items: [createItem] }
|
|
301
|
+
];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
onchange?.(filterValue);
|
|
151
306
|
}
|
|
152
307
|
|
|
153
308
|
let textboxElement: HTMLElement | undefined = $state(undefined);
|
|
154
|
-
|
|
155
|
-
// returns true if an item was selected, false otherwise
|
|
156
|
-
let comboBoxKeyDown: ((e: KeyboardEvent) => boolean) | undefined =
|
|
157
|
-
$state(undefined);
|
|
309
|
+
let comboBoxEl: ReturnType<typeof ComboBoxMulti> | undefined = $state(undefined);
|
|
158
310
|
|
|
159
311
|
function onKeyDown(e: KeyboardEvent) {
|
|
160
312
|
if (!open) {
|
|
161
|
-
if (e.key ===
|
|
162
|
-
// If combobox is not open return user entered string
|
|
163
|
-
enterPressed?.(value);
|
|
164
|
-
|
|
313
|
+
if (e.key === 'Enter') enterPressed?.(value);
|
|
165
314
|
return;
|
|
166
315
|
}
|
|
167
316
|
|
|
168
|
-
if (e.key ===
|
|
317
|
+
if (e.key === 'Escape') {
|
|
169
318
|
e.preventDefault();
|
|
170
319
|
open = false;
|
|
171
|
-
} else
|
|
172
|
-
|
|
320
|
+
} else {
|
|
321
|
+
selectionJustMade = false;
|
|
322
|
+
comboBoxEl?.handleKeyDown?.(e);
|
|
173
323
|
|
|
174
|
-
if (!
|
|
324
|
+
if (!selectionJustMade && e.key === 'Enter') enterPressed?.(value);
|
|
175
325
|
}
|
|
176
326
|
}
|
|
177
327
|
|
|
178
|
-
function onFocus(e:
|
|
328
|
+
function onFocus(_value: string, e: FocusEvent) {
|
|
179
329
|
open = true;
|
|
180
330
|
const target = e?.target as HTMLInputElement;
|
|
181
331
|
target?.select();
|
|
@@ -188,36 +338,68 @@
|
|
|
188
338
|
}
|
|
189
339
|
</script>
|
|
190
340
|
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
341
|
+
<div class="autocomplete-container">
|
|
342
|
+
<Textbox
|
|
343
|
+
{name}
|
|
344
|
+
type="search"
|
|
345
|
+
{label}
|
|
346
|
+
class={classes}
|
|
347
|
+
{containerClass}
|
|
348
|
+
required={required || showRequired}
|
|
349
|
+
{showError}
|
|
350
|
+
{errorText}
|
|
351
|
+
{tooltipLocation}
|
|
352
|
+
{tooltipContent}
|
|
353
|
+
{tooltipText}
|
|
354
|
+
bind:textboxElement
|
|
355
|
+
placeholder={preloading ? 'Loading...' : placeholder}
|
|
356
|
+
loading={searching || preloading}
|
|
357
|
+
disabled={preloading || disabled}
|
|
358
|
+
autocomplete="off"
|
|
359
|
+
{groupClass}
|
|
360
|
+
{leftIconSvg}
|
|
361
|
+
bind:value
|
|
362
|
+
oninput={() => {
|
|
363
|
+
onFilterChange(value);
|
|
364
|
+
}}
|
|
365
|
+
onfocus={onFocus}
|
|
366
|
+
onkeydown={onKeyDown}
|
|
367
|
+
/>
|
|
368
|
+
<div class="autocomplete-panel {open ? '' : 'hidden'}">
|
|
369
|
+
<div class="autocomplete-panel-inner" use:clickOutside={closePopover}>
|
|
370
|
+
<ComboBoxMulti
|
|
371
|
+
bind:this={comboBoxEl}
|
|
372
|
+
filterString={value}
|
|
373
|
+
values={rawValue ? [rawValue] : []}
|
|
374
|
+
onSelection={onSelection}
|
|
375
|
+
groupedOptions={filteredGroups}
|
|
376
|
+
{open}
|
|
377
|
+
loading={searching}
|
|
378
|
+
showNoResultsMessage={showNoResultsMessage || !hideNoResults}
|
|
379
|
+
{loadingText}
|
|
380
|
+
/>
|
|
381
|
+
</div>
|
|
219
382
|
</div>
|
|
220
|
-
</
|
|
383
|
+
</div>
|
|
221
384
|
|
|
222
385
|
<style>
|
|
386
|
+
.autocomplete-container {
|
|
387
|
+
position: relative;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.autocomplete-panel {
|
|
391
|
+
position: absolute;
|
|
392
|
+
width: 100%;
|
|
393
|
+
left: 0;
|
|
394
|
+
z-index: 70;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.autocomplete-panel-inner {
|
|
398
|
+
position: relative;
|
|
399
|
+
width: 100%;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.hidden {
|
|
403
|
+
display: none;
|
|
404
|
+
}
|
|
223
405
|
</style>
|
|
@@ -1,25 +1,75 @@
|
|
|
1
|
-
import type { ComboBoxItem, FetchFunctionType, SearchFunctionType } from
|
|
2
|
-
|
|
1
|
+
import type { ComboBoxGroup, ComboBoxItem, FetchFunctionType, RetrieveLabelFunctionType, SearchFunctionType } from '../models/ComboBoxItem.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
export interface AutocompleteProps {
|
|
4
|
+
/** HTML name attribute for the input */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Label text displayed above the input */
|
|
3
7
|
label?: string;
|
|
8
|
+
/** Current input value (bindable) */
|
|
4
9
|
value?: string;
|
|
10
|
+
/** Full selected item object (bindable) */
|
|
11
|
+
rawValue?: ComboBoxItem;
|
|
12
|
+
/** Placeholder text when input is empty */
|
|
5
13
|
placeholder?: string;
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
/** Mark field as required (shows asterisk) */
|
|
15
|
+
required?: boolean;
|
|
16
|
+
/** CSS classes for the input element */
|
|
17
|
+
class?: string;
|
|
18
|
+
/** CSS classes for the container element */
|
|
19
|
+
containerClass?: string;
|
|
20
|
+
/** Disable the input */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Show error state styling */
|
|
23
|
+
showError?: boolean;
|
|
24
|
+
/** Error message to display */
|
|
25
|
+
errorText?: string;
|
|
26
|
+
/** Auto-focus the input on mount */
|
|
8
27
|
autofocus?: boolean;
|
|
9
|
-
|
|
28
|
+
/** Auto-focus on every dialog open */
|
|
29
|
+
autofocusDialog?: boolean;
|
|
30
|
+
/** Hide "no results" message when search finds nothing */
|
|
31
|
+
hideNoResults?: boolean;
|
|
32
|
+
/** Prevent updating value when an option is selected */
|
|
10
33
|
preventChangeOnSelection?: boolean;
|
|
11
|
-
|
|
34
|
+
/** Text shown while loading options */
|
|
12
35
|
loadingText?: string;
|
|
36
|
+
/** CSS classes for the FormGroup wrapper */
|
|
13
37
|
groupClass?: string;
|
|
38
|
+
/** SVG icon displayed on the left side */
|
|
14
39
|
leftIconSvg?: string;
|
|
40
|
+
/** Static list of autocomplete options */
|
|
15
41
|
options?: ComboBoxItem[];
|
|
16
|
-
|
|
17
|
-
|
|
42
|
+
/** Options organized into groups */
|
|
43
|
+
groupedOptions?: ComboBoxGroup[];
|
|
44
|
+
/** Position of the tooltip */
|
|
45
|
+
tooltipLocation?: 'top' | 'bottom' | 'left' | 'right';
|
|
46
|
+
/** Async function to fetch all options */
|
|
47
|
+
fetchFunction?: FetchFunctionType | null;
|
|
48
|
+
/** Async function to search/filter options based on input */
|
|
49
|
+
searchFunction?: SearchFunctionType | null;
|
|
50
|
+
/** Async function to retrieve label for a value */
|
|
51
|
+
retrieveLabelFunction?: RetrieveLabelFunctionType | null;
|
|
52
|
+
/** Rich tooltip content using a Svelte snippet */
|
|
53
|
+
tooltipContent?: Snippet;
|
|
54
|
+
/** Simple tooltip text */
|
|
55
|
+
tooltipText?: string;
|
|
56
|
+
/** Callback when input value changes */
|
|
18
57
|
onchange?: (e: string) => void;
|
|
58
|
+
/** Callback when Enter key is pressed */
|
|
19
59
|
enterPressed?: (e: string) => void;
|
|
60
|
+
/** Callback when an option is selected */
|
|
20
61
|
onSelect?: (e: ComboBoxItem) => void;
|
|
62
|
+
/** Callback when selection changes (returns full item object) */
|
|
63
|
+
onchangeRaw?: (e: ComboBoxItem | undefined) => void;
|
|
64
|
+
/** Allow creating new options not in the list */
|
|
65
|
+
allowCreate?: boolean;
|
|
66
|
+
/** Show a "no results" message when no options match */
|
|
67
|
+
showNoResultsMessage?: boolean;
|
|
68
|
+
/** Show required indicator (alias for required) */
|
|
69
|
+
showRequired?: boolean;
|
|
70
|
+
/** Callback when a new option is created (requires allowCreate) */
|
|
21
71
|
onCreate?: (value: string) => void;
|
|
22
72
|
}
|
|
23
|
-
declare const Autocomplete: import("svelte").Component<
|
|
73
|
+
declare const Autocomplete: import("svelte").Component<AutocompleteProps, {}, "value" | "rawValue">;
|
|
24
74
|
type Autocomplete = ReturnType<typeof Autocomplete>;
|
|
25
75
|
export default Autocomplete;
|