<script setup lang="ts">
// Обёртка GnTagInput (кит v0.4.0 без автодополнения): рядок подсказок под полем.
// Набранный текст читается из всплывающего input-события внутреннего поля кита
// (слушатель через attrs попадает на корень .tag-input). Клик по подсказке —
// то же, что набрать имя и нажать Enter; внутренний ввод чистится синтетическим
// input-событием (паттерн синтетических событий уже используется в TaskForm).
import { computed, ref } from 'vue'
import { GnChip, GnTagInput } from 'gnexus-ui-kit/vue'
const props = defineProps<{
modelValue: string[]
allTags: string[]
label?: string
placeholder?: string
}>()
const emit = defineEmits<{
'update:modelValue': [string[]]
add: [string]
remove: [string]
}>()
// Набранный текст внутреннего input'а кита (input-события всплывают до корня)
const typed = ref('')
const rootEl = ref<HTMLElement | null>(null)
function onTyped(e: Event): void {
if (e.target instanceof HTMLInputElement) typed.value = e.target.value
}
// Подсказки: существующие теги, не выбранные (регистронезависимо), по подстроке
const suggestions = computed(() => {
const q = typed.value.trim().toLowerCase()
if (!q) return []
const chosen = new Set(props.modelValue.map((t) => t.toLowerCase()))
return props.allTags
.filter((name) => !chosen.has(name.toLowerCase()) && name.toLowerCase().includes(q))
.slice(0, 8)
})
// Клик по подсказке = добавить тег и очистить внутренний ввод кита (иначе
// набранный кусок оставался бы и Enter-ом добавился бы как отдельный тег)
function pick(name: string): void {
if (props.modelValue.some((t) => t.toLowerCase() === name.toLowerCase())) return
emit('update:modelValue', [...props.modelValue, name])
emit('add', name)
const field = rootEl.value?.querySelector<HTMLInputElement>('.tag-input-field')
if (field && field.value) {
field.value = ''
field.dispatchEvent(new Event('input', { bubbles: true }))
}
}
</script>
<template>
<div ref="rootEl" class="tag-field">
<GnTagInput
:model-value="modelValue"
:label="label"
:placeholder="placeholder"
unique
@update:model-value="emit('update:modelValue', $event)"
@add="emit('add', $event)"
@remove="emit('remove', $event)"
@input="onTyped"
/>
<!-- Подсказки: клик добавляет тег к выбранным -->
<div v-if="suggestions.length" class="tag-suggest">
<GnChip v-for="name in suggestions" :key="name" variant="info" clickable @click="pick(name)">
<i class="ph ph-plus" aria-hidden="true" />{{ name }}
</GnChip>
</div>
</div>
</template>
<style scoped>
/* Подсказки: строка чипов под полем; клик добавляет тег */
.tag-suggest {
margin-top: 5px;
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.tag-suggest .chip {
cursor: pointer;
}
</style>