Archivo Upload

Un elemento de entrada para subir archivos.

xph0000xUso

Utilice la directiva v-model para controlar el valor del archivo.

<script setup lang="ts">
const value = ref(null)
</script>

<template>
  <UFileUpload v-model="value" class="w-96 min-h-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const value = ref(null)
</script>

<template>
  <UFileUpload v-model="value" class="w-96 min-h-48" />
</template>

Multiplicación

Utilice el prop multiple para permitir que se seleccionen varios archivos.

<template>
  <UFileUpload multiple class="w-96 min-h-48" />
</template>

Dropzone

Utilice el prop dropzone para habilitar/deshabilitar el área desplegable.

<template>
  <UFileUpload :dropzone="false" class="w-96 min-h-48" />
</template>

Interactivo

Utilice el prop interactive para habilitar/deshabilitar el área en la que se puede hacer clic.

Esto puede ser útil al agregar un componente Button en la ranura #actions.
<template>
  <UFileUpload :interactive="false" class="w-96 min-h-48" />
</template>

Acepto

Utilice la prop accept para especificar los tipos de archivo permitidos para la entrada. Proporcionar una lista separada por comas de MIME types o extensiones de archivo (por ejemplo, image/png,application/pdf,.jpg). Por defecto a * (todos los tipos de archivo).

<template>
  <UFileUpload accept="image/*" class="w-96 min-h-48" />
</template>

Label

Utilice el prop label para establecer la etiqueta del FileUpload.

Drop your image here
<template>
  <UFileUpload label="Drop your image here" class="w-96 min-h-48" />
</template>

Descripción

Utilice el prop description para establecer la descripción del FileUpload.

Drop your image here
SVG, PNG, JPG or GIF (max. 2MB)
<template>
  <UFileUpload
    label="Drop your image here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    class="w-96 min-h-48"
  />
</template>

Icon

Utilice el prop icon para establecer el icono de FileUpload. Defaults a i-lucide-upload.

Drop your image here
SVG, PNG, JPG or GIF (max. 2MB)
<template>
  <UFileUpload
    icon="i-lucide-image"
    label="Drop your image here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    class="w-96 min-h-48"
  />
</template>
Puede personalizar este icono de forma global en su app.config.ts bajo la tecla ui.icons.upload.
Puede personalizar este icono de forma global en su vite.config.ts bajo la tecla ui.icons.upload.

Color (Edición española)

Utilice el prop color para cambiar el color del archivo.

Drop your image here
SVG, PNG, JPG or GIF (max. 2MB)
<template>
  <UFileUpload
    color="neutral"
    highlight
    label="Drop your image here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    class="w-96 min-h-48"
  />
</template>
El prop highlight se utiliza aquí para mostrar el estado de enfoque. Se utiliza internamente cuando se produce un error de validación.

Variante en Español

Utilice el prop variant para cambiar la variante del FileUpload.

<template>
  <UFileUpload variant="button" />
</template>

Tamaño

Utilice el prop size para cambiar el tamaño del archivo.

Drop your image here
SVG, PNG, JPG or GIF (max. 2MB)
<template>
  <UFileUpload
    size="xl"
    variant="area"
    label="Drop your image here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
  />
</template>

Diseño

Utilice el prop layout para cambiar la forma en que se muestran los archivos en el FileUpload. Defaults a grid.

Este accesorio solo funciona cuando variant es area.
Drop your images here
SVG, PNG, JPG or GIF (max. 2MB)
<template>
  <UFileUpload
    layout="list"
    multiple
    label="Drop your images here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    class="w-96"
    :ui="{
      base: 'min-h-48'
    }"
  />
</template>

Posición

Utilice el prop position para cambiar la posición de los archivos en el FileUpload. Defaults a outside.

Este prop sólo funciona cuando variant es area y cuando layout es list.
Drop your images here
SVG, PNG, JPG or GIF (max. 2MB)
<template>
  <UFileUpload
    position="inside"
    layout="list"
    multiple
    label="Drop your images here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    class="w-96"
    :ui="{
      base: 'min-h-48'
    }"
  />
</template>

Ejemplos

Con validación de formulario

Puede utilizar FileUpload dentro de los componentes Form y FormField para gestionar la validación y el manejo de errores.

JPG, GIF or PNG. 2MB Max.

<script setup lang="ts">
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'

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({
  image: 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>>({
  image: undefined
})

async function onSubmit(event: FormSubmitEvent<Schema>) {
  console.log(event.data)
}
</script>

<template>
  <UForm :schema="schema" :state="state" class="space-y-4 w-96" @submit="onSubmit">
    <UFormField name="image" label="Image" description="JPG, GIF or PNG. 2MB Max.">
      <UFileUpload v-model="state.image" accept="image/*" class="min-h-48" />
    </UFormField>

    <UButton type="submit" label="Submit" color="neutral" />
  </UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'

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({
  image: 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>>({
  image: undefined
})

async function onSubmit(event: FormSubmitEvent<Schema>) {
  console.log(event.data)
}
</script>

<template>
  <UForm :schema="schema" :state="state" class="space-y-4 w-96" @submit="onSubmit">
    <UFormField name="image" label="Image" description="JPG, GIF or PNG. 2MB Max.">
      <UFileUpload v-model="state.image" accept="image/*" class="min-h-48" />
    </UFormField>

    <UButton type="submit" label="Submit" color="neutral" />
  </UForm>
</template>

Con ranura por defecto

Puede utilizar la ranura predeterminada para crear su propio componente FileUpload.

JPG, GIF or PNG. 1MB Max.

<script setup lang="ts">
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'

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
})

function createObjectUrl(file: File): string {
  return URL.createObjectURL(file)
}

async function onSubmit(event: FormSubmitEvent<Schema>) {
  console.log(event.data)
}
</script>

<template>
  <UForm :schema="schema" :state="state" class="space-y-4 w-64" @submit="onSubmit">
    <UFormField name="avatar" label="Avatar" description="JPG, GIF or PNG. 1MB Max.">
      <UFileUpload v-slot="{ open, removeFile }" v-model="state.avatar" accept="image/*">
        <div class="flex flex-wrap items-center gap-3">
          <UAvatar
            size="lg"
            :src="state.avatar ? createObjectUrl(state.avatar) : undefined"
            icon="i-lucide-image"
          />

          <UButton
            :label="state.avatar ? 'Change image' : 'Upload image'"
            color="neutral"
            variant="outline"
            @click="open()"
          />
        </div>

        <p v-if="state.avatar" class="text-xs text-muted mt-1.5">
          {{ state.avatar.name }}

          <UButton
            label="Remove"
            color="error"
            variant="link"
            size="xs"
            class="p-0"
            @click="removeFile()"
          />
        </p>
      </UFileUpload>
    </UFormField>

    <UButton type="submit" label="Submit" color="neutral" />
  </UForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import * as z from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'

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
})

function createObjectUrl(file: File): string {
  return URL.createObjectURL(file)
}

async function onSubmit(event: FormSubmitEvent<Schema>) {
  console.log(event.data)
}
</script>

<template>
  <UForm :schema="schema" :state="state" class="space-y-4 w-64" @submit="onSubmit">
    <UFormField name="avatar" label="Avatar" description="JPG, GIF or PNG. 1MB Max.">
      <UFileUpload v-slot="{ open, removeFile }" v-model="state.avatar" accept="image/*">
        <div class="flex flex-wrap items-center gap-3">
          <UAvatar
            size="lg"
            :src="state.avatar ? createObjectUrl(state.avatar) : undefined"
            icon="i-lucide-image"
          />

          <UButton
            :label="state.avatar ? 'Change image' : 'Upload image'"
            color="neutral"
            variant="outline"
            @click="open()"
          />
        </div>

        <p v-if="state.avatar" class="text-xs text-muted mt-1.5">
          {{ state.avatar.name }}

          <UButton
            label="Remove"
            color="error"
            variant="link"
            size="xs"
            class="p-0"
            @click="removeFile()"
          />
        </p>
      </UFileUpload>
    </UFormField>

    <UButton type="submit" label="Submit" color="neutral" />
  </UForm>
</template>

With ranura inferior de archivos

Puede utilizar la ranura files-bottom para agregar un Button debajo de la lista de archivos para eliminar todos los archivos, por ejemplo.

Drop your images here
SVG, PNG, JPG or GIF (max. 2MB)
<script setup lang="ts">
const value = ref<File[]>([])
</script>

<template>
  <UFileUpload
    v-model="value"
    icon="i-lucide-image"
    label="Drop your images here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    layout="list"
    multiple
    :interactive="false"
    class="w-96 min-h-48"
  >
    <template #actions="{ open }">
      <UButton
        label="Select images"
        icon="i-lucide-upload"
        color="neutral"
        variant="outline"
        @click="open()"
      />
    </template>

    <template #files-bottom="{ removeFile, files }">
      <UButton
        v-if="files?.length"
        label="Remove all files"
        color="neutral"
        @click="removeFile()"
      />
    </template>
  </UFileUpload>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const value = ref<File[]>([])
</script>

<template>
  <UFileUpload
    v-model="value"
    icon="i-lucide-image"
    label="Drop your images here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    layout="list"
    multiple
    :interactive="false"
    class="w-96 min-h-48"
  >
    <template #actions="{ open }">
      <UButton
        label="Select images"
        icon="i-lucide-upload"
        color="neutral"
        variant="outline"
        @click="open()"
      />
    </template>

    <template #files-bottom="{ removeFile, files }">
      <UButton
        v-if="files?.length"
        label="Remove all files"
        color="neutral"
        @click="removeFile()"
      />
    </template>
  </UFileUpload>
</template>
El prop interactive se establece en false en este ejemplo para evitar el área de clic predeterminada.

With Files-Top Slot (Edición española)

Puede usar la ranura files-top para agregar un Button encima de la lista de archivos para agregar nuevos archivos, por ejemplo.

Drop your images here
SVG, PNG, JPG or GIF (max. 2MB)
<script setup lang="ts">
const value = ref<File[]>([])
</script>

<template>
  <UFileUpload
    v-model="value"
    icon="i-lucide-image"
    label="Drop your images here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    layout="grid"
    multiple
    :interactive="false"
    class="w-96 min-h-48"
  >
    <template #actions="{ open }">
      <UButton
        label="Select images"
        icon="i-lucide-upload"
        color="neutral"
        variant="outline"
        @click="open()"
      />
    </template>

    <template #files-top="{ open, files }">
      <div v-if="files?.length" class="mb-2 flex items-center justify-between">
        <p class="font-bold">Files ({{ files?.length }})</p>

        <UButton
          icon="i-lucide-plus"
          label="Add more"
          color="neutral"
          variant="outline"
          class="-my-2"
          @click="open()"
        />
      </div>
    </template>
  </UFileUpload>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const value = ref<File[]>([])
</script>

<template>
  <UFileUpload
    v-model="value"
    icon="i-lucide-image"
    label="Drop your images here"
    description="SVG, PNG, JPG or GIF (max. 2MB)"
    layout="grid"
    multiple
    :interactive="false"
    class="w-96 min-h-48"
  >
    <template #actions="{ open }">
      <UButton
        label="Select images"
        icon="i-lucide-upload"
        color="neutral"
        variant="outline"
        @click="open()"
      />
    </template>

    <template #files-top="{ open, files }">
      <div v-if="files?.length" class="mb-2 flex items-center justify-between">
        <p class="font-bold">Files ({{ files?.length }})</p>

        <UButton
          icon="i-lucide-plus"
          label="Add more"
          color="neutral"
          variant="outline"
          class="-my-2"
          @click="open()"
        />
      </div>
    </template>
  </UFileUpload>
</template>

API (Versión)

Props (accesorios)

Prop Default Type
as
'div'
any

The element or component this component should render as.

id
string
name
string
icon
appConfig.ui.icons.upload
any

The icon to display. Set to false to hide the icon.

label
string
description
string
color
'primary'
"primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"
variant
'area'
"button" | "area"

The button variant is only available when multiple is false.

size
'md'
"xs" | "sm" | "md" | "lg" | "xl"
layout
'grid'
"list" | "grid"

The layout of how files are displayed. Only works when variant is area.

position
'outside'
"inside" | "outside"

The position of the files. Only works when variant is area and when layout is list.

highlight
boolean

Highlight the ring color like a focus state.

accept
'*'
string

Specifies the allowed file types for the input. Provide a comma-separated list of MIME types or file extensions (e.g., "image/png,application/pdf,.jpg").

multiple
false
M
reset
false
boolean

Reset the file input when the dialog is opened.

dropzone
true
boolean

Create a zone that allows the user to drop files onto it.

interactive
true
boolean

Make the dropzone interactive when the user is clicking on it.

required
boolean
disabled
boolean
fileIcon
appConfig.ui.icons.file
any

The icon to display for the file.

fileImage
true
boolean

Preview the file (currently only <img> is rendered) When set false, only fileIcon is displayed

fileDelete
true
boolean | Omit<ButtonProps, LinkPropsKeys>

Configure the delete button for the file. When layout is grid, the default is { color: 'neutral', variant: 'solid', size: 'xs' } When layout is list, the default is { color: 'neutral', variant: 'link' }

fileDeleteIcon
appConfig.ui.icons.close
any

The icon displayed to delete a file.

preview
true
boolean

Show the file preview/list after upload.

modelValue
null | M extends true ? File[] : File
ui
{ root?: SlotClass; base?: SlotClass; wrapper?: SlotClass; icon?: SlotClass; avatar?: SlotClass; label?: SlotClass; description?: SlotClass; actions?: SlotClass; files?: SlotClass; file?: SlotClass; fileLeadingAvatar?: SlotClass; fileWrapper?: SlotClass; fileName?: SlotClass; fileSize?: SlotClass; fileTrailingButton?: SlotClass; }
Este componente también admite todos los atributos HTML nativos de <input>.

Slots en línea

Slot Type
default
{ open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; ui: object; }
leading
{ ui: object; }
label
{}
description
{}
actions
{ files: FileUploadFiles<M> | undefined; open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; }
files
{ files: FileUploadFiles<M>; removeFile: (index?: number | undefined) => void; }
files-top
{ files: FileUploadFiles<M>; open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; }
files-bottom
{ files: FileUploadFiles<M>; open: (localOptions?: Partial<UseFileDialogOptions> | undefined) => void; removeFile: (index?: number | undefined) => void; }
file
{ file: File; index: number; removeFile: (index?: number | undefined) => void; }
file-leading
{ file: File; index: number; ui: object; }
file-name
{ file: File; index: number; }
file-size
{ file: File; index: number; }
file-trailing
{ file: File; index: number; ui: object; }

Emisiones

Event Type
change
[event: Event]
update:modelValue
[value: (M extends true ? File[] : File) | null | undefined]

Exposición

Al acceder al componente a través de una referencia de plantilla, puede utilizar lo siguiente:

NombreTipo
inputRefx{lang="ts-type"}Ref<HTMLInputElement | null>x{lang="ts-type"} (Edición española)
dropzoneRef (Edición española)Ref<HTMLDivElement | null>x{lang="ts-type"} (Edición española)

Temas

app.config.ts
export default defineAppConfig({
  ui: {
    fileUpload: {
      slots: {
        root: 'relative flex flex-col',
        base: [
          'w-full flex-1 bg-default border border-default flex flex-col gap-2 items-stretch justify-center rounded-lg focus-visible:outline-3',
          'transition-[background] ease-out'
        ],
        wrapper: 'flex flex-col items-center justify-center text-center',
        icon: 'shrink-0',
        avatar: 'shrink-0',
        label: 'font-medium text-default mt-2',
        description: 'text-muted mt-1',
        actions: 'flex flex-wrap gap-1.5 shrink-0 mt-4',
        files: '',
        file: 'relative',
        fileLeadingAvatar: 'shrink-0',
        fileWrapper: 'flex flex-col min-w-0',
        fileName: 'text-default truncate',
        fileSize: 'text-muted truncate',
        fileTrailingButton: ''
      },
      variants: {
        color: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        variant: {
          area: {
            wrapper: 'px-4 py-3',
            base: 'p-4'
          },
          button: {}
        },
        size: {
          xs: {
            base: 'text-xs',
            icon: 'size-4',
            file: 'text-xs px-2 py-1 gap-1',
            fileWrapper: 'flex-row gap-1'
          },
          sm: {
            base: 'text-xs',
            icon: 'size-4',
            file: 'text-xs px-2.5 py-1.5 gap-1.5',
            fileWrapper: 'flex-row gap-1'
          },
          md: {
            base: 'text-sm',
            icon: 'size-5',
            file: 'text-xs px-2.5 py-1.5 gap-1.5'
          },
          lg: {
            base: 'text-sm',
            icon: 'size-5',
            file: 'text-sm px-3 py-2 gap-2',
            fileSize: 'text-xs'
          },
          xl: {
            base: 'text-base',
            icon: 'size-6',
            file: 'text-sm px-3 py-2 gap-2'
          }
        },
        layout: {
          list: {
            root: 'gap-2 items-start',
            files: 'flex flex-col w-full gap-2',
            file: 'min-w-0 flex items-center border border-default rounded-md w-full',
            fileTrailingButton: 'ms-auto'
          },
          grid: {
            fileWrapper: 'hidden',
            fileLeadingAvatar: 'size-full rounded-lg',
            fileTrailingButton: 'absolute -top-1.5 -end-1.5 p-0 rounded-full border-2 border-bg'
          }
        },
        position: {
          inside: '',
          outside: ''
        },
        dropzone: {
          true: 'border-dashed data-[dragging=true]:bg-elevated/25'
        },
        interactive: {
          true: ''
        },
        highlight: {
          true: ''
        },
        multiple: {
          true: ''
        },
        disabled: {
          true: 'cursor-not-allowed opacity-75'
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          class: 'outline-primary/25 focus-visible:outline-3 focus-visible:border-primary'
        },
        {
          color: 'primary',
          highlight: true,
          class: 'border-primary'
        },
        {
          color: 'neutral',
          class: 'outline-inverted/25 focus-visible:outline-3 focus-visible:border-inverted'
        },
        {
          color: 'neutral',
          highlight: true,
          class: 'border-inverted'
        },
        {
          size: 'xs',
          layout: 'list',
          class: {
            fileTrailingButton: '-me-1'
          }
        },
        {
          size: 'sm',
          layout: 'list',
          class: {
            fileTrailingButton: '-me-1.5'
          }
        },
        {
          size: 'md',
          layout: 'list',
          class: {
            fileTrailingButton: '-me-1.5'
          }
        },
        {
          size: 'lg',
          layout: 'list',
          class: {
            fileTrailingButton: '-me-2'
          }
        },
        {
          size: 'xl',
          layout: 'list',
          class: {
            fileTrailingButton: '-me-2'
          }
        },
        {
          variant: 'button',
          size: 'xs',
          class: {
            base: 'p-1'
          }
        },
        {
          variant: 'button',
          size: 'sm',
          class: {
            base: 'p-1.5'
          }
        },
        {
          variant: 'button',
          size: 'md',
          class: {
            base: 'p-1.5'
          }
        },
        {
          variant: 'button',
          size: 'lg',
          class: {
            base: 'p-2'
          }
        },
        {
          variant: 'button',
          size: 'xl',
          class: {
            base: 'p-2'
          }
        },
        {
          layout: 'grid',
          multiple: true,
          class: {
            files: 'grid grid-cols-2 md:grid-cols-3 gap-4 w-full',
            file: 'p-0 aspect-square'
          }
        },
        {
          layout: 'grid',
          multiple: false,
          class: {
            file: 'absolute inset-0 p-0'
          }
        },
        {
          interactive: true,
          disabled: false,
          class: 'hover:bg-elevated/25'
        }
      ],
      defaultVariants: {
        color: 'primary',
        variant: 'area',
        size: 'md'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        fileUpload: {
          slots: {
            root: 'relative flex flex-col',
            base: [
              'w-full flex-1 bg-default border border-default flex flex-col gap-2 items-stretch justify-center rounded-lg focus-visible:outline-3',
              'transition-[background] ease-out'
            ],
            wrapper: 'flex flex-col items-center justify-center text-center',
            icon: 'shrink-0',
            avatar: 'shrink-0',
            label: 'font-medium text-default mt-2',
            description: 'text-muted mt-1',
            actions: 'flex flex-wrap gap-1.5 shrink-0 mt-4',
            files: '',
            file: 'relative',
            fileLeadingAvatar: 'shrink-0',
            fileWrapper: 'flex flex-col min-w-0',
            fileName: 'text-default truncate',
            fileSize: 'text-muted truncate',
            fileTrailingButton: ''
          },
          variants: {
            color: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            variant: {
              area: {
                wrapper: 'px-4 py-3',
                base: 'p-4'
              },
              button: {}
            },
            size: {
              xs: {
                base: 'text-xs',
                icon: 'size-4',
                file: 'text-xs px-2 py-1 gap-1',
                fileWrapper: 'flex-row gap-1'
              },
              sm: {
                base: 'text-xs',
                icon: 'size-4',
                file: 'text-xs px-2.5 py-1.5 gap-1.5',
                fileWrapper: 'flex-row gap-1'
              },
              md: {
                base: 'text-sm',
                icon: 'size-5',
                file: 'text-xs px-2.5 py-1.5 gap-1.5'
              },
              lg: {
                base: 'text-sm',
                icon: 'size-5',
                file: 'text-sm px-3 py-2 gap-2',
                fileSize: 'text-xs'
              },
              xl: {
                base: 'text-base',
                icon: 'size-6',
                file: 'text-sm px-3 py-2 gap-2'
              }
            },
            layout: {
              list: {
                root: 'gap-2 items-start',
                files: 'flex flex-col w-full gap-2',
                file: 'min-w-0 flex items-center border border-default rounded-md w-full',
                fileTrailingButton: 'ms-auto'
              },
              grid: {
                fileWrapper: 'hidden',
                fileLeadingAvatar: 'size-full rounded-lg',
                fileTrailingButton: 'absolute -top-1.5 -end-1.5 p-0 rounded-full border-2 border-bg'
              }
            },
            position: {
              inside: '',
              outside: ''
            },
            dropzone: {
              true: 'border-dashed data-[dragging=true]:bg-elevated/25'
            },
            interactive: {
              true: ''
            },
            highlight: {
              true: ''
            },
            multiple: {
              true: ''
            },
            disabled: {
              true: 'cursor-not-allowed opacity-75'
            }
          },
          compoundVariants: [
            {
              color: 'primary',
              class: 'outline-primary/25 focus-visible:outline-3 focus-visible:border-primary'
            },
            {
              color: 'primary',
              highlight: true,
              class: 'border-primary'
            },
            {
              color: 'neutral',
              class: 'outline-inverted/25 focus-visible:outline-3 focus-visible:border-inverted'
            },
            {
              color: 'neutral',
              highlight: true,
              class: 'border-inverted'
            },
            {
              size: 'xs',
              layout: 'list',
              class: {
                fileTrailingButton: '-me-1'
              }
            },
            {
              size: 'sm',
              layout: 'list',
              class: {
                fileTrailingButton: '-me-1.5'
              }
            },
            {
              size: 'md',
              layout: 'list',
              class: {
                fileTrailingButton: '-me-1.5'
              }
            },
            {
              size: 'lg',
              layout: 'list',
              class: {
                fileTrailingButton: '-me-2'
              }
            },
            {
              size: 'xl',
              layout: 'list',
              class: {
                fileTrailingButton: '-me-2'
              }
            },
            {
              variant: 'button',
              size: 'xs',
              class: {
                base: 'p-1'
              }
            },
            {
              variant: 'button',
              size: 'sm',
              class: {
                base: 'p-1.5'
              }
            },
            {
              variant: 'button',
              size: 'md',
              class: {
                base: 'p-1.5'
              }
            },
            {
              variant: 'button',
              size: 'lg',
              class: {
                base: 'p-2'
              }
            },
            {
              variant: 'button',
              size: 'xl',
              class: {
                base: 'p-2'
              }
            },
            {
              layout: 'grid',
              multiple: true,
              class: {
                files: 'grid grid-cols-2 md:grid-cols-3 gap-4 w-full',
                file: 'p-0 aspect-square'
              }
            },
            {
              layout: 'grid',
              multiple: false,
              class: {
                file: 'absolute inset-0 p-0'
              }
            },
            {
              interactive: true,
              disabled: false,
              class: 'hover:bg-elevated/25'
            }
          ],
          defaultVariants: {
            color: 'primary',
            variant: 'area',
            size: 'md'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.

Changelog (Edición española)

No recent changes