Use a diretiva v-model para controlar o valor do FileUpload.
<script setup lang="ts">
const value = ref(null)
</script>
<template>
<NFileUpload v-model="value" class="w-96 min-h-48" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref(null)
</script>
<template>
<NFileUpload v-model="value" class="w-96 min-h-48" />
</template>
Use a prop multiple para permitir que vários arquivos sejam selecionados.
<template>
<NFileUpload multiple class="w-96 min-h-48" />
</template>
Use a prop dropzone para habilitar/desabilitar a área de soltar. O padrão é true.
<template>
<NFileUpload :dropzone="false" class="w-96 min-h-48" />
</template>
Use a prop interactive para habilitar/desabilitar a área clicável. O padrão é true.
<template>
<NFileUpload :interactive="false" class="w-96 min-h-48" />
</template>
Use a prop accept para especificar os tipos de arquivo permitidos no input. Forneça uma lista separada por vírgulas de tipos MIME ou extensões de arquivo (ex.: image/png,application/pdf,.jpg). O padrão é * (todos os tipos de arquivo).
<template>
<NFileUpload accept="image/*" class="w-96 min-h-48" />
</template>
Use a prop label para definir o rótulo do FileUpload.
<template>
<NFileUpload label="Drop your image here" class="w-96 min-h-48" />
</template>
Use a prop description para definir a descrição do FileUpload.
<template>
<NFileUpload
label="Drop your image here"
description="SVG, PNG, JPG or GIF (max. 2MB)"
class="w-96 min-h-48"
/>
</template>
Use a prop icon para definir o ícone do FileUpload. O padrão é i-lucide-upload.
<template>
<NFileUpload
icon="i-lucide-image"
label="Drop your image here"
description="SVG, PNG, JPG or GIF (max. 2MB)"
class="w-96 min-h-48"
/>
</template>
Use a prop color para alterar a cor do FileUpload.
<template>
<NFileUpload
color="neutral"
highlight
label="Drop your image here"
description="SVG, PNG, JPG or GIF (max. 2MB)"
class="w-96 min-h-48"
/>
</template>
highlight é usada aqui para mostrar o estado de foco. Ela é usada internamente quando ocorre um erro de validação.Use a prop variant para alterar a variante do FileUpload.
<template>
<NFileUpload variant="button" />
</template>
Use a prop size para alterar o tamanho do FileUpload.
<template>
<NFileUpload
size="xl"
variant="area"
label="Drop your image here"
description="SVG, PNG, JPG or GIF (max. 2MB)"
/>
</template>
Use a prop layout para alterar como os arquivos são exibidos no FileUpload. O padrão é grid.
variant é area.<template>
<NFileUpload
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>
Use a prop position para alterar a posição dos arquivos no FileUpload. O padrão é outside.
variant é area e quando layout é list.<template>
<NFileUpload
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>
Você pode usar o FileUpload dentro dos componentes Form e FormField para lidar com validação e tratamento de erros.
Você pode usar o slot padrão para criar o seu próprio componente FileUpload.
Você pode usar o slot files-bottom para adicionar um Button abaixo da lista de arquivos para remover todos os arquivos, por exemplo.
Você pode usar o slot files-top para adicionar um Button acima da lista de arquivos para adicionar novos arquivos, por exemplo.
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
id | string | |
name | string | |
icon | appConfig.ui.icons.upload | anyThe icon to display. Set to |
label | string | |
description | string | |
color | 'primary' | "primary" | "secondary" | "accent" | "success" | "info" | "warning" | "error" | "neutral" |
variant | 'area' | "button" | "area"The |
size | 'md' | "xs" | "sm" | "md" | "lg" | "xl" |
layout | 'grid' | "list" | "grid"The layout of how files are displayed.
Only works when |
position | 'outside' | "inside" | "outside"The position of the files.
Only works when |
highlight | boolean Highlight the ring color like a focus state. | |
accept | '*' | stringSpecifies 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 | anyThe icon to display for the file. |
fileImage | true | boolean Preview the file (currently only |
fileDelete | true | boolean | Omit<ButtonProps, LinkPropsKeys> Configure the delete button for the file.
When |
fileDeleteIcon | appConfig.ui.icons.close | anyThe 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; } |
| 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; } |
| Event | Type |
|---|---|
change | [event: Event] |
update:modelValue | [value: (M extends true ? File[] : File) | null | undefined] |
Ao acessar o componente por meio de um template ref, você pode usar o seguinte:
| Name | Type |
|---|---|
inputRef | Ref<HTMLInputElement | null> |
dropzoneRef | Ref<HTMLDivElement | null> |
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-2',
'transition-[background]'
],
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: '',
accent: '',
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: 'focus-visible:outline-primary'
},
{
color: 'primary',
highlight: true,
class: 'border-primary'
},
{
color: 'neutral',
class: 'focus-visible:outline-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'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/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-2',
'transition-[background]'
],
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: '',
accent: '',
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: 'focus-visible:outline-primary'
},
{
color: 'primary',
highlight: true,
class: 'border-primary'
},
{
color: 'neutral',
class: 'focus-visible:outline-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'
}
}
}
})
]
})