文件上传
用法
使用 v-model
指令来控制文件上传组件的值。
<script setup lang="ts">
const value = ref(null)
</script>
<template>
<UFileUpload v-model="value" class="w-96 min-h-48" />
</template>
多选
使用 multiple
属性以允许多选文件。
<template>
<UFileUpload multiple class="w-96 min-h-48" />
</template>
拖放区
使用 dropzone
属性来启用/禁用拖放区域。默认为 true
。
<template>
<UFileUpload :dropzone="false" class="w-96 min-h-48" />
</template>
交互式
使用 interactive
属性来启用/禁用可点击区域。默认为 true
。
<template>
<UFileUpload :interactive="false" class="w-96 min-h-48" />
</template>
接受
使用 accept
属性指定输入允许的文件类型。提供一个逗号分隔的列表,包含MIME 类型或文件扩展名(例如,image/png,application/pdf,.jpg
)。默认为 *
(所有文件类型)。
<template>
<UFileUpload accept="image/*" class="w-96 min-h-48" />
</template>
标签
使用 label
属性来设置文件上传组件的标签。
<template>
<UFileUpload label="Drop your image here" class="w-96 min-h-48" />
</template>
描述
使用 description
属性来设置文件上传组件的描述。
<template>
<UFileUpload
label="Drop your image here"
description="SVG, PNG, JPG or GIF (max. 2MB)"
class="w-96 min-h-48"
/>
</template>
Icon
使用 icon
属性来设置文件上传组件的图标。默认为 i-lucide-upload
。
<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>
颜色
使用 color
属性来更改文件上传组件的颜色。
<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>
highlight
属性在此用于显示焦点状态。当发生验证错误时,它会在内部使用。变体
使用 variant
属性来更改文件上传组件的变体。
<template>
<UFileUpload variant="button" />
</template>
尺寸
使用 size
属性来更改文件上传组件的大小。
<template>
<UFileUpload
size="xl"
variant="area"
label="Drop your image here"
description="SVG, PNG, JPG or GIF (max. 2MB)"
/>
</template>
布局
使用 layout
属性来更改文件上传组件中文件的显示方式。默认为 grid
。
variant
为 area
时才有效。<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>
位置
使用 position
属性来更改文件上传组件中文件的位置。默认为 outside
。
variant
为 area
且 layout
为 list
时才有效。<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>
示例
带表单验证
您可以在 Form 和 FormField 组件中使用文件上传组件来处理验证和错误。
<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 * 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>
带 files-bottom 插槽
您可以使用 files-bottom
插槽在文件列表下方添加一个 Button 按钮,例如用于移除所有文件。
<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>
带 files-top 插槽
您可以使用 files-top
插槽在文件列表上方添加一个 Button 按钮,例如用于添加新文件。
<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>
API
属性
属性 | 默认值 | 类型 |
---|---|---|
as |
|
此组件应渲染为的元素或组件。 |
id |
| |
name |
| |
图标 |
|
要显示的图标。 |
label |
| |
description |
| |
color |
|
|
variant |
|
|
尺寸 |
|
|
layout |
|
文件显示布局。仅当 |
位置 |
|
文件的位置。仅当 |
高亮 |
高亮环形颜色,如同焦点状态。 | |
accept |
|
指定输入允许的文件类型。提供一个逗号分隔的 MIME 类型或文件扩展名列表(例如,“image/png,application/pdf,.jpg”)。 |
multiple |
|
|
reset |
|
当对话框打开时重置文件输入。 |
dropzone |
|
创建一个允许用户将文件拖放到其上的区域。 |
interactive |
|
当用户点击拖放区时,使其具有交互性。 |
required |
| |
disabled |
| |
fileIcon |
|
要为文件显示的图标。 |
fileDelete |
配置文件的删除按钮。当 | |
fileDeleteIcon |
|
用于删除文件的图标。 |
modelValue |
| |
ui |
|
插槽
插槽 | 类型 |
---|---|
default |
|
前置 |
|
label |
|
description |
|
actions |
|
files |
|
files-top |
|
files-bottom |
|
file |
|
file-leading |
|
file-name |
|
file-size |
|
file-trailing |
|
事件
事件 | 类型 |
---|---|
change |
|
update:modelValue |
|
update:modelValue |
|
可访问属性
通过模板引用访问组件时,您可以使用以下内容:
名称 | 类型 |
---|---|
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: '',
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 '@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-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: '',
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 uiPro from '@nuxt/ui-pro/vite'
export default defineConfig({
plugins: [
vue(),
uiPro({
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: '',
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'
}
}
}
})
]
})