Skip to content

Commit b769cec

Browse files
committed
feat: tags navigate & remove with keyboard
1 parent 4b3de4d commit b769cec

File tree

2 files changed

+70
-3
lines changed

2 files changed

+70
-3
lines changed

src/Multiselect.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,20 @@
4343

4444
<!-- Tags (with search) -->
4545
<template v-if="mode == 'tags'">
46-
<div :class="classList.tags">
47-
46+
<div :class="classList.tags" data-tags>
4847
<slot
4948
v-for="(option, i, key) in iv"
5049
name="tag"
5150
:option="option"
5251
:handleTagRemove="handleTagRemove"
5352
:disabled="disabled"
5453
>
55-
<span :class="classList.tag" :key="key">
54+
<span
55+
:class="classList.tag"
56+
tabindex="-1"
57+
@keyup.enter="handleTagRemove(option, $event)"
58+
:key="key"
59+
>
5660
{{ option[label] }}
5761
<span
5862
v-if="!disabled"

src/composables/useKeyboard.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export default function useKeyboard (props, context, dep)
2020
const selectPointer = dep.selectPointer
2121
const backwardPointer = dep.backwardPointer
2222
const forwardPointer = dep.forwardPointer
23+
const multiselect = dep.multiselect
24+
const tags = dep.tags
2325
const isOpen = dep.isOpen
2426
const open = dep.open
2527
const blur = dep.blur
@@ -61,6 +63,14 @@ export default function useKeyboard (props, context, dep)
6163
const handleKeydown = (e) => {
6264
context.emit('keydown', e, $this)
6365

66+
let tagList
67+
let activeIndex
68+
69+
if (['ArrowLeft', 'ArrowRight', 'Enter'].indexOf(e.key) !== -1 && mode.value === 'tags') {
70+
tagList = [...(multiselect.value.querySelectorAll(`[data-tags] > *`)||[])].filter(e => e !== tags.value)
71+
activeIndex = tagList.findIndex(e => e === document.activeElement)
72+
}
73+
6474
switch (e.key) {
6575
case 'Backspace':
6676
if (mode.value === 'single') {
@@ -81,6 +91,21 @@ export default function useKeyboard (props, context, dep)
8191
case 'Enter':
8292
e.preventDefault()
8393

94+
if (activeIndex !== -1) {
95+
update([...iv.value].filter((v, k) => k !== activeIndex))
96+
97+
if (activeIndex === tagList.length - 1) {
98+
if (tagList.length - 1) {
99+
tagList[tagList.length - 2].focus()
100+
} else if (searchable.value) {
101+
tags.value.querySelector('input').focus()
102+
} else {
103+
multiselect.value.focus()
104+
}
105+
}
106+
return
107+
}
108+
84109
if (addOptionOn.value.indexOf('enter') === -1 && createOption.value) {
85110
return
86111
}
@@ -157,6 +182,44 @@ export default function useKeyboard (props, context, dep)
157182

158183
forwardPointer()
159184
break
185+
186+
case 'ArrowLeft':
187+
if ((searchable.value && tags.value.querySelector('input').selectionStart) || e.shiftKey) {
188+
return
189+
}
190+
191+
e.preventDefault()
192+
193+
if (mode.value !== 'tags') {
194+
return
195+
}
196+
197+
if (activeIndex === -1) {
198+
tagList[tagList.length-1].focus()
199+
}
200+
else if (activeIndex > 0) {
201+
tagList[activeIndex-1].focus()
202+
}
203+
break
204+
205+
case 'ArrowRight':
206+
if (activeIndex === -1 || e.shiftKey) {
207+
return
208+
}
209+
210+
e.preventDefault()
211+
212+
if (tagList.length > activeIndex + 1) {
213+
tagList[activeIndex+1].focus()
214+
}
215+
else if (searchable.value) {
216+
tags.value.querySelector('input').focus()
217+
}
218+
else if (!searchable.value) {
219+
multiselect.value.focus()
220+
}
221+
222+
break
160223
}
161224
}
162225

0 commit comments

Comments
 (0)