vueless 1.0.1 → 1.0.2-beta.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/package.json
CHANGED
package/ui.form-input/UInput.vue
CHANGED
|
@@ -72,6 +72,11 @@ const emit = defineEmits([
|
|
|
72
72
|
* Triggers when content pasted to the input.
|
|
73
73
|
*/
|
|
74
74
|
"paste",
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Triggers when a key is pressed down while the input is focused.
|
|
78
|
+
*/
|
|
79
|
+
"keydown",
|
|
75
80
|
]);
|
|
76
81
|
|
|
77
82
|
const VALIDATION_RULES_REG_EX = {
|
|
@@ -164,6 +169,10 @@ function onCopy(event: ClipboardEvent) {
|
|
|
164
169
|
emit("copy", event);
|
|
165
170
|
}
|
|
166
171
|
|
|
172
|
+
function onKeydown(event: KeyboardEvent) {
|
|
173
|
+
emit("keydown", event);
|
|
174
|
+
}
|
|
175
|
+
|
|
167
176
|
/**
|
|
168
177
|
* This trick prevents default browser autocomplete behavior.
|
|
169
178
|
* @param toggleState { boolean }
|
|
@@ -295,6 +304,7 @@ const {
|
|
|
295
304
|
@click="onClick"
|
|
296
305
|
@paste="onPaste"
|
|
297
306
|
@copy="onCopy"
|
|
307
|
+
@keydown="onKeydown"
|
|
298
308
|
/>
|
|
299
309
|
|
|
300
310
|
<div
|
|
@@ -51,6 +51,11 @@ const emit = defineEmits([
|
|
|
51
51
|
* Triggers when the search input value changes.
|
|
52
52
|
*/
|
|
53
53
|
"searchChange",
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Triggers when the search input loses focus.
|
|
57
|
+
*/
|
|
58
|
+
"searchBlur",
|
|
54
59
|
]);
|
|
55
60
|
|
|
56
61
|
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
|
|
@@ -220,6 +225,14 @@ function onSearchChange(value: string) {
|
|
|
220
225
|
emit("searchChange", value);
|
|
221
226
|
}
|
|
222
227
|
|
|
228
|
+
function onKeydownUp() {
|
|
229
|
+
wrapperRef.value?.focus();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function onKeydownDown() {
|
|
233
|
+
wrapperRef.value?.focus();
|
|
234
|
+
}
|
|
235
|
+
|
|
223
236
|
function isMetaKey(key: string) {
|
|
224
237
|
return ["isSubGroup", "groupLabel", "level", "isHidden", "onClick", "divider"].includes(key);
|
|
225
238
|
}
|
|
@@ -307,6 +320,12 @@ function onClickOption(rawOption: Option) {
|
|
|
307
320
|
emit("clickOption", option);
|
|
308
321
|
}
|
|
309
322
|
|
|
323
|
+
function onInputSearchBlur(event: FocusEvent) {
|
|
324
|
+
if (props.searchable) {
|
|
325
|
+
emit("searchBlur", event);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
310
329
|
defineExpose({
|
|
311
330
|
/**
|
|
312
331
|
* Allows setting the pointer to a specific index.
|
|
@@ -408,6 +427,9 @@ const {
|
|
|
408
427
|
:debounce="debounce"
|
|
409
428
|
v-bind="listboxInputAttrs"
|
|
410
429
|
:data-test="getDataTest('search')"
|
|
430
|
+
@blur="onInputSearchBlur"
|
|
431
|
+
@keydown.self.down.prevent="onKeydownDown"
|
|
432
|
+
@keydown.self.up.prevent="onKeydownUp"
|
|
411
433
|
@update:model-value="onSearchChange"
|
|
412
434
|
/>
|
|
413
435
|
</div>
|
|
@@ -7,8 +7,6 @@ import UListbox from "../ui.form-listbox/UListbox.vue";
|
|
|
7
7
|
import UBadge from "../ui.text-badge/UBadge.vue";
|
|
8
8
|
import ULink from "../ui.button-link/ULink.vue";
|
|
9
9
|
|
|
10
|
-
import { vClickOutside } from "../directives";
|
|
11
|
-
|
|
12
10
|
import useUI from "../composables/useUI.ts";
|
|
13
11
|
import { hasSlotContent } from "../utils/helper.ts";
|
|
14
12
|
import { getDefaults } from "../utils/ui.ts";
|
|
@@ -198,10 +196,6 @@ const toggleIconName = computed(() => {
|
|
|
198
196
|
return props.toggleIcon ? config.value.defaults.toggleIcon : "";
|
|
199
197
|
});
|
|
200
198
|
|
|
201
|
-
const clickOutsideOptions = computed(() => ({
|
|
202
|
-
ignore: [labelComponentRef.value?.wrapperElement, labelComponentRef.value?.labelElement],
|
|
203
|
-
}));
|
|
204
|
-
|
|
205
199
|
watch(localValue, setLabelPosition, { deep: true });
|
|
206
200
|
|
|
207
201
|
onMounted(() => {
|
|
@@ -216,16 +210,6 @@ function onSearchChange(query: string) {
|
|
|
216
210
|
emit("searchChange", query);
|
|
217
211
|
}
|
|
218
212
|
|
|
219
|
-
function onListboxInteraction(event: MouseEvent) {
|
|
220
|
-
const target = event.target as HTMLElement;
|
|
221
|
-
|
|
222
|
-
if (target.closest("input")) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
event.preventDefault();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
213
|
function onKeydownAddOption(event: KeyboardEvent) {
|
|
230
214
|
if (!isOpen.value) return;
|
|
231
215
|
|
|
@@ -257,7 +241,7 @@ function deactivate() {
|
|
|
257
241
|
return;
|
|
258
242
|
}
|
|
259
243
|
|
|
260
|
-
|
|
244
|
+
wrapperRef.value?.blur();
|
|
261
245
|
|
|
262
246
|
isOpen.value = false;
|
|
263
247
|
|
|
@@ -283,9 +267,9 @@ function activate() {
|
|
|
283
267
|
}
|
|
284
268
|
|
|
285
269
|
function adjustPosition() {
|
|
286
|
-
if (
|
|
270
|
+
if (!wrapperRef.value) return;
|
|
287
271
|
|
|
288
|
-
const dropdownHeight = listboxRef.value
|
|
272
|
+
const dropdownHeight = listboxRef.value?.wrapperRef?.getBoundingClientRect().height || 0;
|
|
289
273
|
const spaceAbove = wrapperRef.value.getBoundingClientRect().top;
|
|
290
274
|
const spaceBelow = window.innerHeight - wrapperRef.value.getBoundingClientRect().bottom;
|
|
291
275
|
const hasEnoughSpaceBelow = spaceBelow > dropdownHeight;
|
|
@@ -297,7 +281,7 @@ function adjustPosition() {
|
|
|
297
281
|
}
|
|
298
282
|
}
|
|
299
283
|
|
|
300
|
-
function
|
|
284
|
+
function deactivateOnBlur(event: FocusEvent) {
|
|
301
285
|
const related = event.relatedTarget as HTMLElement | null;
|
|
302
286
|
|
|
303
287
|
const isInsideWrapper = related && wrapperRef.value?.contains(related);
|
|
@@ -312,7 +296,19 @@ function onWrapperBlur(event: FocusEvent) {
|
|
|
312
296
|
deactivate();
|
|
313
297
|
}
|
|
314
298
|
|
|
315
|
-
function
|
|
299
|
+
function onBlur(event: FocusEvent) {
|
|
300
|
+
deactivateOnBlur(event);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function onListboxBlur(event: FocusEvent) {
|
|
304
|
+
deactivateOnBlur(event);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function onListboxSearchBlur(event: FocusEvent) {
|
|
308
|
+
deactivateOnBlur(event);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function onClickClearItem(event: MouseEvent, option: Option) {
|
|
316
312
|
if (props.disabled) return;
|
|
317
313
|
|
|
318
314
|
const value = Array.isArray(props.modelValue)
|
|
@@ -325,25 +321,23 @@ function onMouseDownClearItem(event: MouseEvent, option: Option) {
|
|
|
325
321
|
})
|
|
326
322
|
: [];
|
|
327
323
|
|
|
324
|
+
if (isOpen.value) wrapperRef.value?.focus();
|
|
325
|
+
|
|
328
326
|
emit("update:modelValue", value);
|
|
329
327
|
emit("change", { value, options: props.options });
|
|
330
328
|
emit("remove", option);
|
|
331
329
|
}
|
|
332
330
|
|
|
333
|
-
function
|
|
331
|
+
function onClickClear() {
|
|
334
332
|
if (props.disabled) return;
|
|
335
333
|
|
|
336
|
-
if (!props.clearable && !props.multiple) {
|
|
337
|
-
deactivate();
|
|
338
|
-
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
334
|
const value = props.multiple ? [] : "";
|
|
343
335
|
|
|
344
336
|
emit("update:modelValue", value);
|
|
345
337
|
emit("change", { value, options: props.options });
|
|
346
338
|
emit("remove", props.options);
|
|
339
|
+
|
|
340
|
+
deactivate();
|
|
347
341
|
}
|
|
348
342
|
|
|
349
343
|
useMutationObserver(leftSlotWrapperRef, (mutations) => mutations.forEach(setLabelPosition), {
|
|
@@ -475,25 +469,27 @@ const {
|
|
|
475
469
|
v-bind="selectLabelAttrs"
|
|
476
470
|
:data-test="getDataTest()"
|
|
477
471
|
:tabindex="-1"
|
|
478
|
-
@click="toggle"
|
|
479
472
|
>
|
|
480
473
|
<template #label>
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
474
|
+
<div @click="toggle" @mousedown.prevent>
|
|
475
|
+
<!--
|
|
476
|
+
@slot Use this to add custom content instead of the label.
|
|
477
|
+
@binding {string} label
|
|
478
|
+
-->
|
|
479
|
+
<slot name="label" :label="label">
|
|
480
|
+
{{ label }}
|
|
481
|
+
</slot>
|
|
482
|
+
</div>
|
|
486
483
|
</template>
|
|
487
484
|
|
|
488
485
|
<div
|
|
489
486
|
ref="wrapper"
|
|
490
|
-
v-click-outside="[deactivate, clickOutsideOptions]"
|
|
491
487
|
:tabindex="searchable || disabled ? -1 : 0"
|
|
492
488
|
role="combobox"
|
|
493
489
|
:aria-owns="'listbox-' + elementId"
|
|
494
490
|
v-bind="wrapperAttrs"
|
|
495
491
|
@focus="activate"
|
|
496
|
-
@blur="
|
|
492
|
+
@blur="onBlur"
|
|
497
493
|
@keydown.self.down.prevent="listboxRef?.pointerForward"
|
|
498
494
|
@keydown.self.up.prevent="listboxRef?.pointerBackward"
|
|
499
495
|
@keydown.enter.tab.stop.self="listboxRef?.addPointerElement()"
|
|
@@ -526,7 +522,7 @@ const {
|
|
|
526
522
|
v-bind="toggleWrapperAttrs"
|
|
527
523
|
:tabindex="-1"
|
|
528
524
|
:data-test="getDataTest('toggle')"
|
|
529
|
-
@
|
|
525
|
+
@click.stop="toggle"
|
|
530
526
|
>
|
|
531
527
|
<!--
|
|
532
528
|
@slot Use it to add something instead of the toggle icon.
|
|
@@ -546,23 +542,22 @@ const {
|
|
|
546
542
|
</slot>
|
|
547
543
|
</div>
|
|
548
544
|
|
|
549
|
-
<div
|
|
550
|
-
v-if="!isMultipleListVariant && isLocalValue && clearable"
|
|
551
|
-
v-bind="clearAttrs"
|
|
552
|
-
:data-test="getDataTest('clear')"
|
|
553
|
-
@mousedown="onMouseDownClear"
|
|
554
|
-
>
|
|
545
|
+
<div v-if="!isMultipleListVariant && isLocalValue && clearable" v-bind="clearAttrs">
|
|
555
546
|
<!--
|
|
556
547
|
@slot Use it to add something instead of the clear icon.
|
|
557
548
|
@binding {string} icon-name
|
|
549
|
+
@binding {function} clear
|
|
550
|
+
@binding {string} data-test
|
|
558
551
|
-->
|
|
559
|
-
<slot name="clear" :icon-name="config.defaults.clearIcon">
|
|
552
|
+
<slot name="clear" :icon-name="config.defaults.clearIcon" :clear="onClickClear">
|
|
560
553
|
<UIcon
|
|
561
554
|
interactive
|
|
562
555
|
color="neutral"
|
|
563
556
|
:disabled="disabled"
|
|
564
557
|
:name="config.defaults.clearIcon"
|
|
565
558
|
v-bind="clearIconAttrs"
|
|
559
|
+
:data-test="getDataTest('clear')"
|
|
560
|
+
@click.stop="onClickClear"
|
|
566
561
|
/>
|
|
567
562
|
</slot>
|
|
568
563
|
</div>
|
|
@@ -588,7 +583,7 @@ const {
|
|
|
588
583
|
@binding {object} options
|
|
589
584
|
-->
|
|
590
585
|
<slot name="selected-options" :options="multiple ? selectedOptions.full : selectedOption">
|
|
591
|
-
<span v-if="!multiple" v-bind="selectedLabelsAttrs" @
|
|
586
|
+
<span v-if="!multiple" v-bind="selectedLabelsAttrs" @click.stop="toggle">
|
|
592
587
|
<!--
|
|
593
588
|
@slot Use it to customize selected option.
|
|
594
589
|
@binding {string} label
|
|
@@ -669,7 +664,7 @@ const {
|
|
|
669
664
|
:size="size"
|
|
670
665
|
variant="subtle"
|
|
671
666
|
v-bind="badgeLabelAttrs"
|
|
672
|
-
@click="toggle"
|
|
667
|
+
@click.stop="toggle"
|
|
673
668
|
>
|
|
674
669
|
<div v-bind="selectedLabelTextAttrs">
|
|
675
670
|
{{ option[labelKey] }}
|
|
@@ -682,7 +677,7 @@ const {
|
|
|
682
677
|
:disabled="disabled"
|
|
683
678
|
:name="config.defaults.badgeClearIcon"
|
|
684
679
|
v-bind="badgeClearIconAttrs"
|
|
685
|
-
@click="
|
|
680
|
+
@click.stop="onClickClearItem($event, option)"
|
|
686
681
|
/>
|
|
687
682
|
</template>
|
|
688
683
|
</UBadge>
|
|
@@ -735,9 +730,7 @@ const {
|
|
|
735
730
|
:name="config.defaults.listClearIcon"
|
|
736
731
|
:data-test="getDataTest('clear-item')"
|
|
737
732
|
v-bind="listClearIconAttrs"
|
|
738
|
-
@
|
|
739
|
-
@click.prevent.capture
|
|
740
|
-
@mousedown="onMouseDownClearItem($event, option)"
|
|
733
|
+
@click.stop="onClickClearItem($event, option)"
|
|
741
734
|
/>
|
|
742
735
|
</slot>
|
|
743
736
|
</div>
|
|
@@ -767,8 +760,7 @@ const {
|
|
|
767
760
|
:underlined="false"
|
|
768
761
|
v-bind="listClearAllAttrs"
|
|
769
762
|
:data-test="getDataTest('clear-all')"
|
|
770
|
-
@
|
|
771
|
-
@click.prevent.capture
|
|
763
|
+
@click.stop="onClickClear"
|
|
772
764
|
/>
|
|
773
765
|
</div>
|
|
774
766
|
</template>
|
|
@@ -788,17 +780,19 @@ const {
|
|
|
788
780
|
:size="size"
|
|
789
781
|
:debounce="debounce"
|
|
790
782
|
:visible-options="visibleOptions"
|
|
791
|
-
:value-key="valueKey"
|
|
792
783
|
:label-key="labelKey"
|
|
784
|
+
:value-key="valueKey"
|
|
785
|
+
:group-label-key="groupLabelKey"
|
|
786
|
+
:group-value-key="groupValueKey"
|
|
793
787
|
:add-option="addOption"
|
|
794
788
|
tabindex="-1"
|
|
795
789
|
v-bind="listboxAttrs as KeyAttrsWithConfig<UListboxConfig>"
|
|
796
790
|
:data-test="getDataTest()"
|
|
797
791
|
@add="onAddOption"
|
|
798
792
|
@focus="activate"
|
|
793
|
+
@blur="onListboxBlur"
|
|
794
|
+
@search-blur="onListboxSearchBlur"
|
|
799
795
|
@update:model-value="onSearchChange"
|
|
800
|
-
@mousedown.capture="onListboxInteraction"
|
|
801
|
-
@click.capture="onListboxInteraction"
|
|
802
796
|
>
|
|
803
797
|
<template #before-option="{ option, index }">
|
|
804
798
|
<!--
|
|
@@ -334,7 +334,7 @@ export const Slots: StoryFn<USelectArgs> = (args) => ({
|
|
|
334
334
|
</template>
|
|
335
335
|
</USelect>
|
|
336
336
|
|
|
337
|
-
<URow>
|
|
337
|
+
<URow block>
|
|
338
338
|
<USelect v-bind="args" v-model="args.beforeToggleModel" label="Slot before-toggle">
|
|
339
339
|
<template #before-toggle>
|
|
340
340
|
<UAvatar />
|
|
@@ -353,7 +353,7 @@ export const Slots: StoryFn<USelectArgs> = (args) => ({
|
|
|
353
353
|
</USelect>
|
|
354
354
|
</URow>
|
|
355
355
|
|
|
356
|
-
<URow>
|
|
356
|
+
<URow block>
|
|
357
357
|
<USelect v-bind="args" v-model="args.leftModel" label="Slot left">
|
|
358
358
|
<template #left>
|
|
359
359
|
<UAvatar />
|