<div id="app">
<renderless-tags-input v-model="tags">
<div slot-scope="{ tags, addTag, removeTag, inputAttrs, inputEvents }" class="p-4 rounded border bg-white">
<div class="flex">
<input class="text-input flex-1 mr-2" placeholder="New tag" v-on="inputEvents" v-bind="inputAttrs">
<button type="button" class="btn btn-primary" @click="addTag">Add</button>
</div>
<ul v-show="tags.length > 0" class="mt-4 pl-6">
<li v-for="tag in tags" class="mb-2">
<span class="mr-2">{{ tag }}</span>
<button class="text-grey-dark hover:text-grey-darkest underline text-sm" @click="removeTag(tag)">Remove</button>
</li>
</ul>
</div>
</renderless-tags-input>
</div>
@import url('https://fonts.googleapis.com/css?family=Lato|Roboto:700');
body {
margin: 0;
padding: 0;
background-color: #777;
background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAQAAABuBnYAAAAAU0lEQVQIHQXBwRGDIBAAwO2/AMcCDHAcPjIRxjJ8Je1kl1uqUgphsWu6w0sIG6npLpcUBql4e/wsVRKabrkNTacIYbMrwsF06rqUhsnXVKVT+Hj+Ue4rPSONk4kAAAAASUVORK5CYII=);
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
color: #fff;
font-family: 'Lato', sans-serif;
line-height: 1.4;
}
#app {
display: flex;
justify-content: center;
align-items: center;
}
.tags-input {
display: flex;
flex-wrap: wrap;
background-color: #fff;
border-width: 1px;
border-radius: .25rem;
padding-left: .5rem;
padding-right: 1rem;
padding-top: .5rem;
padding-bottom: .25rem;
}
.tags-input-tag {
display: inline-flex;
line-height: 1;
align-items: center;
font-size: .875rem;
background-color: #bcdefa;
color: #1c3d5a;
border-radius: .25rem;
user-select: none;
padding: .25rem;
margin-right: .5rem;
margin-bottom: .25rem;
}
.tags-input-tag:last-of-type {
margin-right: 0;
}
.tags-input-remove {
color: #2779bd;
font-size: 1.125rem;
line-height: 1;
}
.tags-input-remove:first-child {
margin-right: .25rem;
}
.tags-input-remove:last-child {
margin-left: .25rem;
}
.tags-input-remove:focus {
outline: 0;
}
.tags-input-text {
flex: 1;
outline: 0;
padding-top: .25rem;
padding-bottom: .25rem;
margin-left: .5rem;
margin-bottom: .25rem;
min-width: 10rem;
}
.text-input {
background-color: #fff;
border-width: 1px;
border-radius: .25rem;
padding-left: 1rem;
padding-right: 1rem;
padding-top: .5rem;
padding-bottom: .5rem;
width: 100%;
}
.text-input:focus {
outline: 0;
}
.btn {
font-weight: 600;
border-radius: .25rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
padding-top: .5rem;
padding-bottom: .5rem;
}
.btn-primary {
color: #fff;
background-color: #6574cd;
}
.btn-primary:hover {
background-color: #7886d7;
}
.py-16 {
padding-top: 4rem;
padding-bottom: 4rem;
}
ul {
margin: 0;
padding: 0;
color: #333;
}
.mr-2 {
color: #333
}
View Compiled
// Renderless Tags Input Component
Vue.component('renderless-tags-input', {
props: ['value'],
data () {
return {
newTag: ''
}
},
methods: {
removeTag: function (tag) {
this.$emit('input', this.value.filter(t => t !== tag))
},
addTag: function () {
if (this.newTag.trim().length === 0 || this.value.includes(this.newTag.trim())) {
return
}
this.$emit('input', [...this.value, this.newTag.trim()])
this.newTag = ''
}
},
render () {
return this.$scopedSlots.default({
tags: this.value,
removeTag: this.removeTag,
inputAttrs: {
value: this.newTag
},
inputEvents: {
input: (e) => { this.newTag = e.target.value },
keydown: (e) => {
if (e.keyCode === 13) {
e.preventDefault()
this.addTag()
}
}
},
addTag: this.addTag
})
}
})
let app = new Vue({
el:'#app',
data () {
return {
tags: ['Testing', 'Design']
}
}
})
View Compiled