@signal24/vue-foundation 4.25.2 → 4.25.4
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/package.json
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
<script lang="ts" setup generic="T, V = T">
|
|
53
53
|
import { debounce, groupBy, isEqual, uniq } from 'lodash';
|
|
54
|
-
import { computed, onMounted,
|
|
54
|
+
import { computed, onMounted, ref, watch } from 'vue';
|
|
55
55
|
|
|
56
56
|
import { escapeHtml } from '../helpers/string';
|
|
57
57
|
import type { VfSmartSelectOptionDescriptor } from './vf-smart-select.types';
|
|
@@ -63,6 +63,7 @@ const VALID_KEYS = `\`1234567890-=[]\\;',./~!@#$%^&*()_+{}|:"<>?qwertyuiopasdfgh
|
|
|
63
63
|
|
|
64
64
|
const props = defineProps<{
|
|
65
65
|
modelValue: V | null;
|
|
66
|
+
loadingText?: string;
|
|
66
67
|
loadOptions?: (searchText: string | null) => Promise<T[]>;
|
|
67
68
|
options?: T[];
|
|
68
69
|
prependOptions?: T[];
|
|
@@ -82,6 +83,7 @@ const props = defineProps<{
|
|
|
82
83
|
formatter?: (option: T) => string;
|
|
83
84
|
subtitleFormatter?: (option: T) => string;
|
|
84
85
|
classForOption?: (option: T) => string;
|
|
86
|
+
selectionFormatter?: (option: T) => string;
|
|
85
87
|
nullTitle?: string;
|
|
86
88
|
noResultsText?: string;
|
|
87
89
|
disabled?: boolean;
|
|
@@ -108,7 +110,7 @@ const optionsContainer = ref<HTMLDivElement>();
|
|
|
108
110
|
|
|
109
111
|
const isLoading = ref(false);
|
|
110
112
|
const isLoaded = ref(false);
|
|
111
|
-
const loadedOptions = ref<T[]>(
|
|
113
|
+
const loadedOptions = ref<T[]>();
|
|
112
114
|
const isSearching = ref(false);
|
|
113
115
|
const searchText = ref('');
|
|
114
116
|
const selectedOption = ref<T | null>(null);
|
|
@@ -120,7 +122,7 @@ const shouldShowCreateTextOnNewItem = computed(() => props.showCreateTextOnNewIt
|
|
|
120
122
|
|
|
121
123
|
const effectivePrependOptions = computed(() => props.prependOptions ?? []);
|
|
122
124
|
const effectiveAppendOptions = computed(() => props.appendOptions ?? []);
|
|
123
|
-
const effectiveDisabled = computed(() => !!props.disabled);
|
|
125
|
+
const effectiveDisabled = computed(() => !!props.disabled || (!props.options && !loadedOptions.value));
|
|
124
126
|
const effectivePlaceholder = computed(() => {
|
|
125
127
|
if (!isLoaded.value && props.preload) return 'Loading...';
|
|
126
128
|
if (props.nullTitle) return props.nullTitle;
|
|
@@ -149,8 +151,12 @@ const effectiveFormatter = computed(() => {
|
|
|
149
151
|
if (props.labelField) return (option: T) => String(option[props.labelField!]);
|
|
150
152
|
return (option: T) => String(option);
|
|
151
153
|
});
|
|
154
|
+
const effectiveSelectionFormatter = computed(() => {
|
|
155
|
+
if (props.selectionFormatter) return props.selectionFormatter;
|
|
156
|
+
return effectiveFormatter.value;
|
|
157
|
+
});
|
|
152
158
|
|
|
153
|
-
const allOptions = computed(() => [...effectivePrependOptions.value, ...loadedOptions.value, ...effectiveAppendOptions.value]);
|
|
159
|
+
const allOptions = computed(() => [...effectivePrependOptions.value, ...(loadedOptions.value ?? []), ...effectiveAppendOptions.value]);
|
|
154
160
|
const isGrouped = computed(() => !!(props.groupField || props.groupFormatter));
|
|
155
161
|
|
|
156
162
|
const optionsDescriptors = computed(() => {
|
|
@@ -249,7 +255,7 @@ watch(() => props.modelValue, handleValueChanged);
|
|
|
249
255
|
watch(
|
|
250
256
|
() => props.options,
|
|
251
257
|
() => {
|
|
252
|
-
loadedOptions.value = props.options
|
|
258
|
+
loadedOptions.value = props.options;
|
|
253
259
|
isLoaded.value = true;
|
|
254
260
|
}
|
|
255
261
|
);
|
|
@@ -274,7 +280,7 @@ watch(shouldDisplayOptions, () => {
|
|
|
274
280
|
setTimeout(handleOptionsDisplayed, 0);
|
|
275
281
|
} else {
|
|
276
282
|
isSearching.value = false;
|
|
277
|
-
searchText.value = selectedOptionTitle.value
|
|
283
|
+
searchText.value = selectedOptionTitle.value ?? '';
|
|
278
284
|
|
|
279
285
|
if (optionsContainer.value) {
|
|
280
286
|
optionsContainer.value.style.visibility = 'hidden';
|
|
@@ -283,11 +289,14 @@ watch(shouldDisplayOptions, () => {
|
|
|
283
289
|
});
|
|
284
290
|
|
|
285
291
|
watch(effectiveOptions, () => {
|
|
286
|
-
if (props.modelValue &&
|
|
292
|
+
if (props.modelValue !== null && selectedOption.value === null) {
|
|
287
293
|
handleValueChanged();
|
|
288
294
|
}
|
|
289
295
|
|
|
290
|
-
if (
|
|
296
|
+
if (
|
|
297
|
+
(highlightedOptionKey.value !== null || isSearching.value) &&
|
|
298
|
+
!effectiveOptions.value.find(option => option.key == highlightedOptionKey.value)
|
|
299
|
+
) {
|
|
291
300
|
highlightedOptionKey.value = effectiveOptions.value[0]?.key ?? NullSymbol;
|
|
292
301
|
}
|
|
293
302
|
});
|
|
@@ -298,17 +307,23 @@ onMounted(async () => {
|
|
|
298
307
|
if (props.options) {
|
|
299
308
|
loadedOptions.value = [...props.options];
|
|
300
309
|
isLoaded.value = true;
|
|
301
|
-
} else if (props.preload) {
|
|
310
|
+
} else if (props.loadOptions && props.preload) {
|
|
302
311
|
await loadRemoteOptions();
|
|
303
312
|
}
|
|
304
313
|
|
|
305
|
-
|
|
314
|
+
if (!props.options && (props.valueField || props.valueExtractor)) {
|
|
315
|
+
searchText.value = props.loadingText ?? '...';
|
|
316
|
+
} else {
|
|
317
|
+
handleValueChanged();
|
|
318
|
+
}
|
|
306
319
|
|
|
307
320
|
watch(selectedOption, () => {
|
|
308
321
|
if (selectedOption.value !== props.modelValue) {
|
|
309
322
|
emit(
|
|
310
323
|
'update:modelValue',
|
|
311
|
-
selectedOption.value
|
|
324
|
+
selectedOption.value !== null && effectiveValueExtractor.value !== null
|
|
325
|
+
? effectiveValueExtractor.value(selectedOption.value)
|
|
326
|
+
: selectedOption.value
|
|
312
327
|
);
|
|
313
328
|
}
|
|
314
329
|
});
|
|
@@ -324,7 +339,7 @@ async function loadRemoteOptions() {
|
|
|
324
339
|
}
|
|
325
340
|
|
|
326
341
|
async function reloadOptions() {
|
|
327
|
-
const effectiveSearchText = props.remoteSearch && isSearching.value && searchText.value ? searchText.value : null;
|
|
342
|
+
const effectiveSearchText = props.remoteSearch && isSearching.value && searchText.value.length ? searchText.value : null;
|
|
328
343
|
isLoading.value = true;
|
|
329
344
|
loadedOptions.value = (await props.loadOptions?.(effectiveSearchText)) ?? [];
|
|
330
345
|
isLoading.value = false;
|
|
@@ -517,7 +532,7 @@ function incrementHighlightedOption(increment: number) {
|
|
|
517
532
|
function selectOption(option: VfSmartSelectOptionDescriptor<T>) {
|
|
518
533
|
isSearching.value = false;
|
|
519
534
|
|
|
520
|
-
if (option.key
|
|
535
|
+
if (option.key === NullSymbol) {
|
|
521
536
|
searchText.value = '';
|
|
522
537
|
selectedOption.value = null;
|
|
523
538
|
selectedOptionTitle.value = null;
|
|
@@ -531,8 +546,8 @@ function selectOption(option: VfSmartSelectOptionDescriptor<T>) {
|
|
|
531
546
|
const selectedDecoratedOption = optionsDescriptors.value.find(decoratedOption => decoratedOption.key == option.key);
|
|
532
547
|
const realOption = selectedDecoratedOption!.ref;
|
|
533
548
|
selectedOption.value = realOption!;
|
|
534
|
-
selectedOptionTitle.value =
|
|
535
|
-
searchText.value = selectedOptionTitle.value
|
|
549
|
+
selectedOptionTitle.value = effectiveSelectionFormatter.value(realOption!);
|
|
550
|
+
searchText.value = selectedOptionTitle.value ?? '';
|
|
536
551
|
}
|
|
537
552
|
|
|
538
553
|
searchField.value?.blur();
|
|
@@ -540,12 +555,12 @@ function selectOption(option: VfSmartSelectOptionDescriptor<T>) {
|
|
|
540
555
|
}
|
|
541
556
|
|
|
542
557
|
function handleValueChanged() {
|
|
543
|
-
if (props.modelValue) {
|
|
558
|
+
if (props.modelValue !== null) {
|
|
544
559
|
selectedOption.value = effectiveValueExtractor.value
|
|
545
560
|
? allOptions.value.find(o => props.modelValue === effectiveValueExtractor.value!(o))
|
|
546
561
|
: props.modelValue;
|
|
547
|
-
selectedOptionTitle.value = selectedOption.value ?
|
|
548
|
-
searchText.value = selectedOptionTitle.value
|
|
562
|
+
selectedOptionTitle.value = selectedOption.value !== null ? effectiveSelectionFormatter.value(selectedOption.value) : null;
|
|
563
|
+
searchText.value = selectedOptionTitle.value ?? '';
|
|
549
564
|
} else {
|
|
550
565
|
selectedOption.value = null;
|
|
551
566
|
selectedOptionTitle.value = null;
|
|
@@ -554,7 +569,7 @@ function handleValueChanged() {
|
|
|
554
569
|
}
|
|
555
570
|
|
|
556
571
|
function addRemoteOption(option: T) {
|
|
557
|
-
loadedOptions.value
|
|
572
|
+
loadedOptions.value!.unshift(option);
|
|
558
573
|
}
|
|
559
574
|
|
|
560
575
|
function focusNextInput() {
|