Vue-multiselect
Example fiddle
HTML
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/vue-multiselect.min.css">
<script src="https://unpkg.com/[email protected]/dist/vue-multiselect.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-resource/1.5.0/vue-resource.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div id="app">
<multiselect :options="options" :value="optionsProxy" @input="updateSelected" @search-change="searchQuery" :multiple="true" :searchable="true" :close-on-select="true" placeholder="Search" :custom-label="customLabel" :loading="showLoadingSpinner"></multiselect>
<ul class="resources-list">
<template v-for="(resource, index) in selectedResources">
<li class="resource-item" :data-index="index">
<div class="resource-info">
<div class="resource-title" :id="index">
<span>{{ resource.name }}</span>
<span class="version">{{resource.version}}</span>
</div>
<div class="resource-description">
<span>{{ resource.description }}</span>
<span>{{ resource.latest }}</span>
</div>
<div class="resource-url">
<a :href="resource.latest" target="_blank">{{ resource.latest }}</a>
</div>
</div>
<div class="delete-controls" v-on:click.prevent="removeDependency(index)">
<i class="fa fa-times fa-fw"></i>
</div>
</li>
</template>
</ul>
</div>
SCSS
* {
font-family: 'Lato', 'Avenir', sans-serif;
}
.resources-list {
margin-top: 15px;
padding: 0;
list-style: none;
}
.resources-list li {
display: flex;
align-items: center;
min-height: 50px;
padding: 10px 40px 10px 0;
border-bottom: 1px solid rgba(51, 51, 51, 0.1);
position: relative;
}
.resources-list li:last-child {
border-bottom: none;
}
.resources-list li .resource-title {
font-size: 1em;
color: #333;
max-height: 50px;
min-height: 50px;
height: 50px;
}
.resources-list li .version {
opacity: 0.7;
margin-left: 5px;
font-size: 75%;
}
.resources-list li .resource-description,
.resources-list li .resource-url {
font-size: 0.8em;
color: #999;
margin-top: 5px;
}
.resources-list li .resource-url {
margin-top: 0;
}
.resources-list li .resource-description:empty {
display: none;
}
.resources-list li .delete-controls {
position: absolute;
right: 0;
width: 40px;
text-align: center;
color: #999;
cursor: pointer;
}
.resources-list li .delete-controls:hover,
.resources-list li .delete-controls:focus {
color: red;
}
Babel + JSX
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data: {
options: [
],
optionsProxy: [],
selectedResources: [],
showLoadingSpinner: false
},
methods: {
customLabel (option) {
return `${option.name} - ${option.version}`
},
updateSelected(value) {
value.forEach((resource) => {
// Adds selected resources to array
this.selectedResources.push(resource)
})
// Clears selected array
// This prevents the tags from being displayed
this.optionsProxy = []
},
cdnRequest(value) {
this.$http.get(`https://api.cdnjs.com/libraries?search=${value}&fields=version,description`).then((response) => {
// get body data
this.options = []
response.body.results.forEach((object) => {
this.options.push(object)
});
this.showLoadingSpinner = false
}, (response) => {
// error callback
})
},
searchQuery(value) {
this.showLoadingSpinner = true
// GET
this.cdnRequest(value)
},
removeDependency(index) {
this.selectedResources.splice(index, 1)
}
},
created() {
const value = ''
this.cdnRequest(value)
}
}).$mount('#app')