11<script setup lang="ts">
2- import { computed , onBeforeUnmount , onMounted , ref } from " vue" ;
2+ import { computed , onBeforeUnmount , onMounted , ref , watch } from " vue" ;
33
44import type { Option } from " ./types" ;
55import ChevronDownIcon from " ./icons/ChevronDownIcon.vue" ;
@@ -150,12 +150,6 @@ const closeMenu = () => {
150150 search .value = " " ;
151151};
152152
153- const focusInput = () => {
154- if (input .value ) {
155- input .value .focus ();
156- }
157- };
158-
159153const setOption = (value : string ) => {
160154 if (props .isMulti ) {
161155 selected .value = [... selected .value , value ];
@@ -214,6 +208,12 @@ const handleNavigation = (e: KeyboardEvent) => {
214208 setOption (filteredOptions .value [focusedOption .value ].value );
215209 }
216210
211+ // When pressing space with menu open but no search, select the focused option.
212+ if (e .code === " Space" && search .value .length === 0 ) {
213+ e .preventDefault ();
214+ setOption (filteredOptions .value [focusedOption .value ].value );
215+ }
216+
217217 if (e .key === " Escape" ) {
218218 e .preventDefault ();
219219 menuOpen .value = false ;
@@ -222,6 +222,20 @@ const handleNavigation = (e: KeyboardEvent) => {
222222 }
223223};
224224
225+ /**
226+ * When pressing space inside the input, open the menu only if the search is
227+ * empty. Otherwise, the user is typing and we should skip this action.
228+ *
229+ * @param e KeyboardEvent
230+ */
231+ const handleInputSpace = (e : KeyboardEvent ) => {
232+ if (! menuOpen .value && search .value .length === 0 ) {
233+ e .preventDefault ();
234+ e .stopImmediatePropagation ();
235+ openMenu ();
236+ }
237+ };
238+
225239const handleClickOutside = (event : MouseEvent ) => {
226240 if (container .value && ! container .value .contains (event .target as Node )) {
227241 menuOpen .value = false ;
@@ -244,6 +258,16 @@ const calculateMenuPosition = () => {
244258 return { top: " 0px" , left: " 0px" };
245259};
246260
261+ // When focusing the input and typing, open the menu automatically.
262+ watch (
263+ () => search .value ,
264+ () => {
265+ if (search .value && ! menuOpen .value ) {
266+ openMenu ();
267+ }
268+ },
269+ );
270+
247271onMounted (() => {
248272 document .addEventListener (" click" , handleClickOutside );
249273 document .addEventListener (" keydown" , handleNavigation );
@@ -276,7 +300,7 @@ onBeforeUnmount(() => {
276300 <div
277301 v-if =" !props.isMulti && selectedOptions[0]"
278302 class =" single-value"
279- @click =" focusInput "
303+ @click =" input?.focus() "
280304 >
281305 <slot name =" value" :option =" selectedOptions[0]" >
282306 {{ getOptionLabel(selectedOptions[0]) }}
@@ -309,8 +333,9 @@ onBeforeUnmount(() => {
309333 tabindex =" 0"
310334 :disabled =" isDisabled"
311335 :placeholder =" selectedOptions.length === 0 ? placeholder : ''"
312- @focus =" openMenu({ focusInput: false } )"
336+ @mousedown =" openMenu()"
313337 @keydown.tab =" closeMenu"
338+ @keydown.space =" handleInputSpace"
314339 >
315340 </div >
316341
0 commit comments