@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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@signal24/vue-foundation",
3
3
  "type": "module",
4
- "version": "4.25.2",
4
+ "version": "4.25.4",
5
5
  "description": "Common components, directives, and helpers for Vue 3 apps",
6
6
  "module": "./dist/vue-foundation.es.js",
7
7
  "exports": {
@@ -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, type Ref, ref, watch } from 'vue';
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[]>([]) as 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 && !selectedOption.value) {
292
+ if (props.modelValue !== null && selectedOption.value === null) {
287
293
  handleValueChanged();
288
294
  }
289
295
 
290
- if ((highlightedOptionKey.value || isSearching.value) && !effectiveOptions.value.find(option => option.key == highlightedOptionKey.value)) {
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
- handleValueChanged();
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 && effectiveValueExtractor.value ? effectiveValueExtractor.value(selectedOption.value) : 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 == NullSymbol) {
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 = effectiveFormatter.value(realOption!);
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 ? effectiveFormatter.value(selectedOption.value) : null;
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.unshift(option);
572
+ loadedOptions.value!.unshift(option);
558
573
  }
559
574
 
560
575
  function focusNextInput() {