<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
// Универсальный враппер «показ ↔ редактор» для инлайн-правки поля на месте.
// В ките инлайн-компонента нет, а GnInput умеет bare (голый input) — слоты
// позволяют подставить любой редактор (input, select, пара дата+период).
// Сохранение — явными кнопками ✓/✗ (не по blur); Esc — отмена.
// save() может кинуть ошибку: тогда редактор остаётся открытым
// (родитель уже показал тост).
const props = defineProps<{
/** aria-label/tooltip кнопки входа в правку */
label: string
/** Родительский хендлер сохранения; promise → busy, throw → редактор открыт */
save: () => Promise<unknown> | unknown
}>()
const emit = defineEmits<{ open: [] }>()
const { t } = useI18n()
const open = ref(false)
const busy = ref(false)
function edit() {
emit('open')
open.value = true
}
function cancel() {
if (busy.value) return
open.value = false
}
async function save() {
if (busy.value) return
busy.value = true
try {
await props.save()
open.value = false
} catch {
// ошибка уже показана родителем — оставляем редактор открытым
} finally {
busy.value = false
}
}
</script>
<template>
<span class="inline-field" @keydown.esc="cancel">
<span v-if="!open" class="inline-display">
<slot name="display" :edit="edit" />
</span>
<span v-else class="inline-editing">
<slot name="edit" :save="save" :cancel="cancel" :busy="busy" />
<span class="inline-actions">
<GnIconButton
icon="ph-check"
size="sm"
:label="t('common.save')"
:disabled="busy"
@click="save"
/>
<GnIconButton
icon="ph-x"
size="sm"
:label="t('common.cancel')"
:disabled="busy"
@click="cancel"
/>
</span>
</span>
</span>
</template>
<style scoped>
.inline-field {
display: inline-flex;
min-width: 0;
}
/* показ: кликабельно, по hover — подсказка пунктиром */
.inline-display {
cursor: pointer;
min-width: 0;
}
.inline-display:hover {
text-decoration: underline dashed var(--accent, #7aa2f7);
text-underline-offset: 3px;
}
/* правка: редактор + кнопки; перенос строкой ниже лучше обрезки в узкой колонке */
.inline-editing {
display: inline-flex;
align-items: center;
gap: 0.4rem;
flex-wrap: wrap;
min-width: 0;
}
.inline-actions {
display: inline-flex;
gap: 0.25rem;
flex: none;
}
</style>