feat(SPA): media page T0

This commit is contained in:
fsylvain committed 2026-09-30 19:45:47 +02:00
1 parent 0d99544579
commit 67d456baf6
32 files changed
+4874 -40

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
<script setup lang="ts">
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) ? INVALID_FILENAME_MESSAGE : undefined,
)
const help = computed(
() => `Letters, numbers, dashes and underscores. The ${parts.extension} ending stays.`,
)
async function submit() {
touched.value = true
if (!isValidFilenameStem(stem.value)) {
return false
}
await props.action(stem.value.trim())
}
</script>
<template>
<ConfirmDialog
title="Rename image"
description="Only the filename changes. The URL stays the same, so articles that use this image keep working."
confirm-label="Rename"
cancel-label="Cancel"
pending-label="Renaming…"
tone="neutral"
:action="submit"
@close="emit('close', $event)"
>
<template #body="{ pending, confirm }">
<form class="flex flex-col" novalidate @submit.prevent="confirm">
<UFormField
label="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>