Skip to content

Instantly share code, notes, and snippets.

@stephan-v
Last active May 31, 2017 13:53
Show Gist options
  • Save stephan-v/690e99e76b5f7188b95570a8e9f19de7 to your computer and use it in GitHub Desktop.
Save stephan-v/690e99e76b5f7188b95570a8e9f19de7 to your computer and use it in GitHub Desktop.
Vue autocomplete
<template>
<div class="relative">
<input type="text"
autocomplete="off"
class="form-control"
id="destination"
name="destination"
:placeholder="placeholder"
v-model="query"
@keydown="keydown">
<input type="hidden" name="id" :value="id">
<input type="hidden" name="type" :value="type">
<ul class="autocomplete-dropdown" v-show="suggestions.length && show">
<li class="category" v-for="(group, category) in groups" v-show="group.length">
<ul>
<strong>{{ translations[category] }}</strong>
<li class="suggestion" v-for="suggestion in group">
<a href="#"
:class="{ active: suggestion.active }"
@click.prevent="select(suggestion)"
v-html="highlight(suggestion.name, suggestion.nr_hotels)">
</a>
</li>
</ul>
</li>
</ul>
</div>
</template>
<script>
import throttle from 'lodash/throttle';
export default {
props: ['placeholder'],
data() {
return {
id: null,
query: '',
type: '',
suggestions: [],
translations: null,
position: 0,
show: true
};
},
created() {
document.addEventListener('click', () => {
if (this.suggestions.length) {
this.suggestions = [];
}
});
},
computed: {
groups() {
if (this.suggestions.length) {
return this.suggestions.reduce((object, suggestion) => {
object[suggestion.type].push(suggestion);
return object;
}, {
chain: [],
city: [],
province: [],
hotel: [],
theme: [],
region: [],
poi: []
});
}
return '';
}
},
watch: {
query: throttle(function throttling() {
this.autocomplete();
}, 200),
position() {
this.suggestions.forEach((suggestion, index) => {
this.$set(suggestion, 'active', index === this.position);
});
},
suggestions() {
this.position = 0;
// Always highlight the first result when we get a new suggestions list.
this.suggestions.forEach((suggestion, index) => {
this.$set(suggestion, 'active', index === 0);
});
}
},
methods: {
autocomplete() {
axios.get('/autocomplete/', {
params: {
lang: this.$language,
query: this.query
}
})
.then((response) => {
this.suggestions = response.data.data;
this.translations = response.data.types;
})
.catch((error) => {
console.log(error.response.data);
});
},
highlight(name, number) {
// Create a capture group since we are searching case insensitive.
const searchFor = new RegExp(`(${this.query})`, 'gi');
const replaceWith = '<strong class="highlight">$1</strong>';
name = name.replace(searchFor, replaceWith);
return (number > 1) ? `${name} (${number})` : name;
},
select(suggestion) {
this.suggestions = [];
this.id = suggestion.id;
this.type = suggestion.type;
this.query = suggestion.name;
this.show = false;
},
up() {
if (this.position >= 1) this.position -= 1;
},
down() {
if (this.position < this.suggestions.length - 1) this.position += 1;
},
enter() {
this.select(this.suggestions[this.position]);
},
keydown(event) {
switch (event.key) {
case 'Enter':
event.preventDefault();
this.enter();
break;
case 'ArrowUp':
this.up();
break;
case 'ArrowDown':
this.down();
break;
default:
this.show = true;
}
}
}
};
</script>
<style lang="scss" scoped>
.autocomplete-dropdown {
position: absolute;
z-index: 1;
top: 34px;
margin-top: 8px;
min-width: 100%;
border: 1px solid gainsboro;
border-radius: 3px;
background: white;
color: black;
white-space: nowrap;
&:after {
content: '';
transform: rotate(45deg);
position: absolute;
top: -3px;
left: 10%;
display: block;
width: 10px;
height: 10px;
border: 1px solid gainsboro;
border-right: 0;
border-bottom: 0;
margin-top: -3px;
background: #ffffff;
}
.active {
background: #cfe5f7;
}
strong {
padding: 12px 15px 0 15px;
display: block
}
.category {
border-bottom: 1px solid gainsboro;
&:last-child {
border: 0;
}
a {
color: #6d6d6d;
display: block;
padding: 6px 15px;
&:hover {
background: #ebf6fc;
text-decoration: none;
}
}
}
}
@media (max-width: 768px) {
.autocomplete-dropdown {
white-space: inherit;
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment