@signal24/vue-foundation 4.24.2 → 4.25.0
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/demo/components/demo-vf-smart-select.vue +29 -13
- package/dist/src/components/vf-smart-select.types.d.ts +1 -0
- package/dist/src/components/vf-smart-select.vue.d.ts +6 -0
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/dist/vue-foundation.css +1 -1
- package/dist/vue-foundation.es.js +757 -727
- package/package.json +1 -1
- package/src/components/vf-smart-select.types.ts +1 -0
- package/src/components/vf-smart-select.vue +60 -19
package/package.json
CHANGED
|
@@ -15,26 +15,34 @@
|
|
|
15
15
|
@focus="handleInputFocused"
|
|
16
16
|
@blur="handleInputBlurred"
|
|
17
17
|
/>
|
|
18
|
-
<div v-if="shouldDisplayOptions" ref="optionsContainer" class="vf-smart-select-options">
|
|
18
|
+
<div v-if="shouldDisplayOptions" ref="optionsContainer" class="vf-smart-select-options" :class="{ grouped: isGrouped }">
|
|
19
19
|
<div v-if="!isLoaded" class="no-results">Loading...</div>
|
|
20
20
|
<template v-else>
|
|
21
|
-
<div
|
|
22
|
-
v-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
21
|
+
<div v-for="group in groupedOptions" :key="group.groupTitle" class="group">
|
|
22
|
+
<div v-if="group.groupTitle" class="group-title">
|
|
23
|
+
<slot name="group" :group="group.groupTitle">
|
|
24
|
+
{{ group.groupTitle }}
|
|
25
|
+
</slot>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div
|
|
29
|
+
v-for="option in group.options"
|
|
30
|
+
:key="option.key"
|
|
31
|
+
class="option"
|
|
32
|
+
:class="[highlightedOptionKey === option.key && 'highlighted', option.ref && classForOption?.(option.ref)]"
|
|
33
|
+
@mousemove="handleOptionHover(option)"
|
|
34
|
+
@mousedown="selectOption(option)"
|
|
35
|
+
>
|
|
36
|
+
<slot name="option" :option="option">
|
|
37
|
+
<div class="title" v-html="option.title" />
|
|
38
|
+
<div v-if="option.subtitle" class="subtitle" v-html="option.subtitle" />
|
|
39
|
+
</slot>
|
|
40
|
+
</div>
|
|
41
|
+
<div v-if="!effectiveOptions.length && searchText" class="no-results">
|
|
42
|
+
<slot name="no-results">
|
|
43
|
+
{{ effectiveNoResultsText }}
|
|
44
|
+
</slot>
|
|
45
|
+
</div>
|
|
38
46
|
</div>
|
|
39
47
|
</template>
|
|
40
48
|
</div>
|
|
@@ -42,7 +50,7 @@
|
|
|
42
50
|
</template>
|
|
43
51
|
|
|
44
52
|
<script lang="ts" setup generic="T, V = T">
|
|
45
|
-
import { debounce, isEqual } from 'lodash';
|
|
53
|
+
import { debounce, groupBy, isEqual, uniq } from 'lodash';
|
|
46
54
|
import { computed, onMounted, type Ref, ref, watch } from 'vue';
|
|
47
55
|
|
|
48
56
|
import { escapeHtml } from '../helpers/string';
|
|
@@ -69,6 +77,8 @@ const props = defineProps<{
|
|
|
69
77
|
valueField?: keyof T;
|
|
70
78
|
valueExtractor?: (option: T) => V;
|
|
71
79
|
labelField?: keyof T;
|
|
80
|
+
groupField?: keyof T;
|
|
81
|
+
groupFormatter?: (option: T) => string;
|
|
72
82
|
formatter?: (option: T) => string;
|
|
73
83
|
subtitleFormatter?: (option: T) => string;
|
|
74
84
|
classForOption?: (option: T) => string;
|
|
@@ -129,6 +139,11 @@ const effectiveKeyExtractor = computed(() => {
|
|
|
129
139
|
if (effectiveValueExtractor.value) return (option: T) => String(effectiveValueExtractor.value!(option));
|
|
130
140
|
return null;
|
|
131
141
|
});
|
|
142
|
+
const effectiveGroupFormatter = computed(() => {
|
|
143
|
+
if (props.groupFormatter) return props.groupFormatter;
|
|
144
|
+
if (props.groupField) return (option: T) => String(option[props.groupField!]);
|
|
145
|
+
return null;
|
|
146
|
+
});
|
|
132
147
|
const effectiveFormatter = computed(() => {
|
|
133
148
|
if (props.formatter) return props.formatter;
|
|
134
149
|
if (props.labelField) return (option: T) => String(option[props.labelField!]);
|
|
@@ -136,9 +151,11 @@ const effectiveFormatter = computed(() => {
|
|
|
136
151
|
});
|
|
137
152
|
|
|
138
153
|
const allOptions = computed(() => [...effectivePrependOptions.value, ...loadedOptions.value, ...effectiveAppendOptions.value]);
|
|
154
|
+
const isGrouped = computed(() => !!(props.groupField || props.groupFormatter));
|
|
139
155
|
|
|
140
156
|
const optionsDescriptors = computed(() => {
|
|
141
157
|
return allOptions.value.map((option, index) => {
|
|
158
|
+
const group = effectiveGroupFormatter.value?.(option);
|
|
142
159
|
const title = effectiveFormatter.value(option);
|
|
143
160
|
const subtitle = props.subtitleFormatter?.(option);
|
|
144
161
|
const strippedTitle = title ? title.trim().toLowerCase() : '';
|
|
@@ -160,6 +177,7 @@ const optionsDescriptors = computed(() => {
|
|
|
160
177
|
|
|
161
178
|
return {
|
|
162
179
|
key: effectiveKeyExtractor.value?.(option) ?? String(index),
|
|
180
|
+
group,
|
|
163
181
|
title,
|
|
164
182
|
subtitle,
|
|
165
183
|
searchContent: searchContent.join(''),
|
|
@@ -208,6 +226,24 @@ const effectiveOptions = computed(() => {
|
|
|
208
226
|
return options;
|
|
209
227
|
});
|
|
210
228
|
|
|
229
|
+
const groupedOptions = computed(() => {
|
|
230
|
+
if (!effectiveOptions.value[0]?.group) {
|
|
231
|
+
return [
|
|
232
|
+
{
|
|
233
|
+
groupTitle: '',
|
|
234
|
+
options: effectiveOptions.value
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const groupTitles = uniq(effectiveOptions.value.map(option => option.group ?? ''));
|
|
240
|
+
const groupedOptions = groupBy(effectiveOptions.value, option => option.group);
|
|
241
|
+
return groupTitles.map(groupTitle => ({
|
|
242
|
+
groupTitle,
|
|
243
|
+
options: groupedOptions[groupTitle!]
|
|
244
|
+
}));
|
|
245
|
+
});
|
|
246
|
+
|
|
211
247
|
// watch props
|
|
212
248
|
watch(() => props.modelValue, handleValueChanged);
|
|
213
249
|
watch(
|
|
@@ -593,6 +629,11 @@ function focusNextInput() {
|
|
|
593
629
|
overflow: auto;
|
|
594
630
|
z-index: 101;
|
|
595
631
|
|
|
632
|
+
.group-title {
|
|
633
|
+
padding: 5px 8px;
|
|
634
|
+
color: #999;
|
|
635
|
+
}
|
|
636
|
+
|
|
596
637
|
.option,
|
|
597
638
|
.no-results {
|
|
598
639
|
padding: 5px 8px;
|