94 lines
2.3 KiB
Vue
94 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
import { t } from '@/i18n'
|
|
|
|
import { computed, ref } from 'vue'
|
|
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
|
import type { MediaItem } from '@/composables/useMedia'
|
|
import { isValidFilenameStem, splitFilename } from '@/lib/mediaFormat'
|
|
|
|
const INVALID_FILENAME_MESSAGE = 'Enter a filename.'
|
|
|
|
const props = defineProps<{
|
|
item: MediaItem
|
|
action: (stem: string) => Promise<void>
|
|
}>()
|
|
|
|
const emit = defineEmits<{ close: [confirmed: boolean] }>()
|
|
|
|
const parts = splitFilename(props.item.filename)
|
|
const stem = ref(parts.stem)
|
|
const touched = ref(false)
|
|
|
|
const error = computed(() =>
|
|
touched.value && !isValidFilenameStem(stem.value) ? t(INVALID_FILENAME_MESSAGE) : undefined,
|
|
)
|
|
const help = computed(() =>
|
|
t('Letters, numbers, dashes and underscores. The {0} ending stays.', [parts.extension]),
|
|
)
|
|
|
|
async function submit() {
|
|
touched.value = true
|
|
if (!isValidFilenameStem(stem.value)) {
|
|
return false
|
|
}
|
|
await props.action(stem.value.trim())
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<ConfirmDialog
|
|
:title="t('Rename image')"
|
|
:description="
|
|
t(
|
|
'Only the filename changes. The URL stays the same, so articles that use this image keep working.',
|
|
)
|
|
"
|
|
:confirm-label="t('Rename')"
|
|
:cancel-label="t('Cancel')"
|
|
:pending-label="t('Renaming…')"
|
|
tone="neutral"
|
|
:action="submit"
|
|
@close="emit('close', $event)"
|
|
>
|
|
<template #body="{ pending, confirm }">
|
|
<form class="flex flex-col" novalidate @submit.prevent="confirm">
|
|
<UFormField
|
|
:label="t('Filename')"
|
|
name="filename"
|
|
:error="error"
|
|
:help="help"
|
|
:ui="{
|
|
error: 'mt-1.5 text-xs/5',
|
|
help: 'mt-1.5 text-xs/5',
|
|
}"
|
|
>
|
|
<template v-if="error" #error="{ error: message }">
|
|
<span class="flex items-center gap-1.5">
|
|
<UIcon
|
|
name="i-lucide-circle-alert"
|
|
class="size-3.5 shrink-0 text-error"
|
|
/>
|
|
{{ message }}
|
|
</span>
|
|
</template>
|
|
<UInput
|
|
v-model="stem"
|
|
autocomplete="off"
|
|
:disabled="pending"
|
|
class="w-full"
|
|
:highlight="!!error"
|
|
:ui="{ base: 'h-11 text-lg md:h-10 md:text-base' }"
|
|
@blur="touched = true"
|
|
>
|
|
<template #trailing>
|
|
<span class="font-mono text-base text-muted">{{
|
|
parts.extension
|
|
}}</span>
|
|
</template>
|
|
</UInput>
|
|
</UFormField>
|
|
</form>
|
|
</template>
|
|
</ConfirmDialog>
|
|
</template>
|