Created
December 9, 2016 12:45
-
-
Save TahaSh/04fae389aaa0250bc04a37da6d691385 to your computer and use it in GitHub Desktop.
Reusable Autocomplete-Input Component in Vue 2.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Thank you very much for sharing this.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Taha, thank you for your work.
I have faced with is a scrolling problem on mouse movement. Let's change the css like this:
.autocomplete-input {
position: relative;
height: 200px;
overflow-y: auto;
overflow-x: hidden;
}
Then, search for word "scene". The results causes to overflowing. When you move down by keyboard, the block does not scroll down. How can it be solved?
Best regards...