Skip to content

Commit 6646027

Browse files
author
Ryan
committed
Added keyboard navigation and show all results options
1 parent 09a6cf7 commit 6646027

File tree

3 files changed

+80
-17
lines changed

3 files changed

+80
-17
lines changed

src/components/VueBootstrapTypeahead.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
@focus="isFocused = true"
1717
@blur="handleBlur"
1818
@input="handleInput($event.target.value)"
19+
@keyup.down="$emit('keyup.down', $event.target.value)"
20+
@keyup.up="$emit('keyup.up', $event.target.value)"
21+
@keyup.enter="$emit('keyup.enter', $event.target.value)"
1922
autocomplete="off"
2023
/>
2124
<div v-if="$slots.append || append" class="input-group-append">
@@ -35,6 +38,7 @@
3538
:maxMatches="maxMatches"
3639
:minMatchingChars="minMatchingChars"
3740
:showOnFocus="showOnFocus"
41+
:showAllResults="showAllResults"
3842
@hit="handleHit"
3943
>
4044
<!-- pass down all scoped slots -->
@@ -97,6 +101,10 @@ export default {
97101
type: Boolean,
98102
default: false
99103
},
104+
showAllResults: {
105+
type: Boolean,
106+
default: false
107+
},
100108
placeholder: String,
101109
prepend: String,
102110
append: String

src/components/VueBootstrapTypeaheadList.vue

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<template>
2-
<div class="list-group shadow">
2+
<div class="list-group shadow" ref="suggestionList">
33
<vue-bootstrap-typeahead-list-item
44
v-for="(item, id) in matchedItems" :key="id"
5+
:active="isListItemActive(id)"
56
:data="item.data"
67
:html-text="highlight(item.text)"
78
:background-variant="backgroundVariant"
@@ -61,11 +62,27 @@ export default {
6162
type: Boolean,
6263
default: false
6364
},
65+
showAllResults: {
66+
type: Boolean,
67+
default: false
68+
}
69+
},
70+
71+
created() {
72+
this.$parent.$on('input', this.resetActiveListItem)
73+
this.$parent.$on('keyup.down', this.selectNextListItem)
74+
this.$parent.$on('keyup.up', this.selectPreviousListItem)
75+
this.$parent.$on('keyup.enter', this.hitActiveListItem)
76+
},
77+
data() {
78+
return {
79+
activeListItem: -1
80+
}
6481
},
6582
6683
computed: {
6784
highlight() {
68-
return (text) => {
85+
return text => {
6986
text = sanitize(text)
7087
if (this.query.length === 0) {
7188
return text
@@ -85,7 +102,7 @@ export default {
85102
return []
86103
}
87104
88-
const re = new RegExp(this.escapedQuery, 'gi')
105+
const re = new RegExp(this.showAllResults ? "" : this.escapedQuery, 'gi')
89106
90107
// Filter, sort, and concat
91108
return this.data
@@ -105,6 +122,49 @@ export default {
105122
handleHit(item, evt) {
106123
this.$emit('hit', item)
107124
evt.preventDefault()
125+
},
126+
hitActiveListItem() {
127+
if (this.activeListItem >= 0) {
128+
this.$emit('hit', this.matchedItems[this.activeListItem])
129+
}
130+
},
131+
isListItemActive(id) {
132+
return this.activeListItem === id
133+
},
134+
resetActiveListItem() {
135+
this.activeListItem = -1
136+
},
137+
selectNextListItem() {
138+
if (this.activeListItem < this.matchedItems.length - 1) {
139+
this.activeListItem++
140+
} else {
141+
this.activeListItem = -1
142+
}
143+
},
144+
selectPreviousListItem() {
145+
if (this.activeListItem < 0) {
146+
this.activeListItem = this.matchedItems.length - 1
147+
} else {
148+
this.activeListItem--
149+
}
150+
}
151+
},
152+
watch: {
153+
activeListItem(newValue, oldValue) {
154+
if (newValue >= 0) {
155+
const scrollContainer = this.$refs.suggestionList
156+
const listItem = scrollContainer.children[this.activeListItem]
157+
const scrollContainerlHeight = scrollContainer.clientHeight
158+
const listItemHeight = listItem.clientHeight
159+
const visibleItems = Math.floor(
160+
scrollContainerlHeight / (listItemHeight + 20)
161+
)
162+
if (newValue >= visibleItems) {
163+
scrollContainer.scrollTop = listItemHeight * this.activeListItem
164+
} else {
165+
scrollContainer.scrollTop = 0
166+
}
167+
}
108168
}
109169
}
110170
}

src/components/VueBootstrapTypeaheadListItem.vue

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
tabindex="0"
44
href="#"
55
:class="textClasses"
6-
@mouseover="active = true"
7-
@mouseout="active = false"
86
>
97
<slot name="suggestion" v-bind="{ data: data, htmlText: htmlText }">
108
<span v-html="htmlText"></span>
@@ -16,7 +14,10 @@
1614
export default {
1715
name: 'VueBootstrapTypeaheadListItem',
1816
19-
props: {
17+
props: {
18+
active: {
19+
type: Boolean
20+
},
2021
data: {},
2122
htmlText: {
2223
type: String
@@ -29,19 +30,13 @@ export default {
2930
}
3031
},
3132
32-
data() {
33-
return {
34-
active: false
35-
}
36-
},
37-
3833
computed: {
3934
textClasses() {
40-
let classes = ''
41-
classes += this.active ? 'active' : ''
42-
classes += this.backgroundVariant ? ` bg-${this.backgroundVariant}` : ''
43-
classes += this.textVariant ? ` text-${this.textVariant}` : ''
44-
return `vbst-item list-group-item list-group-item-action ${classes}`
35+
const classes = ['vbst-item', 'list-group-item', 'list-group-item-action']
36+
if (this.active) classes.push('active')
37+
if (this.backgroundVariant) classes.push(`bg-${this.backgroundVariant}`)
38+
if (this.textVariant) classes.push(`text-${this.textVariant}`)
39+
return classes.join(' ')
4540
}
4641
}
4742
}

0 commit comments

Comments
 (0)