@madgex/design-system-ce 5.6.1 → 5.6.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/components/combobox/Combobox.ce.vue +217 -253
- package/components/combobox/ComboboxClear.vue +5 -5
- package/components/combobox/ListBox.vue +10 -15
- package/components/combobox/ListBoxOption.vue +26 -37
- package/dist/custom-elements/mds-combobox.js +1 -1
- package/dist/custom-elements/mds-text-editor.js +20 -20
- package/dist/index.js +1 -1
- package/dist/manifest.json +17 -13
- package/dist/runtime-dom.esm-bundler.js +18 -0
- package/package.json +14 -12
- package/vite.config.js +1 -1
- package/components/combobox/Combobox.ce.spec.js +0 -76
- package/dist/plugin-vue_export-helper.js +0 -18
- package/eslint.config.js +0 -6
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
<!-- eslint-disable vue/multi-word-component-names -->
|
|
1
2
|
<template>
|
|
2
3
|
<div
|
|
4
|
+
ref="$el"
|
|
3
5
|
class="mds-combobox"
|
|
4
6
|
:class="{ 'mds-combobox--active': !listBoxHidden }"
|
|
5
|
-
@keydown.down="
|
|
6
|
-
@keydown.up="
|
|
7
|
-
@keydown.home="
|
|
8
|
-
@keydown.end="
|
|
7
|
+
@keydown.down="onKeyDown"
|
|
8
|
+
@keydown.up="onKeyUp"
|
|
9
|
+
@keydown.home="onKeyHome"
|
|
10
|
+
@keydown.end="onKeyEnd"
|
|
9
11
|
@keydown.esc="makeInactive"
|
|
10
12
|
@keydown.enter="handleKeyDownEnter"
|
|
11
13
|
>
|
|
12
14
|
<input
|
|
13
15
|
:id="comboboxid"
|
|
14
|
-
ref="comboInput"
|
|
16
|
+
ref="$comboInput"
|
|
15
17
|
:value="inputValue"
|
|
16
18
|
class="mds-form-control"
|
|
17
19
|
autocomplete="off"
|
|
@@ -43,271 +45,233 @@
|
|
|
43
45
|
@mousedown="clickOption(option)"
|
|
44
46
|
/>
|
|
45
47
|
</ListBox>
|
|
46
|
-
<div aria-live="polite" role="status" class="mds-visually-hidden">
|
|
48
|
+
<div aria-live="polite" role="status" class="mds-visually-hidden">
|
|
49
|
+
{{ resultCountMessage }}
|
|
50
|
+
</div>
|
|
47
51
|
</div>
|
|
48
52
|
</template>
|
|
49
53
|
|
|
50
|
-
<script>
|
|
54
|
+
<script setup>
|
|
55
|
+
import { computed, onMounted, provide, ref, useTemplateRef } from 'vue';
|
|
51
56
|
import ComboboxClear from './ComboboxClear.vue';
|
|
52
57
|
import ListBox from './ListBox.vue';
|
|
53
58
|
import ListBoxOption from './ListBoxOption.vue';
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
const props = defineProps({
|
|
61
|
+
comboboxid: { type: String, required: true },
|
|
62
|
+
placeholder: { type: String, default: '' },
|
|
63
|
+
name: { type: [String, Boolean], default: false },
|
|
64
|
+
value: { type: String, default: '' },
|
|
65
|
+
options: { type: Array, default: () => [] },
|
|
66
|
+
filterOptions: { type: Boolean, default: true },
|
|
67
|
+
iconpath: { type: String, default: '/assets/icons.svg' },
|
|
68
|
+
dataAriaInvalid: { type: String, default: '' },
|
|
69
|
+
i18n: { type: String, default: '' },
|
|
70
|
+
describedbyId: { type: String, default: '' },
|
|
71
|
+
minSearchCharacters: { type: Number, default: 2 },
|
|
72
|
+
});
|
|
73
|
+
const emit = defineEmits(['search', 'select-option', 'clear-all']);
|
|
74
|
+
|
|
75
|
+
const $el = useTemplateRef('$el');
|
|
76
|
+
const $comboInput = useTemplateRef('$comboInput');
|
|
77
|
+
|
|
78
|
+
const expanded = ref(false);
|
|
79
|
+
const selected = ref(null);
|
|
80
|
+
const chosen = ref(null);
|
|
81
|
+
const searchValue = ref(props.value);
|
|
82
|
+
const resultCountMessage = ref(null);
|
|
83
|
+
|
|
84
|
+
const i18nText = computed(() => {
|
|
85
|
+
return props.i18n
|
|
86
|
+
? JSON.parse(props.i18n)
|
|
87
|
+
: {
|
|
88
|
+
loadingText: 'Loading',
|
|
89
|
+
resultsMessage: '{count} result available',
|
|
90
|
+
resultsMessage_plural: '{count} results available',
|
|
91
|
+
clearInput: 'clear input',
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
const inputValue = computed(() => {
|
|
95
|
+
if (chosenOption.value) {
|
|
96
|
+
return chosenOption.value.label;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return searchValue.value;
|
|
100
|
+
});
|
|
101
|
+
const visibleOptions = computed(() => {
|
|
102
|
+
if (props.filterOptions) {
|
|
103
|
+
return props.options.filter((opt) => opt.label.toLowerCase().includes(searchValue.value.toLowerCase()));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return props.options;
|
|
107
|
+
});
|
|
108
|
+
const listBoxId = computed(() => {
|
|
109
|
+
return `${props.comboboxid}-listbox`;
|
|
110
|
+
});
|
|
111
|
+
const optionId = computed(() => {
|
|
112
|
+
return `${props.comboboxid}-option`;
|
|
113
|
+
});
|
|
114
|
+
const isLoading = computed(() => {
|
|
115
|
+
return props.options.length === 0 && expanded.value;
|
|
116
|
+
});
|
|
117
|
+
const selectedOptionId = computed(() => {
|
|
118
|
+
// make sure comparison is treated as strings (in case option value Number etc)
|
|
119
|
+
const index = visibleOptions.value.findIndex((obj) => String(obj.value) === String(selectedOption.value?.value));
|
|
120
|
+
|
|
121
|
+
if (index > -1) {
|
|
122
|
+
return `${optionId.value}-${index}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return undefined;
|
|
126
|
+
});
|
|
127
|
+
const listBoxHidden = computed(() => {
|
|
128
|
+
return !expanded.value;
|
|
129
|
+
});
|
|
130
|
+
const lastOptionIndex = computed(() => {
|
|
131
|
+
return visibleOptions.value.length - 1;
|
|
132
|
+
});
|
|
133
|
+
const ariaExpanded = computed(() => {
|
|
134
|
+
// These must be strings to apply as an aria attribute of the same name
|
|
135
|
+
return expanded.value ? 'true' : 'false';
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const ariaInvalid = computed(() => {
|
|
139
|
+
return props.dataAriaInvalid ? 'true' : 'false';
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const selectedOption = computed({
|
|
143
|
+
get() {
|
|
144
|
+
return selected.value;
|
|
61
145
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
iconPath: this.iconpath,
|
|
65
|
-
loadingText: this.i18nText.loadingText,
|
|
66
|
-
clearInput: this.i18nText.clearInput,
|
|
67
|
-
};
|
|
146
|
+
set(newOption) {
|
|
147
|
+
selected.value = newOption;
|
|
68
148
|
},
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
},
|
|
74
|
-
placeholder: {
|
|
75
|
-
type: String,
|
|
76
|
-
default: '',
|
|
77
|
-
},
|
|
78
|
-
name: {
|
|
79
|
-
type: [String, Boolean],
|
|
80
|
-
default: false,
|
|
81
|
-
},
|
|
82
|
-
value: {
|
|
83
|
-
type: String,
|
|
84
|
-
default: '',
|
|
85
|
-
},
|
|
86
|
-
options: {
|
|
87
|
-
type: Array,
|
|
88
|
-
default: () => [],
|
|
89
|
-
},
|
|
90
|
-
filterOptions: {
|
|
91
|
-
type: Boolean,
|
|
92
|
-
default: true,
|
|
93
|
-
},
|
|
94
|
-
iconpath: {
|
|
95
|
-
type: String,
|
|
96
|
-
default: '/assets/icons.svg',
|
|
97
|
-
},
|
|
98
|
-
dataAriaInvalid: {
|
|
99
|
-
type: String,
|
|
100
|
-
default: '',
|
|
101
|
-
},
|
|
102
|
-
i18n: {
|
|
103
|
-
type: String,
|
|
104
|
-
default: '',
|
|
105
|
-
},
|
|
106
|
-
describedbyId: {
|
|
107
|
-
type: String,
|
|
108
|
-
default: ''
|
|
109
|
-
},
|
|
110
|
-
minSearchCharacters: {
|
|
111
|
-
type: Number,
|
|
112
|
-
default: 2
|
|
113
|
-
}
|
|
149
|
+
});
|
|
150
|
+
const chosenOption = computed({
|
|
151
|
+
get() {
|
|
152
|
+
return chosen.value;
|
|
114
153
|
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
selected: null,
|
|
120
|
-
chosen: null,
|
|
121
|
-
searchValue: this.$props.value,
|
|
122
|
-
resultCountMessage: null,
|
|
123
|
-
};
|
|
154
|
+
set(newOption) {
|
|
155
|
+
chosen.value = newOption;
|
|
156
|
+
selectedOption.value = newOption;
|
|
157
|
+
emit('select-option', chosen.value);
|
|
124
158
|
},
|
|
125
|
-
|
|
126
|
-
inputValue() {
|
|
127
|
-
if (this.chosenOption) {
|
|
128
|
-
return this.chosenOption.label;
|
|
129
|
-
}
|
|
159
|
+
});
|
|
130
160
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return this.selected;
|
|
136
|
-
},
|
|
137
|
-
set(newOption) {
|
|
138
|
-
this.selected = newOption;
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
chosenOption: {
|
|
142
|
-
get() {
|
|
143
|
-
return this.chosen;
|
|
144
|
-
},
|
|
145
|
-
set(newOption) {
|
|
146
|
-
this.chosen = newOption;
|
|
147
|
-
this.selectedOption = newOption;
|
|
148
|
-
this.$emit('select-option', this.chosen);
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
visibleOptions() {
|
|
152
|
-
if (this.filterOptions) {
|
|
153
|
-
return this.options.filter((opt) => opt.label.toLowerCase().includes(this.searchValue.toLowerCase()));
|
|
154
|
-
}
|
|
161
|
+
onMounted(() => {
|
|
162
|
+
// TODO: Get rid of code which couples to the nunjucks MdsCombobox template!
|
|
163
|
+
const fallbackInput = $el.value?.parentElement?.parentElement?.querySelector('.mds-form-element__fallback input');
|
|
164
|
+
const fallbackSelect = $el.value?.parentElement?.parentElement?.querySelector('.mds-form-element__fallback select');
|
|
155
165
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return `${this.comboboxid}-listbox`;
|
|
160
|
-
},
|
|
161
|
-
optionId() {
|
|
162
|
-
return `${this.comboboxid}-option`;
|
|
163
|
-
},
|
|
164
|
-
isLoading() {
|
|
165
|
-
return this.options.length === 0 && this.expanded;
|
|
166
|
-
},
|
|
167
|
-
selectedOptionId() {
|
|
168
|
-
const index = this.visibleOptions.findIndex((obj) => obj.value == this.selectedOption?.value);
|
|
166
|
+
if (fallbackInput) fallbackInput.remove();
|
|
167
|
+
if (fallbackSelect) fallbackSelect.removeAttribute('id');
|
|
168
|
+
});
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
function makeActive() {
|
|
171
|
+
expanded.value = true;
|
|
172
|
+
}
|
|
173
|
+
function makeInactive() {
|
|
174
|
+
expanded.value = false;
|
|
175
|
+
}
|
|
176
|
+
function handleInput(event) {
|
|
177
|
+
// Reset any chosen option if user is typing again
|
|
178
|
+
chosenOption.value = null;
|
|
179
|
+
searchValue.value = event.target ? event.target.value : '';
|
|
180
|
+
handleChange();
|
|
181
|
+
emit('search', searchValue.value);
|
|
182
|
+
if (visibleOptions.value.length > 0) {
|
|
183
|
+
updateCount();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function handleChange() {
|
|
187
|
+
if (searchValue.value.length === 0) clearField();
|
|
188
|
+
if (searchValue.value.length >= props.minSearchCharacters) {
|
|
189
|
+
makeActive();
|
|
190
|
+
updateCount();
|
|
191
|
+
} else {
|
|
192
|
+
makeInactive();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function handleFocus() {
|
|
196
|
+
handleChange();
|
|
197
|
+
if (visibleOptions.value.length > 1) {
|
|
198
|
+
updateCount();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function handleClear() {
|
|
202
|
+
clearField();
|
|
203
|
+
$comboInput.value?.focus();
|
|
204
|
+
}
|
|
205
|
+
function clearField() {
|
|
206
|
+
searchValue.value = '';
|
|
207
|
+
chosenOption.value = null;
|
|
208
|
+
emit('clear-all');
|
|
209
|
+
}
|
|
210
|
+
function clickOption(option = selectedOption.value) {
|
|
211
|
+
chosenOption.value = option;
|
|
212
|
+
makeInactive();
|
|
213
|
+
}
|
|
214
|
+
/* When expanded then enter key down selects an option, otherwise enter key will be native behaviour, eg submit form */
|
|
215
|
+
function handleKeyDownEnter(event) {
|
|
216
|
+
if (expanded.value) {
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
chooseOption();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function chooseOption() {
|
|
222
|
+
chosenOption.value = selectedOption.value;
|
|
223
|
+
makeInactive();
|
|
224
|
+
clearCount();
|
|
225
|
+
}
|
|
173
226
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// These must be strings to apply as an aria attribute of the same name
|
|
184
|
-
return this.expanded ? 'true' : 'false';
|
|
185
|
-
},
|
|
186
|
-
ariaInvalid() {
|
|
187
|
-
return this.dataAriaInvalid ? 'true' : 'false';
|
|
188
|
-
},
|
|
189
|
-
i18nText() {
|
|
190
|
-
return this.i18n
|
|
191
|
-
? JSON.parse(this.i18n)
|
|
192
|
-
: {
|
|
193
|
-
loadingText: 'Loading',
|
|
194
|
-
resultsMessage: '{count} result available',
|
|
195
|
-
resultsMessage_plural: '{count} results available',
|
|
196
|
-
clearInput: 'clear input',
|
|
197
|
-
};
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
mounted() {
|
|
201
|
-
// TODO: Get rid of this code which couples this to the nunjucks MdsCombobox template!
|
|
202
|
-
const fallbackInput = this.$el.parentElement?.parentElement?.querySelector('.mds-form-element__fallback input');
|
|
203
|
-
const fallbackSelect = this.$el.parentElement?.parentElement?.querySelector('.mds-form-element__fallback select');
|
|
227
|
+
function onInputBlur() {
|
|
228
|
+
makeInactive();
|
|
229
|
+
clearCount();
|
|
230
|
+
}
|
|
231
|
+
function onKeyDown() {
|
|
232
|
+
if (listBoxHidden.value) return;
|
|
233
|
+
if (selectedOption.value) {
|
|
234
|
+
const currentIndex = visibleOptions.value.findIndex((item) => item.value === selectedOption.value.value);
|
|
235
|
+
const nextIndex = currentIndex === lastOptionIndex.value ? currentIndex : currentIndex + 1;
|
|
204
236
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
handleInput(event) {
|
|
216
|
-
// Reset any chosen option if user is typing again
|
|
217
|
-
this.chosenOption = null;
|
|
218
|
-
this.searchValue = event.target ? event.target.value : '';
|
|
219
|
-
this.handleChange();
|
|
220
|
-
this.$emit('search', this.searchValue);
|
|
221
|
-
if (this.visibleOptions.length > 0) {
|
|
222
|
-
this.updateCount();
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
handleChange() {
|
|
226
|
-
if (this.searchValue.length === 0) this.clearField();
|
|
227
|
-
if (this.searchValue.length >= this.minSearchCharacters) {
|
|
228
|
-
this.makeActive();
|
|
229
|
-
this.updateCount();
|
|
230
|
-
} else {
|
|
231
|
-
this.makeInactive();
|
|
232
|
-
}
|
|
233
|
-
},
|
|
234
|
-
handleFocus() {
|
|
235
|
-
this.handleChange();
|
|
236
|
-
if (this.visibleOptions.length > 1) {
|
|
237
|
-
this.updateCount();
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
handleClear() {
|
|
241
|
-
this.clearField();
|
|
242
|
-
this.$refs.comboInput.focus();
|
|
243
|
-
},
|
|
244
|
-
clearField() {
|
|
245
|
-
this.searchValue = '';
|
|
246
|
-
this.chosenOption = null;
|
|
247
|
-
this.$emit('clear-all');
|
|
248
|
-
},
|
|
249
|
-
clickOption(option = this.selectedOption) {
|
|
250
|
-
this.chosenOption = option;
|
|
251
|
-
this.makeInactive();
|
|
252
|
-
},
|
|
253
|
-
/* When expanded then enter key down selects an option, otherwise enter key will be native behaviour, eg submit form */
|
|
254
|
-
handleKeyDownEnter(event) {
|
|
255
|
-
if (this.expanded) {
|
|
256
|
-
event.preventDefault();
|
|
257
|
-
this.chooseOption();
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
chooseOption() {
|
|
261
|
-
this.chosenOption = this.selectedOption;
|
|
262
|
-
this.makeInactive();
|
|
263
|
-
this.clearCount();
|
|
264
|
-
},
|
|
265
|
-
hiddenGuard(fn) {
|
|
266
|
-
if (this.listBoxHidden) return;
|
|
267
|
-
fn.call(this);
|
|
268
|
-
},
|
|
269
|
-
onInputBlur() {
|
|
270
|
-
this.makeInactive();
|
|
271
|
-
this.clearCount();
|
|
272
|
-
},
|
|
273
|
-
onKeyDown() {
|
|
274
|
-
if (this.selectedOption) {
|
|
275
|
-
const currentIndex = this.visibleOptions.findIndex((item) => item.value === this.selectedOption.value);
|
|
276
|
-
const nextIndex = currentIndex === this.lastOptionIndex ? currentIndex : currentIndex + 1;
|
|
237
|
+
selectedOption.value = visibleOptions.value[nextIndex];
|
|
238
|
+
} else {
|
|
239
|
+
[selectedOption.value] = visibleOptions.value;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function onKeyUp() {
|
|
243
|
+
if (listBoxHidden.value) return;
|
|
244
|
+
if (selectedOption.value) {
|
|
245
|
+
const currentIndex = visibleOptions.value.findIndex((item) => item.value === selectedOption.value.value);
|
|
246
|
+
const nextIndex = currentIndex === 0 ? currentIndex : currentIndex - 1;
|
|
277
247
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
248
|
+
selectedOption.value = visibleOptions.value[nextIndex];
|
|
249
|
+
} else {
|
|
250
|
+
selectedOption.value = visibleOptions.value[lastOptionIndex.value];
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function onKeyHome() {
|
|
254
|
+
if (listBoxHidden.value) return;
|
|
255
|
+
[selectedOption.value] = visibleOptions.value;
|
|
256
|
+
}
|
|
257
|
+
function onKeyEnd() {
|
|
258
|
+
if (listBoxHidden.value) return;
|
|
259
|
+
selectedOption.value = visibleOptions.value[lastOptionIndex.value];
|
|
260
|
+
}
|
|
261
|
+
function updateCount() {
|
|
262
|
+
clearCount();
|
|
263
|
+
setTimeout(() => {
|
|
264
|
+
const message =
|
|
265
|
+
visibleOptions.value.length === 1 ? i18nText.value.resultsMessage : i18nText.value.resultsMessage_plural;
|
|
287
266
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
[this.selectedOption] = this.visibleOptions;
|
|
295
|
-
},
|
|
296
|
-
onKeyEnd() {
|
|
297
|
-
this.selectedOption = this.visibleOptions[this.lastOptionIndex];
|
|
298
|
-
},
|
|
299
|
-
updateCount() {
|
|
300
|
-
this.clearCount();
|
|
301
|
-
setTimeout(() => {
|
|
302
|
-
const message =
|
|
303
|
-
this.visibleOptions.length === 1 ? this.i18nText.resultsMessage : this.i18nText.resultsMessage_plural;
|
|
267
|
+
resultCountMessage.value = message.replace('{count}', visibleOptions.value.length);
|
|
268
|
+
}, 1400);
|
|
269
|
+
}
|
|
270
|
+
function clearCount() {
|
|
271
|
+
resultCountMessage.value = null;
|
|
272
|
+
}
|
|
304
273
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
clearCount() {
|
|
309
|
-
this.resultCountMessage = null;
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
};
|
|
274
|
+
provide('iconPath', props.iconpath);
|
|
275
|
+
provide('loadingText', i18nText.value.loadingText);
|
|
276
|
+
provide('clearInput', i18nText.value.clearInput);
|
|
313
277
|
</script>
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
</button>
|
|
14
14
|
</template>
|
|
15
15
|
|
|
16
|
-
<script>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
<script setup>
|
|
17
|
+
import { inject } from 'vue';
|
|
18
|
+
|
|
19
|
+
const iconPath = inject('iconPath');
|
|
20
|
+
const clearInput = inject('clearInput');
|
|
21
21
|
</script>
|
|
@@ -12,19 +12,14 @@
|
|
|
12
12
|
</ul>
|
|
13
13
|
</template>
|
|
14
14
|
|
|
15
|
-
<script>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
default: true,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
inject: ['iconPath', 'loadingText'],
|
|
29
|
-
};
|
|
15
|
+
<script setup>
|
|
16
|
+
import { inject } from 'vue';
|
|
17
|
+
|
|
18
|
+
defineProps({
|
|
19
|
+
hidden: { type: Boolean, default: true },
|
|
20
|
+
isLoading: { type: Boolean, default: true },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const iconPath = inject('iconPath');
|
|
24
|
+
const loadingText = inject('loadingText');
|
|
30
25
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<li
|
|
3
|
-
ref="listItem"
|
|
3
|
+
ref="$listItem"
|
|
4
4
|
class="mds-combobox__option"
|
|
5
5
|
role="option"
|
|
6
6
|
:class="{ 'mds-combobox__option--focused': focused }"
|
|
@@ -10,41 +10,30 @@
|
|
|
10
10
|
/>
|
|
11
11
|
</template>
|
|
12
12
|
|
|
13
|
-
<script>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
watch: {
|
|
31
|
-
searchValue(newSearchValue) {
|
|
32
|
-
return newSearchValue;
|
|
33
|
-
},
|
|
34
|
-
focused(value) {
|
|
35
|
-
if (value) {
|
|
36
|
-
this.$refs.listItem.scrollIntoView({block: 'nearest', inline: 'nearest'});
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
methods: {
|
|
41
|
-
highlightOption() {
|
|
42
|
-
const optionLabelHtml = this.option.label.replace(
|
|
43
|
-
new RegExp(this.searchValue, 'gi'),
|
|
44
|
-
(match) => `<span class="mds-combobox__option--marked">${match}</span>`
|
|
45
|
-
);
|
|
46
|
-
return optionLabelHtml;
|
|
47
|
-
},
|
|
13
|
+
<script setup>
|
|
14
|
+
import { useTemplateRef, watch } from 'vue';
|
|
15
|
+
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
option: { type: Object, required: true },
|
|
18
|
+
focused: { type: Boolean, default: false },
|
|
19
|
+
searchValue: { type: String, default: '' },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const $listItem = useTemplateRef('$listItem');
|
|
23
|
+
|
|
24
|
+
watch(
|
|
25
|
+
() => props.focused,
|
|
26
|
+
(value) => {
|
|
27
|
+
if (value) {
|
|
28
|
+
$listItem.value?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
29
|
+
}
|
|
48
30
|
},
|
|
49
|
-
|
|
31
|
+
);
|
|
32
|
+
function highlightOption() {
|
|
33
|
+
const optionLabelHtml = props.option.label.replace(
|
|
34
|
+
new RegExp(props.searchValue, 'gi'),
|
|
35
|
+
(match) => `<span class="mds-combobox__option--marked">${match}</span>`,
|
|
36
|
+
);
|
|
37
|
+
return optionLabelHtml;
|
|
38
|
+
}
|
|
50
39
|
</script>
|