mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
refactor: TagsSelectizeItem typing
This commit is contained in:
parent
2977ed4d7a
commit
0f8fbd4830
2 changed files with 60 additions and 53 deletions
|
@ -1,7 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// FIXME addTag removeTag types
|
|
||||||
import type { BDropdown, BFormInput } from 'bootstrap-vue-next'
|
import type { BDropdown, BFormInput } from 'bootstrap-vue-next'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import type {
|
||||||
|
BaseItemComputedProps,
|
||||||
|
TagsSelectizeItemProps,
|
||||||
|
} from '@/types/form'
|
||||||
|
|
||||||
type TagUpdateArgs = {
|
type TagUpdateArgs = {
|
||||||
action: 'add' | 'remove'
|
action: 'add' | 'remove'
|
||||||
|
@ -14,30 +19,23 @@ defineOptions({
|
||||||
})
|
})
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<TagsSelectizeItemProps & BaseItemComputedProps<string[]>>(),
|
||||||
modelValue: string[]
|
|
||||||
// FIXME typing
|
|
||||||
options: string[]
|
|
||||||
id: string
|
|
||||||
placeholder?: string
|
|
||||||
limit?: number
|
|
||||||
name?: string
|
|
||||||
itemsName: string
|
|
||||||
disabledItems?: string[]
|
|
||||||
auto?: boolean
|
|
||||||
noTags?: boolean
|
|
||||||
label?: string
|
|
||||||
tagIcon?: string
|
|
||||||
}>(),
|
|
||||||
{
|
{
|
||||||
placeholder: undefined,
|
id: undefined,
|
||||||
limit: undefined,
|
|
||||||
name: undefined,
|
name: undefined,
|
||||||
disabledItems: () => [],
|
placeholder: undefined,
|
||||||
|
touchKey: undefined,
|
||||||
auto: false,
|
auto: false,
|
||||||
noTags: false,
|
disabledItems: undefined,
|
||||||
label: undefined,
|
label: undefined,
|
||||||
|
limit: undefined,
|
||||||
|
noTags: false,
|
||||||
tagIcon: undefined,
|
tagIcon: undefined,
|
||||||
|
|
||||||
|
ariaDescribedby: undefined,
|
||||||
|
modelValue: undefined,
|
||||||
|
state: undefined,
|
||||||
|
validation: undefined,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,18 +44,21 @@ const emit = defineEmits<{
|
||||||
'tag-update': [value: TagUpdateArgs]
|
'tag-update': [value: TagUpdateArgs]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const search = ref('')
|
const model = defineModel<string[]>()
|
||||||
const searchElem = ref<InstanceType<typeof BDropdown> | null>(null)
|
const searchElem = ref<InstanceType<typeof BDropdown> | null>(null)
|
||||||
const dropdownElem = ref<InstanceType<typeof BFormInput> | null>(null)
|
const dropdownElem = ref<InstanceType<typeof BFormInput> | null>(null)
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const search = ref('')
|
||||||
const criteria = computed(() => {
|
const criteria = computed(() => {
|
||||||
return search.value.trim().toLowerCase()
|
return search.value.trim().toLowerCase()
|
||||||
})
|
})
|
||||||
|
|
||||||
const availableOptions = computed(() => {
|
const availableOptions = computed(() => {
|
||||||
const options = props.options.filter((opt) => {
|
const options = props.options.filter((opt) => {
|
||||||
return (
|
return (
|
||||||
props.modelValue.indexOf(opt) === -1 && !props.disabledItems.includes(opt)
|
props.modelValue?.indexOf(opt) === -1 &&
|
||||||
|
props.disabledItems?.includes(opt)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if (criteria.value) {
|
if (criteria.value) {
|
||||||
|
@ -67,7 +68,14 @@ const availableOptions = computed(() => {
|
||||||
}
|
}
|
||||||
return options
|
return options
|
||||||
})
|
})
|
||||||
|
const searchI18n = computed(() => {
|
||||||
|
const params = { items: t('items.' + props.itemsName, 0) }
|
||||||
|
return {
|
||||||
|
label: t('search.for', { items: props.itemsName }),
|
||||||
|
invalidFeedback: t('search.not_found', params, 0),
|
||||||
|
noItems: t('items_verbose_items_left', params, 0),
|
||||||
|
}
|
||||||
|
})
|
||||||
const searchState = computed(() => {
|
const searchState = computed(() => {
|
||||||
return criteria.value && availableOptions.value.length === 0 ? false : null
|
return criteria.value && availableOptions.value.length === 0 ? false : null
|
||||||
})
|
})
|
||||||
|
@ -87,7 +95,7 @@ function onRemoveTag(option: string, applyFn: TagUpdateArgs['applyFn']) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDropdownKeydown(e) {
|
function onDropdownKeydown(e: KeyboardEvent) {
|
||||||
// Allow to start searching after dropdown opening
|
// Allow to start searching after dropdown opening
|
||||||
// FIXME check if dropdownElem.value!.firstElementChild works (removed the $el)
|
// FIXME check if dropdownElem.value!.firstElementChild works (removed the $el)
|
||||||
if (
|
if (
|
||||||
|
@ -97,18 +105,22 @@ function onDropdownKeydown(e) {
|
||||||
searchElem.value!.$el.focus()
|
searchElem.value!.$el.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME call touch somewhere?
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="tags-selectize">
|
<div class="tags-selectize">
|
||||||
<BFormTags
|
<BFormTags
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:modelValue="modelValue"
|
|
||||||
@update:modelValue="emit('update:modelValue', $event)"
|
|
||||||
:id="id"
|
:id="id"
|
||||||
|
v-model="model"
|
||||||
|
:name="name"
|
||||||
|
:aria-describedby="ariaDescribedby"
|
||||||
|
:state="state"
|
||||||
|
no-outer-focus
|
||||||
size="lg"
|
size="lg"
|
||||||
class="p-0 border-0"
|
class="p-0 border-0"
|
||||||
no-outer-focus
|
|
||||||
>
|
>
|
||||||
<template #default="{ tags, disabled, addTag, removeTag }">
|
<template #default="{ tags, disabled, addTag, removeTag }">
|
||||||
<ul
|
<ul
|
||||||
|
@ -121,10 +133,10 @@ function onDropdownKeydown(e) {
|
||||||
class="list-inline-item"
|
class="list-inline-item"
|
||||||
>
|
>
|
||||||
<BFormTag
|
<BFormTag
|
||||||
@remove="onRemoveTag(tag, removeTag)"
|
|
||||||
:title="tag"
|
:title="tag"
|
||||||
:disabled="disabled || disabledItems.includes(tag)"
|
:disabled="disabled || disabledItems?.includes(tag)"
|
||||||
class="border border-dark mb-2"
|
class="border border-dark mb-2"
|
||||||
|
@remove="onRemoveTag(tag, removeTag)"
|
||||||
>
|
>
|
||||||
<YIcon v-if="tagIcon" :iname="tagIcon" /> {{ tag }}
|
<YIcon v-if="tagIcon" :iname="tagIcon" /> {{ tag }}
|
||||||
</BFormTag>
|
</BFormTag>
|
||||||
|
@ -145,30 +157,22 @@ function onDropdownKeydown(e) {
|
||||||
<BDropdownGroup class="search-group">
|
<BDropdownGroup class="search-group">
|
||||||
<BDropdownForm @submit.stop.prevent="() => {}">
|
<BDropdownForm @submit.stop.prevent="() => {}">
|
||||||
<BFormGroup
|
<BFormGroup
|
||||||
:label="$t('search.for', { items: itemsName })"
|
:label="searchI18n.label"
|
||||||
|
:label-for="id + '-search-input'"
|
||||||
label-cols-md="auto"
|
label-cols-md="auto"
|
||||||
label-size="sm"
|
label-size="sm"
|
||||||
:label-for="id + '-search-input'"
|
:invalid-feedback="searchI18n.invalidFeedback"
|
||||||
:invalid-feedback="
|
|
||||||
$t(
|
|
||||||
'search.not_found',
|
|
||||||
{
|
|
||||||
items: $t('items.' + itemsName, 0),
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:state="searchState"
|
:state="searchState"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="mb-0"
|
class="mb-0"
|
||||||
>
|
>
|
||||||
<BFormInput
|
<BFormInput
|
||||||
|
:id="id + '-search-input'"
|
||||||
ref="searchElem"
|
ref="searchElem"
|
||||||
v-model="search"
|
v-model="search"
|
||||||
:id="id + '-search-input'"
|
|
||||||
type="search"
|
|
||||||
size="sm"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
size="sm"
|
||||||
|
type="search"
|
||||||
/>
|
/>
|
||||||
</BFormGroup>
|
</BFormGroup>
|
||||||
</BDropdownForm>
|
</BDropdownForm>
|
||||||
|
@ -184,15 +188,7 @@ function onDropdownKeydown(e) {
|
||||||
</BDropdownItemButton>
|
</BDropdownItemButton>
|
||||||
<BDropdownText v-if="!criteria && availableOptions.length === 0">
|
<BDropdownText v-if="!criteria && availableOptions.length === 0">
|
||||||
<YIcon iname="exclamation-triangle" />
|
<YIcon iname="exclamation-triangle" />
|
||||||
{{
|
{{ searchI18n.noItems }}
|
||||||
$t(
|
|
||||||
'items_verbose_items_left',
|
|
||||||
{
|
|
||||||
items: $t('items.' + itemsName, 0),
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</BDropdownText>
|
</BDropdownText>
|
||||||
</BDropdown>
|
</BDropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -82,3 +82,14 @@ export type TagsItemProps = BaseWritableItemProps & {
|
||||||
// FIXME no options on BFormTags?
|
// FIXME no options on BFormTags?
|
||||||
// options?: string[]
|
// options?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TagsSelectizeItemProps = BaseWritableItemProps & {
|
||||||
|
itemsName: string
|
||||||
|
options: string[]
|
||||||
|
auto?: boolean
|
||||||
|
disabledItems?: string[]
|
||||||
|
label: string
|
||||||
|
limit?: number
|
||||||
|
noTags?: boolean
|
||||||
|
tagIcon?: string
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue