add component TagsSelectize

This commit is contained in:
axolotle 2021-04-16 20:26:47 +02:00
parent c3d86065fa
commit 225b1a5d61
2 changed files with 158 additions and 2 deletions

View file

@ -0,0 +1,154 @@
<template>
<div class="tags-selectize">
<b-form-tags
v-bind="$attrs" v-on="$listeners"
:value="value" :id="id"
size="lg" class="p-0 border-0" no-outer-focus
>
<template v-slot="{ tags, disabled, addTag, removeTag }">
<ul v-if="!noTags && tags.length > 0" class="list-inline d-inline-block mb-2">
<li v-for="tag in tags" :key="id + '-' + tag" class="list-inline-item">
<b-form-tag
@remove="onRemoveTag({ option: tag, removeTag })"
:title="tag"
:disabled="disabled || disabledItems.includes(tag)"
variant="light"
class="border border-dark mb-2"
>
<icon v-if="tagIcon" :iname="tagIcon" /> {{ tag }}
</b-form-tag>
</li>
</ul>
<b-dropdown
ref="dropdown"
variant="outline-dark" block menu-class="w-100"
@keydown.native="onDropdownKeydown"
>
<template #button-content>
<icon iname="search-plus" /> {{ label }}
</template>
<b-dropdown-group class="search-group">
<b-dropdown-form @submit.stop.prevent="() => {}">
<b-form-group
:label="$t('search.for', { items: itemsName })"
label-cols-md="auto" label-size="sm" :label-for="id + '-search-input'"
:invalid-feedback="$t('search.not_found', { items: $tc('items.' + itemsName, 0) })"
:state="searchState" :disabled="disabled"
class="mb-0"
>
<b-form-input
ref="search-input" v-model="search"
:id="id + '-search-input'"
type="search" size="sm" autocomplete="off"
/>
</b-form-group>
</b-dropdown-form>
<b-dropdown-divider />
</b-dropdown-group>
<b-dropdown-item-button
v-for="option in availableOptions"
:key="option"
@click="onAddTag({ option, addTag })"
>
{{ option }}
</b-dropdown-item-button>
<b-dropdown-text v-if="!criteria && availableOptions.length === 0">
<icon iname="exclamation-triangle" />
{{ $t('items_verbose_items_left', { items: $tc('items.' + itemsName, 0) }) }}
</b-dropdown-text>
</b-dropdown>
</template>
</b-form-tags>
</div>
</template>
<script>
export default {
name: 'TagsSelectize',
props: {
value: { type: Array, required: true },
options: { type: Array, required: true },
id: { type: String, required: true },
itemsName: { type: String, required: true },
disabledItems: { type: Array, default: () => ([]) },
// By default `addTag` and `removeTag` have to be executed manually by listening to 'tag-update'.
auto: { type: Boolean, default: false },
noTags: { type: Boolean, default: false },
label: { type: String, default: null },
tagIcon: { type: String, default: null }
},
data () {
return {
search: ''
}
},
computed: {
criteria () {
return this.search.trim().toLowerCase()
},
availableOptions () {
const criteria = this.criteria
const options = this.options.filter(opt => {
return this.value.indexOf(opt) === -1 && !this.disabledItems.includes(opt)
})
if (criteria) {
return options.filter(opt => opt.toLowerCase().indexOf(criteria) > -1)
}
return options
},
searchState () {
return this.criteria && this.availableOptions.length === 0 ? false : null
}
},
methods: {
onAddTag ({ option, addTag }) {
this.$emit('tag-update', { action: 'add', option, applyMethod: addTag })
this.search = ''
if (this.auto) {
addTag(option)
}
},
onRemoveTag ({ option, removeTag }) {
this.$emit('tag-update', { action: 'remove', option, applyMethod: removeTag })
if (this.auto) {
removeTag(option)
}
},
onDropdownKeydown (e) {
// Allow to start searching after dropdown opening
if (
!['Tab', 'Space'].includes(e.code) &&
e.target === this.$refs.dropdown.$el.lastElementChild
) {
this.$refs['search-input'].focus()
}
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .dropdown-menu {
max-height: 300px;
overflow-y: auto;
padding-top: 0;
.search-group {
padding-top: .5rem;
position: sticky;
top: 0;
background-color: white;
}
}
</style>

View file

@ -242,10 +242,12 @@
"groups": "no groups | group | {c} groups",
"installed_apps": "no installed apps | installed app | {c} installed apps",
"logs": "no logs | log | {c} logs",
"permissions": "no permissions | permission | {c} permissions",
"services": "no services | service | {c} services",
"users": "no users | user | {c} users"
},
"items_verbose_count": "There is {items}.",
"items_verbose_count": "There are {items}.",
"items_verbose_items_left": "There are {items} left.",
"label": "Label",
"label_for_manifestname": "Label for {name}",
"last_ran": "Last time ran:",
@ -412,7 +414,7 @@
"save": "Save",
"search": {
"for": "Search for {items}...",
"not_found": "There is {items} matching your criteria."
"not_found": "There are {items} matching your criteria."
},
"select_all": "Select all",
"select_none": "Select none",