file-upload
Basic file upload
Basic file upload
Загрузка превью…
src/file-upload/c-file-upload-1.vue
<!--
Оригинал использует IconPlaceholder для дефолтной иконки аватара —
смонтирована всегда (пока файл не выбран). Заменено инлайновым <svg>
(путь lucide "circle-user-round"), идентичным react.tsx.
-->
<script setup lang="ts">
import { Button } from "@/components/ui/button"
import { useFileUpload } from "@/composables/use-file-upload"
const [state, { removeFile, openFileDialog, handleFileChange }] = useFileUpload({
accept: "image/*",
})
</script>
<template>
<div class="flex flex-col items-center gap-2">
<div class="inline-flex items-center gap-2 align-top">
<div
class="border-input rounded-md relative flex size-9 shrink-0 items-center justify-center overflow-hidden border"
:aria-label="state.files[0]?.preview ? 'Preview of uploaded image' : 'Default user avatar'"
>
<img
v-if="state.files[0]?.preview"
class="size-full object-cover"
:src="state.files[0]?.preview"
alt="Preview of uploaded image"
width="32"
height="32"
/>
<svg
v-else
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="opacity-60"
><path d="M18 20a6 6 0 0 0-12 0" /><circle cx="12" cy="10" r="4" /><circle cx="12" cy="12" r="10" /></svg>
</div>
<div class="relative inline-block">
<Button aria-haspopup="dialog" @click="openFileDialog">
{{ state.files[0]?.file.name ? "Change image" : "Upload image" }}
</Button>
<input
type="file"
accept="image/*"
class="sr-only"
aria-label="Upload image file"
tabindex="-1"
@change="handleFileChange"
/>
</div>
</div>
<div v-if="state.files[0]?.file.name" class="inline-flex gap-2 text-xs">
<p class="text-muted-foreground truncate" aria-live="polite">{{ state.files[0]?.file.name }}</p>
<button
class="text-destructive cursor-pointer font-medium hover:underline"
:aria-label="`Remove ${state.files[0]?.file.name}`"
@click="removeFile(state.files[0]?.id ?? '')"
>
Remove
</button>
</div>
<div v-else class="inline-flex gap-2 text-xs">
<p class="text-muted-foreground truncate" aria-live="polite">No image attached</p>
</div>
</div>
</template>
Установка
npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-file-upload-1.jsonЗависимости реестра
Источник: порт из ReUI (Keenthemes, MIT)