vueless 0.0.592 → 0.0.593
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/composables/useUI.ts +17 -15
- package/package.json +1 -1
- package/types.ts +2 -0
- package/ui.button/storybook/stories.ts +3 -1
- package/ui.button-link/storybook/stories.ts +3 -1
- package/ui.button-toggle/storybook/stories.ts +3 -1
- package/ui.button-toggle-item/storybook/stories.ts +3 -1
- package/ui.container-accordion/storybook/stories.ts +3 -1
- package/ui.container-card/storybook/stories.ts +3 -1
- package/ui.container-col/storybook/stories.ts +1 -1
- package/ui.container-divider/storybook/stories.ts +3 -1
- package/ui.container-group/storybook/stories.ts +1 -1
- package/ui.container-groups/storybook/stories.ts +1 -1
- package/ui.container-modal/storybook/stories.ts +1 -1
- package/ui.container-modal-confirm/storybook/stories.ts +1 -1
- package/ui.container-page/storybook/stories.ts +3 -1
- package/ui.container-row/storybook/stories.ts +3 -1
- package/ui.data-list/storybook/stories.ts +3 -1
- package/ui.data-table/storybook/stories.ts +3 -1
- package/ui.dropdown-badge/storybook/stories.ts +1 -1
- package/ui.dropdown-button/storybook/stories.ts +1 -1
- package/ui.dropdown-link/storybook/stories.ts +1 -1
- package/ui.dropdown-list/storybook/stories.ts +1 -1
- package/ui.dropdown-list/types.ts +1 -1
- package/ui.form-calendar/storybook/stories.ts +1 -1
- package/ui.form-checkbox/storybook/stories.ts +3 -1
- package/ui.form-checkbox-group/storybook/stories.ts +3 -1
- package/ui.form-checkbox-multi-state/storybook/stories.ts +3 -1
- package/ui.form-color-picker/storybook/stories.ts +3 -1
- package/ui.form-date-picker/storybook/stories.ts +1 -1
- package/ui.form-date-picker-range/storybook/stories.ts +3 -1
- package/ui.form-input/storybook/stories.ts +3 -1
- package/ui.form-input-file/storybook/stories.ts +3 -1
- package/ui.form-input-money/storybook/stories.ts +3 -1
- package/ui.form-input-number/storybook/stories.ts +3 -1
- package/ui.form-input-rating/storybook/stories.ts +3 -1
- package/ui.form-input-search/storybook/stories.ts +3 -1
- package/ui.form-label/ULabel.vue +1 -1
- package/ui.form-label/storybook/stories.ts +3 -1
- package/ui.form-radio/storybook/stories.ts +3 -1
- package/ui.form-radio-group/storybook/stories.ts +3 -1
- package/ui.form-select/USelect.vue +535 -682
- package/ui.form-select/{config.js → config.ts} +2 -1
- package/ui.form-select/{constants.js → constants.ts} +0 -5
- package/ui.form-select/storybook/Docs.mdx +2 -2
- package/ui.form-select/storybook/{stories.js → stories.ts} +23 -13
- package/ui.form-select/types.ts +134 -0
- package/ui.form-select/utilSelect.ts +122 -0
- package/ui.form-switch/storybook/stories.ts +3 -1
- package/ui.form-textarea/storybook/stories.ts +3 -1
- package/ui.image-avatar/storybook/stories.ts +3 -1
- package/ui.image-icon/storybook/stories.ts +3 -1
- package/ui.loader/storybook/stories.ts +3 -1
- package/ui.loader-overlay/storybook/stories.ts +1 -1
- package/ui.loader-progress/storybook/stories.ts +3 -1
- package/ui.navigation-progress/storybook/stories.ts +3 -1
- package/ui.navigation-tab/storybook/stories.ts +3 -1
- package/ui.navigation-tabs/storybook/stories.ts +3 -1
- package/ui.other-dot/storybook/stories.ts +3 -1
- package/ui.text-alert/storybook/stories.ts +3 -1
- package/ui.text-badge/storybook/stories.ts +3 -1
- package/ui.text-block/storybook/stories.ts +3 -1
- package/ui.text-empty/storybook/stories.ts +3 -1
- package/ui.text-file/storybook/stories.ts +3 -1
- package/ui.text-files/storybook/stories.ts +3 -1
- package/ui.text-header/storybook/stories.ts +3 -1
- package/ui.text-money/storybook/stories.ts +3 -1
- package/ui.text-notify/storybook/stories.ts +1 -1
- package/utils/storybook.ts +3 -5
- package/web-types.json +77 -35
- package/ui.form-select/utilSelect.js +0 -100
|
@@ -1,316 +1,4 @@
|
|
|
1
|
-
<
|
|
2
|
-
<ULabel
|
|
3
|
-
ref="labelComponentRef"
|
|
4
|
-
:for="elementId"
|
|
5
|
-
:size="size"
|
|
6
|
-
:label="label"
|
|
7
|
-
:error="error"
|
|
8
|
-
:description="description"
|
|
9
|
-
:align="labelAlign"
|
|
10
|
-
:disabled="disabled"
|
|
11
|
-
centred
|
|
12
|
-
v-bind="selectLabelAttrs"
|
|
13
|
-
:data-test="dataTest"
|
|
14
|
-
:tabindex="-1"
|
|
15
|
-
>
|
|
16
|
-
<div
|
|
17
|
-
ref="wrapperRef"
|
|
18
|
-
:tabindex="searchable || disabled ? -1 : 0"
|
|
19
|
-
role="combobox"
|
|
20
|
-
:aria-owns="'listbox-' + elementId"
|
|
21
|
-
v-bind="wrapperAttrs"
|
|
22
|
-
@focus="activate"
|
|
23
|
-
@blur="deactivate"
|
|
24
|
-
@keydown.self.down.prevent="dropdownListRef.pointerForward"
|
|
25
|
-
@keydown.self.up.prevent="dropdownListRef.pointerBackward"
|
|
26
|
-
@keydown.enter.tab.stop.self="dropdownListRef.addPointerElement"
|
|
27
|
-
@keyup.esc="deactivate"
|
|
28
|
-
>
|
|
29
|
-
<!-- @slot Use it to add something right. -->
|
|
30
|
-
<slot name="right" />
|
|
31
|
-
|
|
32
|
-
<div v-if="hasSlotContent($slots['right-icon']) || rightIcon" v-bind="rightIconWrapperAttrs">
|
|
33
|
-
<!--
|
|
34
|
-
@slot Use it to add icon right.
|
|
35
|
-
@binding {string} icon-name
|
|
36
|
-
@binding {string} icon-size
|
|
37
|
-
-->
|
|
38
|
-
<slot name="right-icon" :icon-name="rightIcon" :icon-size="iconSize">
|
|
39
|
-
<UIcon
|
|
40
|
-
v-if="rightIcon"
|
|
41
|
-
:name="rightIcon"
|
|
42
|
-
:size="iconSize"
|
|
43
|
-
internal
|
|
44
|
-
v-bind="rightIconAttrs"
|
|
45
|
-
/>
|
|
46
|
-
</slot>
|
|
47
|
-
</div>
|
|
48
|
-
|
|
49
|
-
<div
|
|
50
|
-
v-if="hasSlotContent($slots['after-caret']) && !(multiple && localValue.length)"
|
|
51
|
-
v-bind="afterCaretAttrs"
|
|
52
|
-
:tabindex="-1"
|
|
53
|
-
>
|
|
54
|
-
<!--
|
|
55
|
-
@slot Use it to add something after caret.
|
|
56
|
-
@binding {object} scope-props
|
|
57
|
-
-->
|
|
58
|
-
<slot :scope-props="props" name="after-caret" />
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div
|
|
62
|
-
v-show="!multiple || (!isLocalValue && multiple)"
|
|
63
|
-
v-bind="toggleAttrs"
|
|
64
|
-
:tabindex="-1"
|
|
65
|
-
@mousedown.prevent.stop="toggle"
|
|
66
|
-
>
|
|
67
|
-
<!--
|
|
68
|
-
@slot Use it to add something instead of the toggle icon.
|
|
69
|
-
@binding {string} icon-name
|
|
70
|
-
@binding {string} icon-size
|
|
71
|
-
@binding {boolean} opened
|
|
72
|
-
-->
|
|
73
|
-
<slot
|
|
74
|
-
name="toggle"
|
|
75
|
-
:icon-name="config.defaults.dropdownIcon"
|
|
76
|
-
:icon-size="iconSize"
|
|
77
|
-
:opened="isOpen"
|
|
78
|
-
>
|
|
79
|
-
<UIcon
|
|
80
|
-
internal
|
|
81
|
-
interactive
|
|
82
|
-
color="gray"
|
|
83
|
-
:size="iconSize"
|
|
84
|
-
:name="config.defaults.dropdownIcon"
|
|
85
|
-
v-bind="toggleIconAttrs"
|
|
86
|
-
:tabindex="-1"
|
|
87
|
-
/>
|
|
88
|
-
</slot>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<div
|
|
92
|
-
v-if="isLocalValue && !clearable && !disabled && !multiple"
|
|
93
|
-
v-bind="clearAttrs"
|
|
94
|
-
@mousedown="removeElement"
|
|
95
|
-
>
|
|
96
|
-
<!--
|
|
97
|
-
@slot Use it to add something instead of the clear icon.
|
|
98
|
-
@binding {string} icon-name
|
|
99
|
-
@binding {string} icon-size
|
|
100
|
-
-->
|
|
101
|
-
<slot name="clear" :icon-name="config.defaults.clearIcon" :icon-size="iconSize">
|
|
102
|
-
<UIcon
|
|
103
|
-
internal
|
|
104
|
-
interactive
|
|
105
|
-
color="gray"
|
|
106
|
-
:size="iconSize"
|
|
107
|
-
:name="config.defaults.clearIcon"
|
|
108
|
-
v-bind="clearIconAttrs"
|
|
109
|
-
/>
|
|
110
|
-
</slot>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<div
|
|
114
|
-
v-if="hasSlotContent($slots['before-caret']) && !(multiple && localValue.length)"
|
|
115
|
-
v-bind="beforeCaretAttrs"
|
|
116
|
-
>
|
|
117
|
-
<!--
|
|
118
|
-
@slot Use it to add something before caret.
|
|
119
|
-
@binding {object} scope-props
|
|
120
|
-
-->
|
|
121
|
-
<slot :scope-props="props" name="before-caret" />
|
|
122
|
-
</div>
|
|
123
|
-
|
|
124
|
-
<div ref="innerWrapperRef" v-bind="innerWrapperAttrs">
|
|
125
|
-
<span
|
|
126
|
-
v-if="hasSlotContent($slots['left-icon']) || leftIcon"
|
|
127
|
-
ref="leftSlotWrapperRef"
|
|
128
|
-
v-bind="leftIconWrapperAttrs"
|
|
129
|
-
>
|
|
130
|
-
<!--
|
|
131
|
-
@slot Use it to add icon left.
|
|
132
|
-
@binding {string} icon-name
|
|
133
|
-
@binding {string} icon-size
|
|
134
|
-
-->
|
|
135
|
-
<slot name="left-icon" :icon-name="leftIcon" :icon-size="iconSize">
|
|
136
|
-
<UIcon
|
|
137
|
-
v-if="leftIcon"
|
|
138
|
-
:name="leftIcon"
|
|
139
|
-
:size="iconSize"
|
|
140
|
-
internal
|
|
141
|
-
v-bind="leftIconAttrs"
|
|
142
|
-
/>
|
|
143
|
-
</slot>
|
|
144
|
-
</span>
|
|
145
|
-
|
|
146
|
-
<span v-if="hasSlotContent($slots['left'])" ref="leftSlotWrapperRef">
|
|
147
|
-
<!-- @slot Use it to add something left. -->
|
|
148
|
-
<slot name="left" />
|
|
149
|
-
</span>
|
|
150
|
-
|
|
151
|
-
<div v-if="multiple && localValue.length" v-bind="selectedLabelsAttrs">
|
|
152
|
-
<span v-for="item in localValue" :key="item[valueKey]" v-bind="selectedLabelAttrs">
|
|
153
|
-
<!--
|
|
154
|
-
@slot Use it to customise selected value label.
|
|
155
|
-
@binding {string} selected-label
|
|
156
|
-
-->
|
|
157
|
-
<slot
|
|
158
|
-
name="selected-label"
|
|
159
|
-
:selected-label="getOptionLabel(item)"
|
|
160
|
-
:value="item[valueKey]"
|
|
161
|
-
:raw-option="item"
|
|
162
|
-
>
|
|
163
|
-
{{ getOptionLabel(item) }}
|
|
164
|
-
</slot>
|
|
165
|
-
|
|
166
|
-
<!--
|
|
167
|
-
@slot Use it to add something after selected value label.
|
|
168
|
-
@binding {object} scope-props
|
|
169
|
-
-->
|
|
170
|
-
<slot :scope-props="props" name="selected-label-after" />
|
|
171
|
-
|
|
172
|
-
<div
|
|
173
|
-
v-if="!disabled"
|
|
174
|
-
v-bind="clearMultipleAttrs"
|
|
175
|
-
@mousedown.prevent.capture
|
|
176
|
-
@click.prevent.capture
|
|
177
|
-
@mousedown="removeElement(item)"
|
|
178
|
-
>
|
|
179
|
-
<!--
|
|
180
|
-
@slot Use it to add something instead of the clear icon (when multiple prop enabled).
|
|
181
|
-
@binding {string} icon-name
|
|
182
|
-
@binding {string} icon-size
|
|
183
|
-
-->
|
|
184
|
-
<slot
|
|
185
|
-
name="clear-multiple"
|
|
186
|
-
:icon-name="config.defaults.clearMultipleIcon"
|
|
187
|
-
:icon-size="iconSize"
|
|
188
|
-
>
|
|
189
|
-
<UIcon
|
|
190
|
-
internal
|
|
191
|
-
interactive
|
|
192
|
-
color="gray"
|
|
193
|
-
:size="iconSize"
|
|
194
|
-
:name="config.defaults.clearMultipleIcon"
|
|
195
|
-
v-bind="clearMultipleIconAttrs"
|
|
196
|
-
/>
|
|
197
|
-
</slot>
|
|
198
|
-
</div>
|
|
199
|
-
</span>
|
|
200
|
-
</div>
|
|
201
|
-
|
|
202
|
-
<div v-bind="searchAttrs">
|
|
203
|
-
<input
|
|
204
|
-
v-show="searchable || !localValue || multiple || !isOpen"
|
|
205
|
-
:id="elementId"
|
|
206
|
-
ref="searchInputRef"
|
|
207
|
-
v-model="search"
|
|
208
|
-
type="text"
|
|
209
|
-
autocomplete="off"
|
|
210
|
-
:spellcheck="false"
|
|
211
|
-
:placeholder="inputPlaceholder"
|
|
212
|
-
:disabled="disabled"
|
|
213
|
-
:aria-controls="'listbox-' + elementId"
|
|
214
|
-
v-bind="searchInputAttrs"
|
|
215
|
-
@focus="activate"
|
|
216
|
-
@blur.prevent="deactivate"
|
|
217
|
-
@keyup.esc="deactivate"
|
|
218
|
-
@keydown.down.prevent="dropdownListRef.pointerForward"
|
|
219
|
-
@keydown.up.prevent="dropdownListRef.pointerBackward"
|
|
220
|
-
@keydown.enter.prevent.stop.self="dropdownListRef.addPointerElement"
|
|
221
|
-
/>
|
|
222
|
-
</div>
|
|
223
|
-
|
|
224
|
-
<span
|
|
225
|
-
v-if="isSelectedValueLabelVisible"
|
|
226
|
-
v-bind="selectedLabelAttrs"
|
|
227
|
-
@mousedown.prevent="toggle"
|
|
228
|
-
>
|
|
229
|
-
<!--
|
|
230
|
-
@slot Use it to add selected value label.
|
|
231
|
-
@binding {string} selected-label
|
|
232
|
-
@binding {string} value
|
|
233
|
-
-->
|
|
234
|
-
<slot name="selected-label" :selected-label="selectedLabel" :value="localValue[valueKey]">
|
|
235
|
-
{{ selectedLabel }}
|
|
236
|
-
</slot>
|
|
237
|
-
|
|
238
|
-
<!--
|
|
239
|
-
@slot Use it to add something after selected value label.
|
|
240
|
-
@binding {object} scope-props
|
|
241
|
-
-->
|
|
242
|
-
<slot :scope-props="props" name="selected-label-after" />
|
|
243
|
-
</span>
|
|
244
|
-
|
|
245
|
-
<div
|
|
246
|
-
v-if="isLocalValue && !clearable && !disabled && multiple"
|
|
247
|
-
v-bind="clearMultipleTextAttrs"
|
|
248
|
-
@mousedown.prevent.capture="removeElement(localValue)"
|
|
249
|
-
@click.prevent.capture
|
|
250
|
-
v-text="currentLocale.clear"
|
|
251
|
-
/>
|
|
252
|
-
</div>
|
|
253
|
-
|
|
254
|
-
<UDropdownList
|
|
255
|
-
v-if="isOpen"
|
|
256
|
-
ref="dropdownListRef"
|
|
257
|
-
v-model="dropdownValue"
|
|
258
|
-
:options="filteredOptions"
|
|
259
|
-
:disabled="disabled"
|
|
260
|
-
:size="size"
|
|
261
|
-
:visible-options="visibleOptions"
|
|
262
|
-
:value-key="valueKey"
|
|
263
|
-
:label-key="labelKey"
|
|
264
|
-
:add-option="addOption"
|
|
265
|
-
tabindex="-1"
|
|
266
|
-
v-bind="dropdownListAttrs"
|
|
267
|
-
@add="onAddOption"
|
|
268
|
-
@focus="activate"
|
|
269
|
-
@mousedown.prevent.capture
|
|
270
|
-
@click.prevent.capture
|
|
271
|
-
>
|
|
272
|
-
<template #before-option="{ option, index }">
|
|
273
|
-
<!--
|
|
274
|
-
@slot Use it to add something before option.
|
|
275
|
-
@binding {object} option
|
|
276
|
-
@binding {number} index
|
|
277
|
-
-->
|
|
278
|
-
<slot name="before-option" :option="option" :index="index" />
|
|
279
|
-
</template>
|
|
280
|
-
|
|
281
|
-
<template #option="{ option, index }">
|
|
282
|
-
<!--
|
|
283
|
-
@slot Use it to customise the option.
|
|
284
|
-
@binding {object} option
|
|
285
|
-
@binding {number} index
|
|
286
|
-
-->
|
|
287
|
-
<slot name="option" :option="option" :index="index" />
|
|
288
|
-
</template>
|
|
289
|
-
|
|
290
|
-
<template #after-option="{ option, index }">
|
|
291
|
-
<!--
|
|
292
|
-
@slot Use it to add something after option.
|
|
293
|
-
@binding {object} option
|
|
294
|
-
@binding {number} index
|
|
295
|
-
-->
|
|
296
|
-
<slot name="after-option" :option="option" :index="index" />
|
|
297
|
-
</template>
|
|
298
|
-
|
|
299
|
-
<template #empty>
|
|
300
|
-
<template v-if="isEmpty">
|
|
301
|
-
{{ currentLocale.listIsEmpty }}
|
|
302
|
-
</template>
|
|
303
|
-
|
|
304
|
-
<template v-else>
|
|
305
|
-
{{ currentLocale.noDataToShow }}
|
|
306
|
-
</template>
|
|
307
|
-
</template>
|
|
308
|
-
</UDropdownList>
|
|
309
|
-
</div>
|
|
310
|
-
</ULabel>
|
|
311
|
-
</template>
|
|
312
|
-
|
|
313
|
-
<script setup>
|
|
1
|
+
<script setup lang="ts">
|
|
314
2
|
import { ref, computed, nextTick, watch, useSlots, onMounted, useId } from "vue";
|
|
315
3
|
import { merge } from "lodash-es";
|
|
316
4
|
|
|
@@ -323,247 +11,61 @@ import { createDebounce, hasSlotContent } from "../utils/helper.ts";
|
|
|
323
11
|
import { getDefaults } from "../utils/ui.ts";
|
|
324
12
|
import { isMac } from "../utils/platform.ts";
|
|
325
13
|
|
|
326
|
-
import
|
|
327
|
-
|
|
328
|
-
|
|
14
|
+
import {
|
|
15
|
+
filterOptions,
|
|
16
|
+
filterGroups,
|
|
17
|
+
removeSelectedValues,
|
|
18
|
+
getCurrentOption,
|
|
19
|
+
} from "./utilSelect.ts";
|
|
20
|
+
import defaultConfig from "./config.ts";
|
|
21
|
+
import { USelect, DIRECTION, KEYS } from "./constants.ts";
|
|
329
22
|
|
|
330
23
|
import { useLocale } from "../composables/useLocale.ts";
|
|
331
24
|
|
|
332
|
-
|
|
25
|
+
import type { Option, Config as UDropdownListConfig } from "../ui.dropdown-list/types.ts";
|
|
26
|
+
import type { Props, Config, IconSize } from "./types.ts";
|
|
27
|
+
import type { ComponentExposed, KeyAttrsWithConfig } from "../types.ts";
|
|
333
28
|
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Select value.
|
|
337
|
-
*/
|
|
338
|
-
modelValue: {
|
|
339
|
-
type: [String, Number, Array],
|
|
340
|
-
default: "",
|
|
341
|
-
},
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Select options.
|
|
345
|
-
*/
|
|
346
|
-
options: {
|
|
347
|
-
type: Array,
|
|
348
|
-
default: () => [],
|
|
349
|
-
},
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Select label.
|
|
353
|
-
*/
|
|
354
|
-
label: {
|
|
355
|
-
type: String,
|
|
356
|
-
default: "",
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Label placement.
|
|
361
|
-
* @values top, topInside, topWithDesc, left, right
|
|
362
|
-
*/
|
|
363
|
-
labelAlign: {
|
|
364
|
-
type: String,
|
|
365
|
-
default: getDefaults(defaultConfig, USelect).labelAlign,
|
|
366
|
-
},
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Select placeholder.
|
|
370
|
-
*/
|
|
371
|
-
placeholder: {
|
|
372
|
-
type: String,
|
|
373
|
-
default: "",
|
|
374
|
-
},
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Select description.
|
|
378
|
-
*/
|
|
379
|
-
description: {
|
|
380
|
-
type: String,
|
|
381
|
-
default: "",
|
|
382
|
-
},
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Select error message.
|
|
386
|
-
*/
|
|
387
|
-
error: {
|
|
388
|
-
type: String,
|
|
389
|
-
default: "",
|
|
390
|
-
},
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Select size.
|
|
394
|
-
* @values sm, md, lg
|
|
395
|
-
*/
|
|
396
|
-
size: {
|
|
397
|
-
type: String,
|
|
398
|
-
default: getDefaults(defaultConfig, USelect).size,
|
|
399
|
-
},
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Left icon name.
|
|
403
|
-
*/
|
|
404
|
-
leftIcon: {
|
|
405
|
-
type: String,
|
|
406
|
-
default: "",
|
|
407
|
-
},
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Right icon name.
|
|
411
|
-
*/
|
|
412
|
-
rightIcon: {
|
|
413
|
-
type: String,
|
|
414
|
-
default: "",
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Select open direction.
|
|
419
|
-
* @values auto, top, bottom
|
|
420
|
-
*/
|
|
421
|
-
openDirection: {
|
|
422
|
-
type: String,
|
|
423
|
-
default: getDefaults(defaultConfig, USelect).openDirection,
|
|
424
|
-
},
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Label key in the item object of options.
|
|
428
|
-
*/
|
|
429
|
-
labelKey: {
|
|
430
|
-
type: String,
|
|
431
|
-
default: getDefaults(defaultConfig, USelect).labelKey,
|
|
432
|
-
},
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Value key in the item object of options.
|
|
436
|
-
*/
|
|
437
|
-
valueKey: {
|
|
438
|
-
type: String,
|
|
439
|
-
default: getDefaults(defaultConfig, USelect).valueKey,
|
|
440
|
-
},
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Set a name of the property containing the group label.
|
|
444
|
-
*/
|
|
445
|
-
groupLabelKey: {
|
|
446
|
-
type: String,
|
|
447
|
-
default: "label",
|
|
448
|
-
},
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Set a name of the property containing the group values.
|
|
452
|
-
*/
|
|
453
|
-
groupValueKey: {
|
|
454
|
-
type: String,
|
|
455
|
-
default: "",
|
|
456
|
-
},
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Number of options displayed in the dropdown.
|
|
460
|
-
*/
|
|
461
|
-
optionsLimit: {
|
|
462
|
-
type: Number,
|
|
463
|
-
default: getDefaults(defaultConfig, USelect).optionsLimit,
|
|
464
|
-
},
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Amount of options you can see without scroll.
|
|
468
|
-
*/
|
|
469
|
-
visibleOptions: {
|
|
470
|
-
type: Number,
|
|
471
|
-
default: getDefaults(defaultConfig, USelect).visibleOptions,
|
|
472
|
-
},
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Allow clearing selected value.
|
|
476
|
-
*/
|
|
477
|
-
clearable: {
|
|
478
|
-
type: Boolean,
|
|
479
|
-
default: getDefaults(defaultConfig, USelect).clearable,
|
|
480
|
-
},
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Allows multiple selection.
|
|
484
|
-
*/
|
|
485
|
-
multiple: {
|
|
486
|
-
type: Boolean,
|
|
487
|
-
default: getDefaults(defaultConfig, USelect).multiple,
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
/**
|
|
491
|
-
* Allows to search value in a list.
|
|
492
|
-
*/
|
|
493
|
-
searchable: {
|
|
494
|
-
type: Boolean,
|
|
495
|
-
default: getDefaults(defaultConfig, USelect).searchable,
|
|
496
|
-
},
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Disable the select.
|
|
500
|
-
*/
|
|
501
|
-
disabled: {
|
|
502
|
-
type: Boolean,
|
|
503
|
-
default: getDefaults(defaultConfig, USelect).disabled,
|
|
504
|
-
},
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Show "Add new option" button in the list.
|
|
508
|
-
*/
|
|
509
|
-
addOption: {
|
|
510
|
-
type: Boolean,
|
|
511
|
-
default: getDefaults(defaultConfig, USelect).addOption,
|
|
512
|
-
},
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* Unique element id.
|
|
516
|
-
*/
|
|
517
|
-
id: {
|
|
518
|
-
type: String,
|
|
519
|
-
default: "",
|
|
520
|
-
},
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Component config object.
|
|
524
|
-
*/
|
|
525
|
-
config: {
|
|
526
|
-
type: Object,
|
|
527
|
-
default: () => ({}),
|
|
528
|
-
},
|
|
29
|
+
defineOptions({ inheritAttrs: false });
|
|
529
30
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
},
|
|
31
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
32
|
+
...getDefaults<Props, Config>(defaultConfig, USelect),
|
|
33
|
+
options: () => [],
|
|
34
|
+
modelValue: "",
|
|
35
|
+
label: "",
|
|
36
|
+
placeholder: "",
|
|
537
37
|
});
|
|
538
38
|
|
|
539
39
|
const emit = defineEmits([
|
|
540
40
|
/**
|
|
541
41
|
* Triggers when a dropdown list is opened.
|
|
542
|
-
* @property {string}
|
|
42
|
+
* @property {string} elementId
|
|
543
43
|
*/
|
|
544
44
|
"open",
|
|
545
45
|
|
|
546
46
|
/**
|
|
547
47
|
* Triggers when a dropdown list is closed.
|
|
548
|
-
* @property {string}
|
|
48
|
+
* @property {string} elementId
|
|
549
49
|
*/
|
|
550
50
|
"close",
|
|
551
51
|
|
|
552
52
|
/**
|
|
553
53
|
* Triggers when the search value is changed.
|
|
54
|
+
* @property {string} query
|
|
554
55
|
*/
|
|
555
56
|
"searchChange",
|
|
556
57
|
|
|
557
58
|
/**
|
|
558
59
|
* Triggers when option is removed.
|
|
559
60
|
* @property {string} option
|
|
560
|
-
* @property {string} propsId
|
|
561
61
|
*/
|
|
562
62
|
"remove",
|
|
563
63
|
|
|
564
64
|
/**
|
|
565
65
|
* Triggers when option is selected.
|
|
66
|
+
* @property {string} value
|
|
566
67
|
* @property {number} value
|
|
68
|
+
* @property {Option} value
|
|
567
69
|
*/
|
|
568
70
|
"update:modelValue",
|
|
569
71
|
|
|
@@ -585,12 +87,17 @@ const isOpen = ref(false);
|
|
|
585
87
|
const preferredOpenDirection = ref(DIRECTION.bottom);
|
|
586
88
|
const search = ref("");
|
|
587
89
|
|
|
588
|
-
const dropdownListRef = ref(null);
|
|
589
|
-
const wrapperRef = ref(null);
|
|
590
|
-
const searchInputRef = ref(null);
|
|
591
|
-
const labelComponentRef = ref(null);
|
|
592
|
-
const leftSlotWrapperRef = ref(null);
|
|
593
|
-
const innerWrapperRef = ref(null);
|
|
90
|
+
const dropdownListRef = ref<ComponentExposed<typeof UDropdownList> | null>(null);
|
|
91
|
+
const wrapperRef = ref<HTMLDivElement | null>(null);
|
|
92
|
+
const searchInputRef = ref<HTMLInputElement | null>(null);
|
|
93
|
+
const labelComponentRef = ref<ComponentExposed<typeof ULabel> | null>(null);
|
|
94
|
+
const leftSlotWrapperRef = ref<HTMLSpanElement | null>(null);
|
|
95
|
+
const innerWrapperRef = ref<HTMLDivElement | null>(null);
|
|
96
|
+
|
|
97
|
+
const elementId = props.id || useId();
|
|
98
|
+
|
|
99
|
+
const i18nGlobal = tm(USelect);
|
|
100
|
+
const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
|
|
594
101
|
|
|
595
102
|
const isTop = computed(() => {
|
|
596
103
|
if (props.openDirection === DIRECTION.top) return true;
|
|
@@ -599,11 +106,6 @@ const isTop = computed(() => {
|
|
|
599
106
|
return preferredOpenDirection.value === DIRECTION.top;
|
|
600
107
|
});
|
|
601
108
|
|
|
602
|
-
const elementId = props.id || useId();
|
|
603
|
-
|
|
604
|
-
const i18nGlobal = tm(USelect);
|
|
605
|
-
const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
|
|
606
|
-
|
|
607
109
|
const iconSize = computed(() => {
|
|
608
110
|
const sizes = {
|
|
609
111
|
sm: "xs",
|
|
@@ -611,22 +113,24 @@ const iconSize = computed(() => {
|
|
|
611
113
|
lg: "md",
|
|
612
114
|
};
|
|
613
115
|
|
|
614
|
-
return sizes[props.size];
|
|
116
|
+
return sizes[props.size] as IconSize;
|
|
615
117
|
});
|
|
616
118
|
|
|
617
119
|
const inputPlaceholder = computed(() => {
|
|
618
120
|
const message = currentLocale.value.addMore;
|
|
619
121
|
|
|
620
|
-
return props.multiple && localValue.value
|
|
122
|
+
return props.multiple && localValue.value?.length ? message : props.placeholder;
|
|
621
123
|
});
|
|
622
124
|
|
|
623
125
|
const dropdownValue = computed({
|
|
624
126
|
get: () => props.modelValue,
|
|
625
127
|
set: (newValue) => {
|
|
626
|
-
let value
|
|
128
|
+
let value;
|
|
627
129
|
|
|
628
130
|
if (props.multiple) {
|
|
629
131
|
value = Array.isArray(props.modelValue) ? [...props.modelValue, newValue] : [newValue];
|
|
132
|
+
} else {
|
|
133
|
+
value = newValue;
|
|
630
134
|
}
|
|
631
135
|
|
|
632
136
|
emit("update:modelValue", value);
|
|
@@ -642,55 +146,76 @@ const isSelectedValueLabelVisible = computed(() => {
|
|
|
642
146
|
const filteredOptions = computed(() => {
|
|
643
147
|
const normalizedSearch = search.value.toLowerCase().trim() || "";
|
|
644
148
|
|
|
149
|
+
let selectedValues: (string | number)[] = [];
|
|
150
|
+
|
|
151
|
+
if (Array.isArray(props.modelValue)) {
|
|
152
|
+
selectedValues = props.modelValue.map((value) => {
|
|
153
|
+
if (typeof value === "object") {
|
|
154
|
+
return value[props.valueKey] as string | number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return value;
|
|
158
|
+
});
|
|
159
|
+
} else if (props.modelValue) {
|
|
160
|
+
selectedValues =
|
|
161
|
+
typeof props.modelValue === "object"
|
|
162
|
+
? [props.modelValue[props.valueKey]]
|
|
163
|
+
: [props.modelValue];
|
|
164
|
+
}
|
|
165
|
+
|
|
645
166
|
let options = props.multiple
|
|
646
|
-
?
|
|
647
|
-
props.options,
|
|
648
|
-
props.groupValueKey,
|
|
649
|
-
props.valueKey,
|
|
650
|
-
props.modelValue,
|
|
651
|
-
)
|
|
167
|
+
? removeSelectedValues(props.options, selectedValues, props.valueKey, props.groupValueKey)
|
|
652
168
|
: [...props.options];
|
|
653
169
|
|
|
654
170
|
options = props.groupValueKey
|
|
655
|
-
?
|
|
656
|
-
|
|
171
|
+
? filterGroups(
|
|
172
|
+
options,
|
|
173
|
+
normalizedSearch,
|
|
174
|
+
props.labelKey,
|
|
175
|
+
props.groupValueKey,
|
|
176
|
+
props.groupLabelKey,
|
|
177
|
+
)
|
|
178
|
+
: filterOptions(options, normalizedSearch, props.labelKey);
|
|
657
179
|
|
|
658
180
|
return options.slice(0, props.optionsLimit || options.length);
|
|
659
181
|
});
|
|
660
182
|
|
|
661
183
|
const localValue = computed(() => {
|
|
662
184
|
if (!props.multiple) {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
props.groupValueKey,
|
|
667
|
-
props.valueKey,
|
|
668
|
-
);
|
|
185
|
+
const [singleValue] = Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue];
|
|
186
|
+
|
|
187
|
+
return getCurrentOption(props.options, singleValue, props.valueKey, props.groupValueKey);
|
|
669
188
|
}
|
|
670
189
|
|
|
671
|
-
return props.modelValue
|
|
672
|
-
? props.modelValue.map((
|
|
673
|
-
|
|
190
|
+
return props.modelValue && Array.isArray(props.modelValue)
|
|
191
|
+
? props.modelValue.map((value) =>
|
|
192
|
+
getCurrentOption(props.options, value, props.valueKey, props.groupValueKey),
|
|
674
193
|
)
|
|
675
194
|
: [];
|
|
676
195
|
});
|
|
677
196
|
|
|
678
197
|
const isLocalValue = computed(() => {
|
|
679
|
-
|
|
198
|
+
const value = localValue.value;
|
|
680
199
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
200
|
+
if (Array.isArray(value)) {
|
|
201
|
+
return !!value.length;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (typeof value === "object") {
|
|
205
|
+
return !!Object.keys(value).length;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return !!String(value);
|
|
684
209
|
});
|
|
685
210
|
|
|
686
211
|
const selectedLabel = computed(() => {
|
|
687
|
-
return isLocalValue.value ? getOptionLabel(localValue.value) : "";
|
|
212
|
+
return isLocalValue.value ? getOptionLabel(localValue.value as Option) : "";
|
|
688
213
|
});
|
|
689
214
|
|
|
690
215
|
const isEmpty = computed(() => {
|
|
691
216
|
return (
|
|
692
217
|
(filteredOptions.value.length === 0 && search) ||
|
|
693
|
-
(props.multiple && localValue.value
|
|
218
|
+
(props.multiple && localValue.value?.length === props.options.length)
|
|
694
219
|
);
|
|
695
220
|
});
|
|
696
221
|
|
|
@@ -707,16 +232,16 @@ if (props.addOption) {
|
|
|
707
232
|
|
|
708
233
|
onMounted(setLabelPosition);
|
|
709
234
|
|
|
710
|
-
function getOptionLabel(option) {
|
|
235
|
+
function getOptionLabel(option: Option) {
|
|
711
236
|
if (!option) return "";
|
|
712
237
|
|
|
713
238
|
return option[props.labelKey] || "";
|
|
714
239
|
}
|
|
715
240
|
|
|
716
|
-
function onKeydownAddOption(event) {
|
|
241
|
+
function onKeydownAddOption(event: KeyboardEvent) {
|
|
717
242
|
if (!isOpen.value) return;
|
|
718
243
|
|
|
719
|
-
const isEnter = event.
|
|
244
|
+
const isEnter = event.key === KEYS.enter;
|
|
720
245
|
const isCtrl = event.ctrlKey;
|
|
721
246
|
const isMeta = event.metaKey;
|
|
722
247
|
|
|
@@ -735,18 +260,6 @@ function onAddOption() {
|
|
|
735
260
|
emit("add");
|
|
736
261
|
}
|
|
737
262
|
|
|
738
|
-
function filterAndFlat(options, search, label) {
|
|
739
|
-
const filteredGroups = SelectService.filterGroups(
|
|
740
|
-
options,
|
|
741
|
-
search,
|
|
742
|
-
label,
|
|
743
|
-
props.groupValueKey,
|
|
744
|
-
props.groupLabelKey,
|
|
745
|
-
);
|
|
746
|
-
|
|
747
|
-
return SelectService.flattenOptions(filteredGroups, props.groupValueKey, props.groupLabelKey);
|
|
748
|
-
}
|
|
749
|
-
|
|
750
263
|
function toggle() {
|
|
751
264
|
isOpen.value ? deactivate() : activate();
|
|
752
265
|
}
|
|
@@ -763,7 +276,7 @@ function deactivate() {
|
|
|
763
276
|
}
|
|
764
277
|
|
|
765
278
|
function activate() {
|
|
766
|
-
if (
|
|
279
|
+
if (isOpen.value || props.disabled) return;
|
|
767
280
|
|
|
768
281
|
adjustPosition();
|
|
769
282
|
|
|
@@ -775,15 +288,17 @@ function activate() {
|
|
|
775
288
|
nextTick(() => searchInputRef.value && searchInputRef.value.focus());
|
|
776
289
|
}
|
|
777
290
|
|
|
778
|
-
if (wrapperRef.value
|
|
291
|
+
if (wrapperRef.value && !props.searchable) {
|
|
292
|
+
wrapperRef.value.focus();
|
|
293
|
+
}
|
|
779
294
|
|
|
780
295
|
emit("open", elementId);
|
|
781
296
|
}
|
|
782
297
|
|
|
783
298
|
function adjustPosition() {
|
|
784
|
-
if (typeof window === "undefined" || !dropdownListRef.value) return;
|
|
299
|
+
if (typeof window === "undefined" || !dropdownListRef.value || !wrapperRef.value) return;
|
|
785
300
|
|
|
786
|
-
const dropdownHeight = dropdownListRef.value.wrapperRef
|
|
301
|
+
const dropdownHeight = dropdownListRef.value.wrapperRef?.getBoundingClientRect().height || 0;
|
|
787
302
|
const spaceAbove = wrapperRef.value.getBoundingClientRect().top;
|
|
788
303
|
const spaceBelow = window.innerHeight - wrapperRef.value.getBoundingClientRect().bottom;
|
|
789
304
|
const hasEnoughSpaceBelow = spaceBelow > dropdownHeight;
|
|
@@ -795,128 +310,466 @@ function adjustPosition() {
|
|
|
795
310
|
}
|
|
796
311
|
}
|
|
797
312
|
|
|
798
|
-
function
|
|
313
|
+
function onMouseDownClearItem(event: MouseEvent, option: Option) {
|
|
314
|
+
if (props.disabled) return;
|
|
315
|
+
|
|
316
|
+
const value = Array.isArray(props.modelValue)
|
|
317
|
+
? [...props.modelValue].filter((item) => {
|
|
318
|
+
if (typeof item === "object") {
|
|
319
|
+
return item[props.valueKey] !== option[props.valueKey];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return item !== option[props.valueKey];
|
|
323
|
+
})
|
|
324
|
+
: [];
|
|
325
|
+
|
|
326
|
+
emit("update:modelValue", value);
|
|
327
|
+
emit("change", { value, options: props.options });
|
|
328
|
+
emit("remove", option);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function onMouseDownClear() {
|
|
799
332
|
if (props.disabled) return;
|
|
800
333
|
|
|
801
|
-
if (props.clearable && !props.multiple) {
|
|
334
|
+
if (!props.clearable && !props.multiple) {
|
|
802
335
|
deactivate();
|
|
803
336
|
|
|
804
337
|
return;
|
|
805
338
|
}
|
|
806
339
|
|
|
807
|
-
|
|
340
|
+
const value = props.multiple ? [] : "";
|
|
341
|
+
|
|
342
|
+
emit("update:modelValue", value);
|
|
343
|
+
emit("change", { value, options: props.options });
|
|
344
|
+
emit("remove", props.options);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function setLabelPosition() {
|
|
348
|
+
if (
|
|
349
|
+
props.labelAlign === "top" ||
|
|
350
|
+
!hasSlotContent(slots["left"]) ||
|
|
351
|
+
(!hasSlotContent(slots["left-icon"]) && !props.leftIcon)
|
|
352
|
+
) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!leftSlotWrapperRef.value || !innerWrapperRef.value || !labelComponentRef.value) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const leftSlotWidth = leftSlotWrapperRef.value.getBoundingClientRect().width;
|
|
361
|
+
const innerWrapperPaddingLeft = parseInt(
|
|
362
|
+
window.getComputedStyle(innerWrapperRef.value).paddingLeft,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const nestedLabel = labelComponentRef.value.labelElement;
|
|
366
|
+
|
|
367
|
+
if (props.multiple && Array.isArray(localValue.value) && localValue.value.length >= 1) {
|
|
368
|
+
if (nestedLabel) {
|
|
369
|
+
nestedLabel.style.left = `${leftSlotWidth - innerWrapperPaddingLeft}px`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
leftSlotWrapperRef.value.classList.remove("group-[]/placement-inside:-mt-4");
|
|
373
|
+
} else {
|
|
374
|
+
if (nestedLabel) {
|
|
375
|
+
nestedLabel.style.left = `${leftSlotWidth + innerWrapperPaddingLeft}px`;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
defineExpose({
|
|
381
|
+
/**
|
|
382
|
+
* A reference to the dropdown list element for direct DOM manipulation.
|
|
383
|
+
* @property {HTMLElement}
|
|
384
|
+
*/
|
|
385
|
+
dropdownListRef,
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* A reference to the wrapper element for direct DOM manipulation.
|
|
389
|
+
* @property {HTMLElement}
|
|
390
|
+
*/
|
|
391
|
+
wrapperRef,
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* A reference to the search input element for direct DOM manipulation.
|
|
395
|
+
* @property {HTMLElement}
|
|
396
|
+
*/
|
|
397
|
+
searchInputRef,
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* A reference to the label component for direct DOM manipulation.
|
|
401
|
+
* @property {HTMLElement}
|
|
402
|
+
*/
|
|
403
|
+
labelComponentRef,
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* A reference to the left slot wrapper element for direct DOM manipulation.
|
|
407
|
+
* @property {HTMLElement}
|
|
408
|
+
*/
|
|
409
|
+
leftSlotWrapperRef,
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* A reference to the inner wrapper element for direct DOM manipulation.
|
|
413
|
+
* @property {HTMLElement}
|
|
414
|
+
*/
|
|
415
|
+
innerWrapperRef,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Get element / nested component attributes for each config token ✨
|
|
420
|
+
* Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
|
|
421
|
+
*/
|
|
422
|
+
const mutatedProps = computed(() => ({
|
|
423
|
+
error: Boolean(props.error),
|
|
424
|
+
label: Boolean(props.label),
|
|
425
|
+
/* component state, not a props */
|
|
426
|
+
selected: Boolean(selectedLabel.value),
|
|
427
|
+
opened: isOpen.value,
|
|
428
|
+
openedTop: isTop.value,
|
|
429
|
+
}));
|
|
430
|
+
|
|
431
|
+
const {
|
|
432
|
+
config,
|
|
433
|
+
selectLabelAttrs,
|
|
434
|
+
wrapperAttrs,
|
|
435
|
+
innerWrapperAttrs,
|
|
436
|
+
leftIconWrapperAttrs,
|
|
437
|
+
rightIconWrapperAttrs,
|
|
438
|
+
leftIconAttrs,
|
|
439
|
+
rightIconAttrs,
|
|
440
|
+
beforeCaretAttrs,
|
|
441
|
+
afterCaretAttrs,
|
|
442
|
+
toggleAttrs,
|
|
443
|
+
clearAttrs,
|
|
444
|
+
clearMultipleTextAttrs,
|
|
445
|
+
clearMultipleAttrs,
|
|
446
|
+
searchAttrs,
|
|
447
|
+
searchInputAttrs,
|
|
448
|
+
selectedLabelsAttrs,
|
|
449
|
+
selectedLabelAttrs,
|
|
450
|
+
dropdownListAttrs,
|
|
451
|
+
toggleIconAttrs,
|
|
452
|
+
clearIconAttrs,
|
|
453
|
+
clearMultipleIconAttrs,
|
|
454
|
+
} = useUI(defaultConfig, mutatedProps);
|
|
455
|
+
</script>
|
|
456
|
+
|
|
457
|
+
<template>
|
|
458
|
+
<ULabel
|
|
459
|
+
ref="labelComponentRef"
|
|
460
|
+
:for="elementId"
|
|
461
|
+
:size="size"
|
|
462
|
+
:label="label"
|
|
463
|
+
:error="error"
|
|
464
|
+
:description="description"
|
|
465
|
+
:align="labelAlign"
|
|
466
|
+
:disabled="disabled"
|
|
467
|
+
centred
|
|
468
|
+
v-bind="selectLabelAttrs"
|
|
469
|
+
:data-test="dataTest"
|
|
470
|
+
:tabindex="-1"
|
|
471
|
+
>
|
|
472
|
+
<div
|
|
473
|
+
ref="wrapperRef"
|
|
474
|
+
:tabindex="searchable || disabled ? -1 : 0"
|
|
475
|
+
role="combobox"
|
|
476
|
+
:aria-owns="'listbox-' + elementId"
|
|
477
|
+
v-bind="wrapperAttrs"
|
|
478
|
+
@focus="activate"
|
|
479
|
+
@blur="deactivate"
|
|
480
|
+
@keydown.self.down.prevent="dropdownListRef?.pointerForward"
|
|
481
|
+
@keydown.self.up.prevent="dropdownListRef?.pointerBackward"
|
|
482
|
+
@keydown.enter.tab.stop.self="dropdownListRef?.addPointerElement"
|
|
483
|
+
@keyup.esc="deactivate"
|
|
484
|
+
>
|
|
485
|
+
<!-- @slot Use it to add something right. -->
|
|
486
|
+
<slot name="right" />
|
|
487
|
+
|
|
488
|
+
<div v-if="hasSlotContent($slots['right-icon']) || rightIcon" v-bind="rightIconWrapperAttrs">
|
|
489
|
+
<!--
|
|
490
|
+
@slot Use it to add icon right.
|
|
491
|
+
@binding {string} icon-name
|
|
492
|
+
@binding {string} icon-size
|
|
493
|
+
-->
|
|
494
|
+
<slot name="right-icon" :icon-name="rightIcon" :icon-size="iconSize">
|
|
495
|
+
<UIcon
|
|
496
|
+
v-if="rightIcon"
|
|
497
|
+
:name="rightIcon"
|
|
498
|
+
:size="iconSize"
|
|
499
|
+
internal
|
|
500
|
+
v-bind="rightIconAttrs"
|
|
501
|
+
/>
|
|
502
|
+
</slot>
|
|
503
|
+
</div>
|
|
504
|
+
|
|
505
|
+
<div
|
|
506
|
+
v-if="hasSlotContent($slots['after-caret']) && !(multiple && localValue?.length)"
|
|
507
|
+
v-bind="afterCaretAttrs"
|
|
508
|
+
:tabindex="-1"
|
|
509
|
+
>
|
|
510
|
+
<!--
|
|
511
|
+
@slot Use it to add something after caret.
|
|
512
|
+
@binding {object} scope-props
|
|
513
|
+
-->
|
|
514
|
+
<slot :scope-props="props" name="after-caret" />
|
|
515
|
+
</div>
|
|
516
|
+
|
|
517
|
+
<div
|
|
518
|
+
v-show="!multiple || (!isLocalValue && multiple)"
|
|
519
|
+
v-bind="toggleAttrs"
|
|
520
|
+
:tabindex="-1"
|
|
521
|
+
@mousedown.prevent.stop="toggle"
|
|
522
|
+
>
|
|
523
|
+
<!--
|
|
524
|
+
@slot Use it to add something instead of the toggle icon.
|
|
525
|
+
@binding {string} icon-name
|
|
526
|
+
@binding {string} icon-size
|
|
527
|
+
@binding {boolean} opened
|
|
528
|
+
-->
|
|
529
|
+
<slot
|
|
530
|
+
name="toggle"
|
|
531
|
+
:icon-name="config.defaults.dropdownIcon"
|
|
532
|
+
:icon-size="iconSize"
|
|
533
|
+
:opened="isOpen"
|
|
534
|
+
>
|
|
535
|
+
<UIcon
|
|
536
|
+
internal
|
|
537
|
+
interactive
|
|
538
|
+
color="gray"
|
|
539
|
+
:size="iconSize"
|
|
540
|
+
:name="config.defaults.dropdownIcon"
|
|
541
|
+
v-bind="toggleIconAttrs"
|
|
542
|
+
:tabindex="-1"
|
|
543
|
+
/>
|
|
544
|
+
</slot>
|
|
545
|
+
</div>
|
|
546
|
+
|
|
547
|
+
<div
|
|
548
|
+
v-if="isLocalValue && clearable && !disabled && !multiple"
|
|
549
|
+
v-bind="clearAttrs"
|
|
550
|
+
@mousedown="onMouseDownClear"
|
|
551
|
+
>
|
|
552
|
+
<!--
|
|
553
|
+
@slot Use it to add something instead of the clear icon.
|
|
554
|
+
@binding {string} icon-name
|
|
555
|
+
@binding {string} icon-size
|
|
556
|
+
-->
|
|
557
|
+
<slot name="clear" :icon-name="config.defaults.clearIcon" :icon-size="iconSize">
|
|
558
|
+
<UIcon
|
|
559
|
+
internal
|
|
560
|
+
interactive
|
|
561
|
+
color="gray"
|
|
562
|
+
:size="iconSize"
|
|
563
|
+
:name="config.defaults.clearIcon"
|
|
564
|
+
v-bind="clearIconAttrs"
|
|
565
|
+
/>
|
|
566
|
+
</slot>
|
|
567
|
+
</div>
|
|
568
|
+
|
|
569
|
+
<div
|
|
570
|
+
v-if="hasSlotContent($slots['before-caret']) && !(multiple && localValue?.length)"
|
|
571
|
+
v-bind="beforeCaretAttrs"
|
|
572
|
+
>
|
|
573
|
+
<!--
|
|
574
|
+
@slot Use it to add something before caret.
|
|
575
|
+
@binding {object} scope-props
|
|
576
|
+
-->
|
|
577
|
+
<slot :scope-props="props" name="before-caret" />
|
|
578
|
+
</div>
|
|
808
579
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
580
|
+
<div ref="innerWrapperRef" v-bind="innerWrapperAttrs">
|
|
581
|
+
<span
|
|
582
|
+
v-if="hasSlotContent($slots['left-icon']) || leftIcon"
|
|
583
|
+
ref="leftSlotWrapperRef"
|
|
584
|
+
v-bind="leftIconWrapperAttrs"
|
|
585
|
+
>
|
|
586
|
+
<!--
|
|
587
|
+
@slot Use it to add icon left.
|
|
588
|
+
@binding {string} icon-name
|
|
589
|
+
@binding {string} icon-size
|
|
590
|
+
-->
|
|
591
|
+
<slot name="left-icon" :icon-name="leftIcon" :icon-size="iconSize">
|
|
592
|
+
<UIcon
|
|
593
|
+
v-if="leftIcon"
|
|
594
|
+
:name="leftIcon"
|
|
595
|
+
:size="iconSize"
|
|
596
|
+
internal
|
|
597
|
+
v-bind="leftIconAttrs"
|
|
598
|
+
/>
|
|
599
|
+
</slot>
|
|
600
|
+
</span>
|
|
814
601
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
602
|
+
<span v-if="hasSlotContent($slots['left'])" ref="leftSlotWrapperRef">
|
|
603
|
+
<!-- @slot Use it to add something left. -->
|
|
604
|
+
<slot name="left" />
|
|
605
|
+
</span>
|
|
818
606
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
607
|
+
<div v-if="multiple && localValue?.length" v-bind="selectedLabelsAttrs">
|
|
608
|
+
<span
|
|
609
|
+
v-for="item in localValue as Option[]"
|
|
610
|
+
:key="String(item[valueKey])"
|
|
611
|
+
v-bind="selectedLabelAttrs"
|
|
612
|
+
>
|
|
613
|
+
<!--
|
|
614
|
+
@slot Use it to customise selected value label.
|
|
615
|
+
@binding {string} selected-label
|
|
616
|
+
-->
|
|
617
|
+
<slot
|
|
618
|
+
name="selected-label"
|
|
619
|
+
:selected-label="getOptionLabel(item)"
|
|
620
|
+
:value="item[valueKey]"
|
|
621
|
+
:raw-option="item"
|
|
622
|
+
>
|
|
623
|
+
{{ getOptionLabel(item) }}
|
|
624
|
+
</slot>
|
|
823
625
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
) {
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
626
|
+
<!--
|
|
627
|
+
@slot Use it to add something after selected value label.
|
|
628
|
+
@binding {object} scope-props
|
|
629
|
+
-->
|
|
630
|
+
<slot :scope-props="props" name="selected-label-after" />
|
|
832
631
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
632
|
+
<div
|
|
633
|
+
v-if="!disabled"
|
|
634
|
+
v-bind="clearMultipleAttrs"
|
|
635
|
+
@mousedown.prevent.capture
|
|
636
|
+
@click.prevent.capture
|
|
637
|
+
@mousedown="onMouseDownClearItem($event, item)"
|
|
638
|
+
>
|
|
639
|
+
<!--
|
|
640
|
+
@slot Use it to add something instead of the clear icon (when multiple prop enabled).
|
|
641
|
+
@binding {string} icon-name
|
|
642
|
+
@binding {string} icon-size
|
|
643
|
+
-->
|
|
644
|
+
<slot
|
|
645
|
+
name="clear-multiple"
|
|
646
|
+
:icon-name="config.defaults.clearMultipleIcon"
|
|
647
|
+
:icon-size="iconSize"
|
|
648
|
+
>
|
|
649
|
+
<UIcon
|
|
650
|
+
internal
|
|
651
|
+
interactive
|
|
652
|
+
color="gray"
|
|
653
|
+
:size="iconSize"
|
|
654
|
+
:name="config.defaults.clearMultipleIcon"
|
|
655
|
+
v-bind="clearMultipleIconAttrs"
|
|
656
|
+
/>
|
|
657
|
+
</slot>
|
|
658
|
+
</div>
|
|
659
|
+
</span>
|
|
660
|
+
</div>
|
|
837
661
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
662
|
+
<div v-bind="searchAttrs">
|
|
663
|
+
<input
|
|
664
|
+
v-show="searchable || !localValue || multiple || !isOpen"
|
|
665
|
+
:id="elementId"
|
|
666
|
+
ref="searchInputRef"
|
|
667
|
+
v-model="search"
|
|
668
|
+
type="text"
|
|
669
|
+
autocomplete="off"
|
|
670
|
+
:spellcheck="false"
|
|
671
|
+
:placeholder="inputPlaceholder"
|
|
672
|
+
:disabled="disabled"
|
|
673
|
+
:aria-controls="'listbox-' + elementId"
|
|
674
|
+
v-bind="searchInputAttrs"
|
|
675
|
+
@focus="activate"
|
|
676
|
+
@blur.prevent="deactivate"
|
|
677
|
+
@keyup.esc="deactivate"
|
|
678
|
+
@keydown.down.prevent="dropdownListRef?.pointerForward"
|
|
679
|
+
@keydown.up.prevent="dropdownListRef?.pointerBackward"
|
|
680
|
+
@keydown.enter.prevent.stop.self="dropdownListRef?.addPointerElement"
|
|
681
|
+
/>
|
|
682
|
+
</div>
|
|
845
683
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
684
|
+
<span
|
|
685
|
+
v-if="isSelectedValueLabelVisible"
|
|
686
|
+
v-bind="selectedLabelAttrs"
|
|
687
|
+
@mousedown.prevent="toggle"
|
|
688
|
+
>
|
|
689
|
+
<!--
|
|
690
|
+
@slot Use it to add selected value label.
|
|
691
|
+
@binding {string} selected-label
|
|
692
|
+
@binding {string} value
|
|
693
|
+
-->
|
|
694
|
+
<slot
|
|
695
|
+
name="selected-label"
|
|
696
|
+
:selected-label="selectedLabel"
|
|
697
|
+
:value="(localValue as Option)[valueKey]"
|
|
698
|
+
>
|
|
699
|
+
{{ selectedLabel }}
|
|
700
|
+
</slot>
|
|
852
701
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
702
|
+
<!--
|
|
703
|
+
@slot Use it to add something after selected value label.
|
|
704
|
+
@binding {object} scope-props
|
|
705
|
+
-->
|
|
706
|
+
<slot :scope-props="props" name="selected-label-after" />
|
|
707
|
+
</span>
|
|
858
708
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
709
|
+
<div
|
|
710
|
+
v-if="isLocalValue && clearable && !disabled && multiple"
|
|
711
|
+
v-bind="clearMultipleTextAttrs"
|
|
712
|
+
@mousedown.prevent.capture="onMouseDownClear"
|
|
713
|
+
@click.prevent.capture
|
|
714
|
+
v-text="currentLocale.clear"
|
|
715
|
+
/>
|
|
716
|
+
</div>
|
|
864
717
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
718
|
+
<UDropdownList
|
|
719
|
+
v-if="isOpen"
|
|
720
|
+
ref="dropdownListRef"
|
|
721
|
+
v-model="dropdownValue as string | number"
|
|
722
|
+
:options="filteredOptions"
|
|
723
|
+
:disabled="disabled"
|
|
724
|
+
:size="size"
|
|
725
|
+
:visible-options="visibleOptions"
|
|
726
|
+
:value-key="valueKey"
|
|
727
|
+
:label-key="labelKey"
|
|
728
|
+
:add-option="addOption"
|
|
729
|
+
tabindex="-1"
|
|
730
|
+
v-bind="dropdownListAttrs as KeyAttrsWithConfig<UDropdownListConfig>"
|
|
731
|
+
@add="onAddOption"
|
|
732
|
+
@focus="activate"
|
|
733
|
+
@mousedown.prevent.capture
|
|
734
|
+
@click.prevent.capture
|
|
735
|
+
>
|
|
736
|
+
<template #before-option="{ option, index }">
|
|
737
|
+
<!--
|
|
738
|
+
@slot Use it to add something before option.
|
|
739
|
+
@binding {object} option
|
|
740
|
+
@binding {number} index
|
|
741
|
+
-->
|
|
742
|
+
<slot name="before-option" :option="option" :index="index" />
|
|
743
|
+
</template>
|
|
870
744
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
745
|
+
<template #option="{ option, index }">
|
|
746
|
+
<!--
|
|
747
|
+
@slot Use it to customise the option.
|
|
748
|
+
@binding {object} option
|
|
749
|
+
@binding {number} index
|
|
750
|
+
-->
|
|
751
|
+
<slot name="option" :option="option" :index="index" />
|
|
752
|
+
</template>
|
|
876
753
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
754
|
+
<template #after-option="{ option, index }">
|
|
755
|
+
<!--
|
|
756
|
+
@slot Use it to add something after option.
|
|
757
|
+
@binding {object} option
|
|
758
|
+
@binding {number} index
|
|
759
|
+
-->
|
|
760
|
+
<slot name="after-option" :option="option" :index="index" />
|
|
761
|
+
</template>
|
|
883
762
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
const mutatedProps = computed(() => ({
|
|
889
|
-
error: Boolean(props.error),
|
|
890
|
-
label: Boolean(props.label),
|
|
891
|
-
/* component state, not a props */
|
|
892
|
-
selected: Boolean(selectedLabel.value),
|
|
893
|
-
opened: isOpen.value,
|
|
894
|
-
openedTop: isTop.value,
|
|
895
|
-
}));
|
|
763
|
+
<template #empty>
|
|
764
|
+
<template v-if="isEmpty">
|
|
765
|
+
{{ currentLocale.listIsEmpty }}
|
|
766
|
+
</template>
|
|
896
767
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
leftIconAttrs,
|
|
906
|
-
rightIconAttrs,
|
|
907
|
-
beforeCaretAttrs,
|
|
908
|
-
afterCaretAttrs,
|
|
909
|
-
toggleAttrs,
|
|
910
|
-
clearAttrs,
|
|
911
|
-
clearMultipleTextAttrs,
|
|
912
|
-
clearMultipleAttrs,
|
|
913
|
-
searchAttrs,
|
|
914
|
-
searchInputAttrs,
|
|
915
|
-
selectedLabelsAttrs,
|
|
916
|
-
selectedLabelAttrs,
|
|
917
|
-
dropdownListAttrs,
|
|
918
|
-
toggleIconAttrs,
|
|
919
|
-
clearIconAttrs,
|
|
920
|
-
clearMultipleIconAttrs,
|
|
921
|
-
} = useUI(defaultConfig, mutatedProps);
|
|
922
|
-
</script>
|
|
768
|
+
<template v-else>
|
|
769
|
+
{{ currentLocale.noDataToShow }}
|
|
770
|
+
</template>
|
|
771
|
+
</template>
|
|
772
|
+
</UDropdownList>
|
|
773
|
+
</div>
|
|
774
|
+
</ULabel>
|
|
775
|
+
</template>
|