mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-26 01:40:34 +01:00
feat(FileUpload): new component
Co-Authored-By: Vachmara <55046446+vachmara@users.noreply.github.com>
This commit is contained in:
5
playground/app/pages/components/file-upload.vue
Normal file
5
playground/app/pages/components/file-upload.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<UFileUpload />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,11 +1,81 @@
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<h1 class="font-semibold text-primary mb-1">
|
||||
Playground
|
||||
</h1>
|
||||
<script setup lang="ts">
|
||||
import * as z from 'zod'
|
||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
<UKbd value="meta" /> <UKbd value="K" />
|
||||
</div>
|
||||
</div>
|
||||
const MAX_FILE_SIZE = 2 * 1024 * 1024 // 2MB
|
||||
const MIN_DIMENSIONS = { width: 200, height: 200 }
|
||||
const MAX_DIMENSIONS = { width: 4096, height: 4096 }
|
||||
const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
|
||||
|
||||
const formatBytes = (bytes: number, decimals = 2) => {
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
const k = 1024
|
||||
const dm = decimals < 0 ? 0 : decimals
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
const schema = z.object({
|
||||
avatar: z
|
||||
.instanceof(File, {
|
||||
message: 'Please select an image file.'
|
||||
})
|
||||
.refine(file => file.size <= MAX_FILE_SIZE, {
|
||||
message: `The image is too large. Please choose an image smaller than ${formatBytes(MAX_FILE_SIZE)}.`
|
||||
})
|
||||
.refine(file => ACCEPTED_IMAGE_TYPES.includes(file.type), {
|
||||
message: 'Please upload a valid image file (JPEG, PNG, or WebP).'
|
||||
})
|
||||
.refine(
|
||||
file =>
|
||||
new Promise((resolve) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
const meetsDimensions
|
||||
= img.width >= MIN_DIMENSIONS.width
|
||||
&& img.height >= MIN_DIMENSIONS.height
|
||||
&& img.width <= MAX_DIMENSIONS.width
|
||||
&& img.height <= MAX_DIMENSIONS.height
|
||||
resolve(meetsDimensions)
|
||||
}
|
||||
img.src = e.target?.result as string
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}),
|
||||
{
|
||||
message: `The image dimensions are invalid. Please upload an image between ${MIN_DIMENSIONS.width}x${MIN_DIMENSIONS.height} and ${MAX_DIMENSIONS.width}x${MAX_DIMENSIONS.height} pixels.`
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
type schema = z.output<typeof schema>
|
||||
|
||||
const state = reactive<Partial<schema>>({
|
||||
avatar: undefined
|
||||
})
|
||||
|
||||
const upload = useUpload('/api/blob', { method: 'PUT' })
|
||||
|
||||
async function onSubmit(event: FormSubmitEvent<schema>) {
|
||||
const res = await upload(event.data.avatar)
|
||||
|
||||
console.log(res)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="schema" :state="state" class="flex flex-col gap-4" @submit="onSubmit">
|
||||
<UFormField name="avatar" label="Avatar" description="JPG, GIF or PNG. 1MB Max.">
|
||||
<UFileUpload v-slot="{ open, previewUrls }" v-model="state.avatar" class="flex flex-wrap items-center gap-3" accept="image/*">
|
||||
<UAvatar size="lg" :src="previewUrls?.[0]" icon="i-lucide-image" />
|
||||
|
||||
<UButton label="Choose" color="neutral" @click="open()" />
|
||||
</UFileUpload>
|
||||
</UFormField>
|
||||
|
||||
<UButton label="Submit" type="submit" block />
|
||||
</UForm>
|
||||
</template>
|
||||
|
||||
@@ -16,6 +16,10 @@ export default defineNuxtConfig({
|
||||
|
||||
compatibilityDate: '2024-07-09',
|
||||
|
||||
hub: {
|
||||
blob: true
|
||||
},
|
||||
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
// prevents reloading page when navigating between components
|
||||
|
||||
12
playground/server/api/blob.put.ts
Normal file
12
playground/server/api/blob.put.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export default eventHandler(async (event) => {
|
||||
return hubBlob().handleUpload(event, {
|
||||
formKey: 'files', // read file or files form the `formKey` field of request body (body should be a `FormData` object)
|
||||
multiple: true, // when `true`, the `formKey` field will be an array of `Blob` objects
|
||||
ensure: {
|
||||
types: ['image/jpeg', 'image/png'] // allowed types of the file
|
||||
},
|
||||
put: {
|
||||
addRandomSuffix: true
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user