Simple Material Design Select (with search) using Vue. Demo: https://jsfiddle.net/o4gowmvb/2/
div.dropdown {
display: inline-block;
background: #FFF;
z-index: 1;
}
div.dropdown, div.options {
width:175px;
}
div.dropdown.active {
z-index:10;
visibility:visible;
}
div.dropdown div.selected {
text-align: left;
border-bottom: 2px solid #DDD;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
display: flex;
flex-flow: row nowrap;
justify-content:space-between;
margin:0;
cursor: pointer;
}
div.dropdown div.selected {
padding: 10px 6px 8px 6px;
}
div.dropdown.active div.selected {
border-bottom: 0;
font-weight: bold;
}
div.dropdown div.selected svg {
transition: transform ease-in-out 0.195s;
z-index:2;
}
div.dropdown div.selected span.option, div.dropdown div.selected svg {
margin: auto 0px auto 0px;
}
div.dropdown.active div.selected svg {
transform: rotate(180deg);
}
div.dropdown div.options {
box-shadow:0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
}
div.dropdown div.options {
height: 0;
position:absolute;
overflow: hidden;
z-index:1;
}
div.dropdown.active div.options {
height:inherit!important;
}
div.dropdown div.options ul.list {
background:#FFF;
padding:0;
margin:0;
list-style:none;
max-height: 400px;
overflow-y: scroll;
}
div.dropdown div.options ul.list li {
text-align: left;
padding: 12px;
cursor: pointer;
background: #FFF;
color: rgba(0, 0, 0, 0.75);
transition: all 0.195s ease-in-out;
cursor: pointer;
box-sizing:border-box;
}
div.dropdown div.options ul.list li.empty {
color:rgba(189, 195, 199,1.0);
text-align:center;
}
div.dropdown div.options ul.list li.empty:hover {
cursor:default;
background:#FFF!important;
}
div.dropdown div.options input.search {
margin:0;
width:100%;
border:0;
font-family:Roboto;
padding:12px;
color:rgba(0, 0, 0, 0.75);
font-size:1em;
box-sizing:border-box;
}
div.dropdown div.options ul.list li:hover {
background: rgba(236, 240, 241, 0.6);
}
div.dropdown[disabled] {
cursor:default;
}
div.dropdown[disabled] div.selected{
color:rgba(189, 195, 199,1.0);
border-bottom:0px;
}
div.dropdown[disabled] svg{
fill:rgba(189, 195, 199,1.0);
}
<material-dropdown
:dropdown-selected="selected"
:dropdown-options="options"
:dropdown-set="changeSelected"
:dropdown-disabled="itsDisabled"
:dropdown-placeholder="placeholder"
:dropdown-empty-text="emptyText">
</material-dropdown>
Vue.component('material-dropdown', {
props: [
'dropdownSelected',
'dropdownOptions',
'dropdownSet',
'dropdownDisabled',
'dropdownPlaceholder',
'dropdownSearchPlaceholder',
'dropdownEmptyText'
],
data: function () {
return {
allOptions: this.dropdownOptions,
options: this.dropdownOptions,
isActive: false,
optionsPosition: {}
}
},
template: `
<div class="dropdown" :class="{active: isActive}" :disabled="dropdownDisabled">
<div class="selected" @click="drop">
<span v-if="isActive"></span>
<span v-else>{{ dropdownSelected.name || dropdownPlaceholder || 'Select an option.' }}</span>
<svg fill="#444444" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
<path d="M0-.75h24v24H0z" fill="none" />
</svg>
</div>
<div class="options" :style="optionsPosition">
<input class="search" @keyup="searchOptions" :placeholder="dropdownSearchPlaceholder || 'Search options.'"/>
<ul class="list">
<li v-for="option in options" @click.stop="emitSet(option)"> {{ option.name }} </li>
<li class="empty" v-if="options.length === 0">
{{ dropdownEmptyText || 'Without options.' }}
</li>
</ul>
</div>
</div>`,
methods: {
drop: function (event) {
let select = this.$el
let search = select.querySelector('div.options input.search')
if (this.dropdownDisabled) return ''
if (this.isActive) {
this.options = this.allOptions
search.value = ''
} else {
search.focus()
}
this.setOptionsPosition()
this.isActive = !this.isActive
},
emitSet: function (option) {
this.dropdownSet(option)
this.drop()
},
searchOptions: function (event) {
let search = event.target.value.toLowerCase()
if (search) {
this.options = this.allOptions.filter(option => {
let name = option.name.toLowerCase()
return name.indexOf(search) !== -1
})
} else {
this.options = this.allOptions
}
},
setOptionsPosition: function () {
let select = this.$el
let options = select.querySelector('div.options')
this.optionsPosition = {
top: `${select.offsetTop}px`,
left: `${select.offsetLeft}px`
}
}
},
updated: function () {
if (this.itsActivated) this.setOptionsPosition()
}
})