@veritree/ui 0.19.2 → 0.20.0-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.
Files changed (78) hide show
  1. package/index.js +64 -68
  2. package/mixins/floating-ui-content.js +81 -0
  3. package/mixins/floating-ui-item.js +266 -0
  4. package/mixins/floating-ui.js +67 -0
  5. package/mixins/form-control-icon.js +53 -0
  6. package/mixins/form-control.js +71 -0
  7. package/nuxt.js +30 -23
  8. package/package.json +9 -4
  9. package/src/components/Alert/VTAlert.vue +55 -14
  10. package/src/components/Avatar/VTAvatar.vue +32 -29
  11. package/src/components/Button/VTButton.vue +9 -6
  12. package/src/components/Checkbox/VTCheckbox.vue +134 -0
  13. package/src/components/Checkbox/VTCheckboxLabel.vue +3 -0
  14. package/src/components/Checkbox/VTCheckboxText.vue +20 -0
  15. package/src/components/Dialog/VTDialog.vue +22 -32
  16. package/src/components/Dialog/VTDialogClose.vue +19 -25
  17. package/src/components/Dialog/VTDialogContent.vue +24 -19
  18. package/src/components/Dialog/VTDialogFooter.vue +11 -16
  19. package/src/components/Dialog/VTDialogHeader.vue +16 -18
  20. package/src/components/Dialog/VTDialogMain.vue +11 -18
  21. package/src/components/Dialog/VTDialogOverlay.vue +14 -18
  22. package/src/components/Dialog/VTDialogTitle.vue +10 -7
  23. package/src/components/Disclosure/VTDisclosureContent.vue +1 -1
  24. package/src/components/Disclosure/VTDisclosureDetails.vue +1 -1
  25. package/src/components/Disclosure/VTDisclosureHeader.vue +2 -2
  26. package/src/components/Disclosure/VTDisclosureIcon.vue +1 -1
  27. package/src/components/Drawer/VTDrawer.vue +14 -16
  28. package/src/components/Drawer/VTDrawerClose.vue +9 -9
  29. package/src/components/Drawer/VTDrawerContent.vue +8 -8
  30. package/src/components/Drawer/VTDrawerFooter.vue +3 -3
  31. package/src/components/Drawer/VTDrawerHeader.vue +4 -4
  32. package/src/components/Drawer/VTDrawerMain.vue +5 -5
  33. package/src/components/Drawer/VTDrawerOverlay.vue +6 -6
  34. package/src/components/Drawer/VTDrawerTitle.vue +5 -5
  35. package/src/components/DropdownMenu/VTDropdownMenu.vue +45 -28
  36. package/src/components/DropdownMenu/VTDropdownMenuContent.vue +29 -64
  37. package/src/components/DropdownMenu/VTDropdownMenuDivider.vue +8 -14
  38. package/src/components/DropdownMenu/VTDropdownMenuItem.vue +11 -124
  39. package/src/components/DropdownMenu/VTDropdownMenuLabel.vue +3 -12
  40. package/src/components/DropdownMenu/VTDropdownMenuTrigger.vue +91 -121
  41. package/src/components/Form/VTFormFeedback.vue +39 -22
  42. package/src/components/Form/VTFormGroup.vue +5 -7
  43. package/src/components/Form/VTFormLabel.vue +22 -0
  44. package/src/components/Form/VTFormRow.vue +5 -0
  45. package/src/components/Form/VTInput.vue +40 -0
  46. package/src/components/Form/VTInputIcon.vue +35 -0
  47. package/src/components/Form/VTInputPassword.vue +55 -0
  48. package/src/components/Form/VTTextarea.vue +22 -0
  49. package/src/components/Listbox/VTListbox.vue +122 -50
  50. package/src/components/Listbox/VTListboxContent.vue +20 -116
  51. package/src/components/Listbox/VTListboxItem.vue +115 -166
  52. package/src/components/Listbox/VTListboxLabel.vue +3 -14
  53. package/src/components/Listbox/VTListboxList.vue +10 -40
  54. package/src/components/Listbox/VTListboxSearch.vue +76 -68
  55. package/src/components/Listbox/VTListboxTrigger.vue +75 -86
  56. package/src/components/Popover/VTPopover.vue +42 -29
  57. package/src/components/Popover/VTPopoverContent.vue +24 -59
  58. package/src/components/Popover/VTPopoverDivider.vue +4 -11
  59. package/src/components/Popover/VTPopoverItem.vue +21 -14
  60. package/src/components/Popover/VTPopoverTrigger.vue +126 -21
  61. package/src/components/ProgressBar/VTProgressBar.vue +21 -3
  62. package/src/components/Skeleton/VTSkeleton.vue +11 -0
  63. package/src/components/Skeleton/VTSkeletonItem.vue +9 -0
  64. package/src/components/Tabs/VTTab.vue +4 -3
  65. package/src/components/Tabs/VTTabGroup.vue +9 -7
  66. package/src/components/Tabs/VTTabPanel.vue +4 -5
  67. package/src/components/Tooltip/VTTooltip.vue +65 -0
  68. package/src/components/Tooltip/VTTooltipContent.vue +59 -0
  69. package/src/components/Tooltip/VTTooltipTrigger.vue +98 -0
  70. package/src/components/Transitions/FadeInOut.vue +2 -2
  71. package/src/components/Utils/FloatingUi.vue +93 -0
  72. package/package-lock.json +0 -13
  73. package/src/components/Input/VTInput.vue +0 -82
  74. package/src/components/Input/VTInputDate.vue +0 -36
  75. package/src/components/Input/VTInputFile.vue +0 -60
  76. package/src/components/Input/VTInputUpload.vue +0 -54
  77. package/src/components/Modal/VTModal.vue +0 -69
  78. package/src/utils/genId.js +0 -13
@@ -1,51 +1,54 @@
1
1
  <template>
2
2
  <li
3
3
  :id="id"
4
- :class="{
5
- ListboxItem: headless,
6
- 'relative z-10 flex items-center gap-2 px-3 py-2 no-underline': !headless,
7
- 'hover:bg-secondary-200/10': !dark && !headless,
8
- 'text-white': dark && !headless,
9
- 'pointer-events-none opacity-75': disabled && !headless,
10
- 'bg-secondary-200/10': selected && !headless,
11
- }"
4
+ :class="classComputed"
12
5
  :aria-disabled="disabled"
13
6
  :aria-selected="String(selected)"
14
7
  :tabindex="tabIndex"
15
- role="option"
8
+ :role="'option'"
16
9
  @click.stop="onClick"
17
- @keydown.down.prevent="focusPreviousItem"
18
- @keydown.up.prevent="focusNextItem"
10
+ @keydown.prevent="onKeydown"
11
+ @keydown.down.prevent="focusNextItem"
12
+ @keydown.up.prevent="focusPreviousItem"
19
13
  @keydown.home.prevent="focusFirstItem"
20
14
  @keydown.end.prevent="focusLastItem"
21
15
  @keydown.esc.stop="onKeyEsc"
22
16
  @keydown.enter.prevent="onClick"
23
- @mousemove="onMousemove"
24
- @mouseout="onMouseleave"
25
17
  @keydown.tab.prevent
26
18
  >
27
- <span class="truncate"><slot></slot></span>
19
+ <span v-if="apiListbox().multiple">
20
+ <input
21
+ v-model="apiListbox().$mutable.valueComputed"
22
+ :id="`${id}-checkbox`"
23
+ :value="value"
24
+ type="checkbox"
25
+ @click.stop
26
+ @change="onChange"
27
+ />
28
+ </span>
29
+ <slot></slot>
30
+ <span v-if="wasSelected" class="ml-auto">
31
+ <IconCheckOutline class="text-secondary-200 h-5 w-5" />
32
+ </span>
28
33
  </li>
29
34
  </template>
30
35
 
31
36
  <script>
32
- import { scrollElementIntoView } from "../../utils/components";
33
- import { genId } from "../../utils/ids";
37
+ import { IconCheckOutline } from '@veritree/icons';
38
+ import { floatingUiItemMixin } from '../../../mixins/floating-ui-item';
39
+
40
+ let keydownSearchTimeout = null;
34
41
 
35
42
  export default {
36
- name: "VTListboxItem",
43
+ name: 'VTListboxItem',
44
+
45
+ mixins: [floatingUiItemMixin],
37
46
 
38
- inject: ["api"],
47
+ components: { IconCheckOutline },
48
+
49
+ inject: ['apiListbox'],
39
50
 
40
51
  props: {
41
- headless: {
42
- type: Boolean,
43
- default: false,
44
- },
45
- disabled: {
46
- type: Boolean,
47
- default: false,
48
- },
49
52
  value: {
50
53
  type: [String, Number, Object, Array],
51
54
  required: true,
@@ -54,172 +57,118 @@ export default {
54
57
 
55
58
  data() {
56
59
  return {
57
- id: `listboxitem-${genId()}`,
58
- index: null,
59
- selected: false,
60
- tabIndex: 0,
60
+ // apiInjected is used in the mixin floating-ui-item
61
+ apiInjected: this.apiListbox,
62
+ componentName: 'listbox-item',
61
63
  };
62
64
  },
63
65
 
64
66
  computed: {
65
- dark() {
66
- return this.api().isDark;
67
- },
68
-
69
- items() {
70
- return this.api().items;
71
- },
72
-
73
- el() {
74
- return this.$el;
75
- },
76
-
77
- trigger() {
78
- return this.api().trigger;
79
- },
80
-
81
- content() {
82
- return this.api().content;
67
+ componentContent() {
68
+ return this.apiInjected().componentContent;
83
69
  },
84
70
 
85
- list() {
86
- return this.api().list;
71
+ hasComponentSearch() {
72
+ return this.apiListbox().componentSearch !== null;
87
73
  },
88
74
 
89
75
  search() {
90
- return this.api().search;
76
+ return this.apiListbox().search;
91
77
  },
92
- },
93
78
 
94
- watch: {
95
- selected(newValue) {
96
- if (!newValue || !this.list) return;
97
-
98
- if (this.content) {
99
- this.content.setActiveDescedant(this.id);
100
- }
101
-
102
- const isMousemove = this.list.getMousemove();
103
-
104
- if (!isMousemove) {
105
- scrollElementIntoView(this.el, this.list.el);
106
- }
79
+ /**
80
+ * The difference between selected (in the mixin) and
81
+ * wasSelected is that selected is more related to
82
+ * the aria-selected while wasSelected is the
83
+ * real selected option for the listbox
84
+ */
85
+ wasSelected() {
86
+ return (
87
+ JSON.stringify(this.value) ===
88
+ JSON.stringify(this.apiListbox().$mutable.valueComputed)
89
+ );
107
90
  },
108
91
  },
109
92
 
110
93
  mounted() {
111
- const item = {
112
- id: this.id,
113
- focus: this.focus,
114
- select: this.select,
115
- unselect: this.unselect,
116
- onClick: this.onClick,
117
- };
118
-
119
- this.api().registerItem(item);
120
-
121
- this.index = this.items.length - 1;
122
- },
123
-
124
- beforeDestroy() {
125
- this.api().unregisterItem(this.id);
94
+ if (this.wasSelected) {
95
+ /**
96
+ * There are some conflicts between the portal and the interaction
97
+ * with the element. It's unclear why it happens but if no delay,
98
+ * the floating ui content, that is placed in the body by the
99
+ * vue portal plugin, will not appear close to the trigger.
100
+ *
101
+ * Kind hacky but no time do find real solution.
102
+ */
103
+ setTimeout(() => {
104
+ this.hasComponentSearch ? this.select() : this.focus();
105
+ }, 1);
106
+ }
126
107
  },
127
108
 
128
109
  methods: {
129
- select() {
130
- this.selected = true;
131
- },
132
-
133
- unselect() {
134
- this.selected = false;
135
- },
136
-
137
- focus() {
138
- if (!this.el) return;
139
-
140
- this.tabIndex = -1;
141
- this.selected = true;
142
- this.el.focus();
143
- },
144
-
145
- focusFirstItem() {
146
- this.setFocusToItem(0);
147
- },
148
-
149
- focusLastItem() {
150
- this.setFocusToItem(this.items.length - 1);
151
- },
152
-
153
- /**
154
- * Focus the previous item in the menu.
155
- * If is the first item, jump to the last item.
156
- */
157
- focusPreviousItem() {
158
- const isLast = this.index === this.items.length - 1;
159
- const goToIndex = isLast ? 0 : this.index + 1;
160
-
161
- this.setFocusToItem(goToIndex);
162
- },
163
-
164
- /**
165
- * Focus the next item in the menu.
166
- * If is the last item, jump to the first item.
167
- */
168
- focusNextItem() {
169
- const isFirst = this.index === 0;
170
- const goToIndex = isFirst ? this.items.length - 1 : this.index - 1;
171
-
172
- this.setFocusToItem(goToIndex);
173
- },
174
-
175
110
  /**
176
- * Focus item by remove its tabindex and calling
177
- * focus to the element.
111
+ * This event is mainly for the search since other keydown
112
+ * events are handled with vue v-on (@) directive
178
113
  *
179
- * @param {Number, String} goToIndex
114
+ * @param {Object} event
180
115
  */
181
- setFocusToItem(goToIndex) {
182
- this.tabIndex = 0;
183
- this.selected = false;
184
-
185
- const isMousemove = this.list.getMousemove();
186
-
187
- if (isMousemove) {
188
- this.list.unsetMousemove();
189
- this.items.forEach((item) => item.unselect());
116
+ onKeydown(e) {
117
+ const isLetter = e.code.includes('Key');
118
+ const isNumber = e.code.includes('Numpad') || e.code.includes('Digit');
119
+ const isBackspace = e.code === 'Backspace';
120
+ const isSpace = e.code === 'Space';
121
+
122
+ if (isLetter || isNumber || isBackspace || isSpace) {
123
+ this.doSearch(e);
190
124
  }
191
-
192
- this.items[goToIndex].focus();
193
125
  },
194
126
 
195
127
  /**
196
- * Hides content/menu and focus on trigger
128
+ * Searches and focus the options that matches the
129
+ * value typed when the item is selected.
130
+ *
131
+ * @param {Object} event
197
132
  */
198
- leaveMenu() {
199
- if (this.content) this.content.hide();
200
- },
201
-
202
- onKeyEsc() {
203
- this.leaveMenu();
204
- },
205
-
206
- onClick() {
207
- if (this.disabled) return;
208
-
209
- this.api().emit(this.value);
210
- this.$nextTick(() => this.leaveMenu());
211
- },
212
-
213
- onMousemove() {
214
- this.items.forEach((item) => item.unselect());
215
-
216
- this.select();
217
- this.list.setMousemove();
218
- },
219
-
220
- onMouseleave() {
221
- this.unselect();
222
- this.list.unsetMousemove();
133
+ doSearch(e) {
134
+ clearTimeout(keydownSearchTimeout);
135
+
136
+ /**
137
+ * Add letter or number to search data in component
138
+ * root, so it is available for all children
139
+ */
140
+ this.apiListbox().pushSearch(e.key);
141
+
142
+ /**
143
+ * Let's get the item selected index at the moment the search
144
+ * starts to later unselect it if necessary
145
+ */
146
+ const itemSelectedIndex = this.getItemSelectedIndex();
147
+
148
+ /**
149
+ * As we have now the search data with value, let's go
150
+ * through all items and check if the search matches
151
+ * with any of the items to get its index value
152
+ */
153
+ const newItemSelectedIndex = this.items.findIndex((item) =>
154
+ item.text.toLowerCase().includes(this.search.toLowerCase())
155
+ );
156
+
157
+ /**
158
+ * Focus the item that matches the search, and if no match
159
+ * we will just remove the selected state from item
160
+ */
161
+ newItemSelectedIndex >= 0
162
+ ? this.setFocusToItem(itemSelectedIndex, newItemSelectedIndex)
163
+ : this.unselect();
164
+
165
+ /**
166
+ * Clear the search data value after some time
167
+ * to allow other searches to be performed
168
+ */
169
+ keydownSearchTimeout = setTimeout(() => {
170
+ this.apiListbox().clearSearch();
171
+ }, 1000);
223
172
  },
224
173
  },
225
174
  };
@@ -1,12 +1,9 @@
1
1
  <template>
2
2
  <component
3
3
  :is="as"
4
- :class="{
5
- ListboxLabel: headless,
6
- 'mb-2 block text-xs font-normal uppercase': !headless,
7
- 'text-inherit': !dark && !headless,
8
- 'text-white': dark && !headless,
9
- }"
4
+ :class="[
5
+ headless ? 'listbox-label' : 'mb-2 block text-xs font-normal uppercase',
6
+ ]"
10
7
  >
11
8
  <slot></slot>
12
9
  </component>
@@ -16,8 +13,6 @@
16
13
  export default {
17
14
  name: 'VTListboxLabel',
18
15
 
19
- inject: ['api'],
20
-
21
16
  props: {
22
17
  as: {
23
18
  type: String,
@@ -28,11 +23,5 @@ export default {
28
23
  default: false,
29
24
  },
30
25
  },
31
-
32
- computed: {
33
- dark() {
34
- return this.api().isDark;
35
- },
36
- },
37
26
  };
38
27
  </script>
@@ -1,22 +1,21 @@
1
1
  <template>
2
2
  <ul
3
3
  :id="id"
4
- :class="{
5
- ListboxList: headless,
6
- '-mx-3 max-h-[160px] w-auto overflow-y-auto': !headless,
7
- }"
4
+ :class="[
5
+ headless
6
+ ? 'listbox-list'
7
+ : '-mx-3 max-h-[160px] w-auto overflow-y-auto scroll-auto',
8
+ ]"
8
9
  >
9
10
  <slot></slot>
10
11
  </ul>
11
12
  </template>
12
13
 
13
14
  <script>
14
- import { genId } from "../../utils/ids";
15
-
16
15
  export default {
17
- name: "VTListboxList",
16
+ name: 'VTListboxList',
18
17
 
19
- inject: ["api"],
18
+ inject: ['apiListbox'],
20
19
 
21
20
  props: {
22
21
  headless: {
@@ -25,38 +24,9 @@ export default {
25
24
  },
26
25
  },
27
26
 
28
- data() {
29
- return {
30
- id: `listboxlist-${genId()}`,
31
- isMousemove: false,
32
- };
33
- },
34
-
35
- mounted() {
36
- const list = {
37
- el: this.$el,
38
- getMousemove: this.getMousemove,
39
- setMousemove: this.setMousemove,
40
- unsetMousemove: this.unsetMousemove,
41
- };
42
-
43
- this.api().registerList(list);
44
- },
45
-
46
- methods: {
47
- // Mousemove instead of mouseover to support keyboard navigation.
48
- // The problem with mouseover is that when scrolling (scrollIntoView),
49
- // mouseover event gets triggered.
50
- setMousemove() {
51
- this.isMousemove = true;
52
- },
53
-
54
- unsetMousemove() {
55
- this.isMousemove = false;
56
- },
57
-
58
- getMousemove() {
59
- return this.isMousemove;
27
+ computed: {
28
+ id() {
29
+ return `listbox-list-${this.apiListbox().id}`;
60
30
  },
61
31
  },
62
32
  };
@@ -1,54 +1,51 @@
1
1
  <template>
2
- <input
3
- v-model="search"
4
- :class="{ ListboxList: headless, 'form-control mb-1': !headless }"
5
- type="text"
6
- @input="onChange"
7
- @keydown.down.prevent="focusNextItem"
8
- @keydown.up.prevent="focusPreviousItem"
9
- @keydown.home.prevent="focusFirstItem"
10
- @keydown.end.prevent="focusLastItem"
11
- @keydown.enter.prevent="onKeyEnter"
12
- @keydown.esc.stop="hide"
13
- @keydown.tab.prevent="hide"
14
- />
2
+ <div class="-mx-3 -mt-2">
3
+ <input
4
+ ref="search"
5
+ :value="modelValue"
6
+ v-bind="$attrs"
7
+ type="text"
8
+ class="leading-0 font-inherit w-full max-w-full appearance-none border-b border-solid border-gray-300 py-2 px-3 text-base text-inherit"
9
+ @input="onInput"
10
+ @click.stop
11
+ @keydown.down.prevent="focusNextItem"
12
+ @keydown.up.prevent="focusPreviousItem"
13
+ @keydown.home.prevent="focusFirstItem"
14
+ @keydown.end.prevent="focusLastItem"
15
+ @keydown.enter.prevent="onKeyEnter"
16
+ @keydown.esc.stop="hide"
17
+ @keydown.tab.prevent="hide"
18
+ />
19
+ </div>
15
20
  </template>
16
21
 
17
22
  <script>
23
+ import { formControlMixin } from '../../../mixins/form-control';
24
+
18
25
  export default {
19
26
  name: 'VTListboxSearch',
20
27
 
21
- inject: ['api'],
28
+ mixins: [formControlMixin],
22
29
 
23
- props: {
24
- headless: {
25
- type: Boolean,
26
- default: false,
27
- },
28
- },
30
+ inheritAttrs: false,
31
+
32
+ inject: ['apiListbox'],
29
33
 
30
34
  data() {
31
35
  return {
36
+ name: 'listbox-search',
37
+ index: null,
32
38
  search: '',
33
- index: -1,
34
39
  };
35
40
  },
36
41
 
37
42
  computed: {
38
- content() {
39
- return this.api().content;
40
- },
41
-
42
- trigger() {
43
- return this.api().trigger;
44
- },
45
-
46
- list() {
47
- return this.api().list;
43
+ componentTrigger() {
44
+ return this.apiListbox().componentTrigger;
48
45
  },
49
46
 
50
47
  items() {
51
- return this.api().items;
48
+ return this.apiListbox().items;
52
49
  },
53
50
 
54
51
  item() {
@@ -57,73 +54,81 @@ export default {
57
54
  },
58
55
 
59
56
  mounted() {
60
- const search = {
57
+ const componentSearch = {
61
58
  el: this.$el,
62
59
  };
63
60
 
64
- this.api().registerSearch(search);
61
+ this.apiListbox().registerSearch(componentSearch);
62
+ this.$nextTick(() => setTimeout(() => this.$refs.search.focus(), 150));
65
63
  },
66
64
 
67
- beforeDestroy() {
65
+ beforeUnmount() {
68
66
  this.search = '';
69
- this.$emit('change', this.search.trim());
67
+ this.$emit('update:modelValue', '');
70
68
  },
71
69
 
72
70
  methods: {
73
71
  focusNextItem() {
74
- if (this.index !== -1) {
75
- this.unselectItem();
76
- }
77
-
78
- this.index++;
72
+ const selectedIndex = this.getItemSelectedIndex();
73
+ const isLast = selectedIndex === this.items.length - 1;
74
+ const firstItemIndex = 0;
75
+ const nextItemIndex = selectedIndex + 1;
76
+ const newSelectedIndex = isLast ? firstItemIndex : nextItemIndex;
79
77
 
80
- if (this.index > this.items.length - 1) {
81
- this.index = 0;
82
- }
83
-
84
- if (this.item) this.item.select();
78
+ this.selectItem(selectedIndex, newSelectedIndex);
85
79
  },
86
80
 
87
81
  focusPreviousItem() {
88
- this.unselectItem();
82
+ const selectedIndex = this.getItemSelectedIndex();
83
+ const isFirst = selectedIndex === 0;
84
+ const lastItemIndex = this.items.length - 1;
85
+ const previousItemIndex = selectedIndex - 1;
86
+ const newSelectedIndex = isFirst ? lastItemIndex : previousItemIndex;
89
87
 
90
- this.index--;
91
-
92
- if (this.index < 0) {
93
- this.index = this.items.length - 1;
94
- }
95
-
96
- this.item.select();
88
+ this.selectItem(selectedIndex, newSelectedIndex);
97
89
  },
98
90
 
99
91
  focusFirstItem() {
100
- this.unselectItem();
101
- this.index = 0;
102
- this.item.select();
92
+ const selectedIndex = this.getItemSelectedIndex();
93
+ const newSelectedIndex = 0;
94
+
95
+ this.selectItem(selectedIndex, newSelectedIndex);
103
96
  },
104
97
 
105
98
  focusLastItem() {
106
- this.unselectItem();
107
- this.index = this.items.length - 1;
108
- this.item.select();
99
+ const selectedIndex = this.getItemSelectedIndex();
100
+ const newSelectedIndex = this.items.length - 1;
101
+
102
+ this.selectItem(selectedIndex, newSelectedIndex);
109
103
  },
110
104
 
111
- unselectItem() {
112
- const isMousemove = this.list.getMousemove();
105
+ selectItem(selectedIndex, newSelectedIndex) {
106
+ // before selecting, let's unselect selected
107
+ // item that were previously focused
108
+ if (selectedIndex >= 0) {
109
+ this.items[selectedIndex].unselect();
110
+ }
113
111
 
114
- if (isMousemove) {
115
- this.list.unsetMousemove();
116
- this.items.forEach((item) => item.unselect());
112
+ // select new item
113
+ if (this.items[newSelectedIndex]) {
114
+ this.index = newSelectedIndex;
115
+ this.items[newSelectedIndex].select();
117
116
  }
117
+ },
118
118
 
119
+ unselectItem() {
119
120
  if (this.item) this.item.unselect();
120
121
  },
121
122
 
122
- onChange() {
123
+ getItemSelectedIndex() {
124
+ return this.items.findIndex((item) => item.isSelected());
125
+ },
126
+
127
+ onInput(event) {
123
128
  this.index = 0;
124
129
  if (this.item) this.item.select();
125
130
 
126
- this.$emit('change', this.search.trim());
131
+ this.$emit('update:modelValue', event.target.value);
127
132
  },
128
133
 
129
134
  onKeyEnter() {
@@ -131,7 +136,10 @@ export default {
131
136
  },
132
137
 
133
138
  hide() {
134
- if (this.content) this.content.hide();
139
+ if (this.componentTrigger) {
140
+ this.componentTrigger.cancel();
141
+ this.componentTrigger.focus();
142
+ }
135
143
  },
136
144
  },
137
145
  };