Skip to content

Instantly share code, notes, and snippets.

@acrolink
Forked from TahaSh/index.html
Created March 1, 2018 21:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acrolink/b2171b7214d872063550338572a4ed29 to your computer and use it in GitHub Desktop.
Save acrolink/b2171b7214d872063550338572a4ed29 to your computer and use it in GitHub Desktop.
Reusable Autocomplete-Input Component in Vue 2.1
<title>Vue Awesome Autocomplete</title>
<link rel="stylesheet" href="https://unpkg.com/bulma/css/bulma.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="style.css">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<autocomplete-input
:options="options"
@select="onOptionSelect"
>
<template slot="item" scope="option">
<article class="media">
<figure class="media-left">
<p class="image is-64x64">
<img :src="option.thumbnail">
</p>
</figure>
<p>
<strong>{{ option.title }}</strong>
<br>
{{ option.description }}
</p>
</article>
</template>
</autocomplete-input>
</div>
<script id="autocomplete-input-template" type="text/x-template">
<div class="autocomplete-input">
<p class="control has-icon has-icon-right">
<input
v-model="keyword"
class="input is-large"
placeholder="Search..."
@input="onInput($event.target.value)"
@keyup.esc="isOpen = false"
@blur="isOpen = false"
@keydown.down="moveDown"
@keydown.up="moveUp"
@keydown.enter="select"
>
<i class="fa fa-angle-down"></i>
</p>
<ul v-show="isOpen"
class="options-list"
>
<li v-for="(option, index) in fOptions"
:class="{
'highlighted': index === highlightedPosition
}"
@mouseenter="highlightedPosition = index"
@mousedown="select"
>
<slot name="item"
:title="option.title"
:description="option.description"
:thumbnail="option.thumbnail"
>
</li>
</ul>
</div>
</script>
<script>
Vue.component('autocomplete-input', {
template: '#autocomplete-input-template',
props: {
options: {
type: Array,
required: true
}
},
data () {
return {
isOpen: false,
highlightedPosition: 0,
keyword: ''
}
},
computed: {
fOptions () {
const re = new RegExp(this.keyword, 'i')
return this.options.filter(o => o.title.match(re))
}
},
methods: {
onInput (value) {
this.highlightedPosition = 0
this.isOpen = !!value
},
moveDown () {
if (!this.isOpen) {
return
}
this.highlightedPosition =
(this.highlightedPosition + 1) % this.fOptions.length
},
moveUp () {
if (!this.isOpen) {
return
}
this.highlightedPosition = this.highlightedPosition - 1 < 0
? this.fOptions.length - 1
: this.highlightedPosition - 1
},
select () {
const selectedOption = this.fOptions[this.highlightedPosition]
this.$emit('select', selectedOption)
this.isOpen = false
this.keyword = selectedOption.title
}
}
})
new Vue({
el: '#app',
data: {
options: [
{
title: 'First Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/1'
},
{
title: 'Second Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/2'
},
{
title: 'Third Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/3'
},
{
title: 'Fourth Scene',
description: 'lorem ipsum dolor amet.',
thumbnail: 'http://lorempicsum.com/nemo/200/200/4'
}
]
},
methods: {
onOptionSelect (option) {
console.log('Selected option:', option)
}
}
})
</script>
html, body {
height: 100%;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
display: flex;
justify-content: center;
padding-top: 100px;
height: 100%;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
input {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
}
.autocomplete-input {
position: relative;
height: 300px;
}
ul.options-list {
display: flex;
flex-direction: column;
margin-top: -12px;
border: 1px solid #dbdbdb;
border-radius: 0 0 3px 3px;
position: absolute;
width: 100%;
overflow: hidden;
}
ul.options-list li {
width: 100%;
flex-wrap: wrap;
background: white;
margin: 0;
border-bottom: 1px solid #eee;
color: #363636;
padding: 7px;
cursor: pointer;
}
ul.options-list li.highlighted {
background: #f8f8f8
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment