在抽屉的默认插槽中使用按钮或任何其他组件。
然后,使用#content插槽添加抽屉打开时显示的内容。
<template>
<UDrawer>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</UDrawer>
</template>
您还可以使用#header、#body和#footer插槽来自定义抽屉的内容。
使用title属性设置抽屉标题。
<template>
<UDrawer title="Drawer with title">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #body>
<Placeholder class="h-48" />
</template>
</UDrawer>
</template>
使用description属性设置抽屉标题的描述。
<template>
<UDrawer
title="Drawer with description"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #body>
<Placeholder class="h-48" />
</template>
</UDrawer>
</template>
使用direction属性控制抽屉的方向。默认为bottom。
<template>
<UDrawer direction="right">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="min-w-96 min-h-96 size-full m-4" />
</template>
</UDrawer>
</template>
使用inset属性将抽屉从边缘内嵌。
<template>
<UDrawer direction="right" inset>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="min-w-96 min-h-96 size-full m-4" />
</template>
</UDrawer>
</template>
使用handle属性控制抽屉是否具有把手。默认为true。
<template>
<UDrawer :handle="false">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</UDrawer>
</template>
使用handle-only属性仅允许通过把手拖动抽屉。
<template>
<UDrawer handle-only>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</UDrawer>
</template>
使用overlay属性控制抽屉是否具有叠加层。默认为true。
<template>
<UDrawer :overlay="false">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</UDrawer>
</template>
使用should-scale-background属性在抽屉打开时缩放背景,创建视觉深度效果。您可以将set-background-color-on-scale属性设置为false以防止更改背景颜色。
<template>
<UDrawer should-scale-background set-background-color-on-scale>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</UDrawer>
</template>
data-vaul-drawer-wrapper指令添加到应用程序的父元素中以使其生效。<template>
<UApp>
<div class="bg-default" data-vaul-drawer-wrapper>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</UApp>
</template>
export default defineNuxtConfig({
app: {
rootAttrs: {
'data-vaul-drawer-wrapper': '',
'class': 'bg-default'
}
}
})
您可以通过使用default-open prop 或v-model:open指令来控制打开状态。
<script setup lang="ts">
const open = ref(false)
defineShortcuts({
o: () => (open.value = !open.value)
})
</script>
<template>
<UDrawer v-model:open="open">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</UDrawer>
</template>
defineShortcuts,您可以通过按O键来切换抽屉。将dismissible属性设置为false以防止在点击抽屉外部或按Esc键时关闭抽屉。当用户尝试关闭抽屉时,将发出close:prevent事件。
<script setup lang="ts">
const open = ref(false)
</script>
<template>
<UDrawer
v-model:open="open"
:dismissible="false"
:handle="false"
:ui="{ header: 'flex items-center justify-between' }"
>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #header>
<h2 class="text-highlighted font-semibold">Drawer non-dismissible</h2>
<UButton color="neutral" variant="ghost" icon="i-lucide-x" @click="open = false" />
</template>
<template #body>
<Placeholder class="h-48" />
</template>
</UDrawer>
</template>
header插槽用于添加关闭按钮,这在默认情况下是不会添加的。将overlay和modal属性设置为false,同时使用dismissible属性,使抽屉的背景具有交互性,而不会关闭抽屉。
<script setup lang="ts">
const open = ref(false)
</script>
<template>
<UDrawer
v-model:open="open"
:dismissible="false"
:overlay="false"
:handle="false"
:modal="false"
:ui="{ header: 'flex items-center justify-between' }"
>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #header>
<h2 class="text-highlighted font-semibold">Drawer non-dismissible</h2>
<UButton color="neutral" variant="ghost" icon="i-lucide-x" @click="open = false" />
</template>
<template #body>
<Placeholder class="h-48" />
</template>
</UDrawer>
</template>
例如,您可以在桌面设备上渲染模态组件,在移动设备上渲染抽屉组件。
<script lang="ts" setup>
import { createReusableTemplate, useMediaQuery } from '@vueuse/core'
const [DefineFormTemplate, ReuseFormTemplate] = createReusableTemplate()
const isDesktop = useMediaQuery('(min-width: 768px)')
const open = ref(false)
const state = reactive({
email: undefined
})
const title = 'Edit profile'
const description = "Make changes to your profile here. Click save when you're done."
</script>
<template>
<DefineFormTemplate>
<UForm :state="state" class="space-y-4">
<UFormField label="Email" name="email" required>
<UInput v-model="state.email" placeholder="shadcn@example.com" required />
</UFormField>
<UButton label="Save changes" type="submit" />
</UForm>
</DefineFormTemplate>
<UModal v-if="isDesktop" v-model:open="open" :title="title" :description="description">
<UButton label="Edit profile" color="neutral" variant="outline" />
<template #body>
<ReuseFormTemplate />
</template>
</UModal>
<UDrawer v-else v-model:open="open" :title="title" :description="description">
<UButton label="Edit profile" color="neutral" variant="outline" />
<template #body>
<ReuseFormTemplate />
</template>
</UDrawer>
</template>
您可以通过使用nested属性将抽屉相互嵌套。
<template>
<UDrawer :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #footer>
<UDrawer nested :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
<UButton color="neutral" variant="outline" label="Open nested" />
<template #content>
<Placeholder class="flex-1 m-4" />
</template>
</UDrawer>
</template>
</UDrawer>
</template>
使用#footer插槽在抽屉主体之后添加内容。
<script setup lang="ts">
const open = ref(false)
</script>
<template>
<UDrawer
v-model:open="open"
title="Drawer with footer"
description="This is useful when you want a form in a Drawer."
:ui="{ container: 'max-w-xl mx-auto' }"
>
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #body>
<Placeholder class="h-48" />
</template>
<template #footer>
<UButton label="Submit" color="neutral" class="justify-center" />
<UButton
label="Cancel"
color="neutral"
variant="outline"
class="justify-center"
@click="open = false"
/>
</template>
</UDrawer>
</template>
您可以在抽屉内容中使用命令面板组件。
<script setup lang="ts">
const searchTerm = ref('')
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
key: 'command-palette-users',
params: { q: searchTerm },
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
},
lazy: true
})
const groups = computed(() => [{
id: 'users',
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
items: users.value || [],
ignoreFilter: true
}])
</script>
<template>
<UDrawer :handle="false">
<UButton
label="Search users..."
color="neutral"
variant="subtle"
icon="i-lucide-search"
/>
<template #content>
<UCommandPalette
v-model:search-term="searchTerm"
:loading="status === 'pending'"
:groups="groups"
placeholder="Search users..."
class="h-80"
/>
</template>
</UDrawer>
</template>
| 属性 | 默认值 | 类型 |
|---|---|---|
as |
|
此组件应渲染为的元素或组件。 |
title |
| |
description |
| |
内嵌 |
|
是否将抽屉内嵌到边缘。 |
内容 |
抽屉的内容。
| |
叠加层 |
|
在抽屉后面渲染一个覆盖层。 |
把手 |
|
在抽屉上渲染一个把手。 |
portal |
|
在门户中渲染抽屉。 |
nested |
|
抽屉是否嵌套在另一个抽屉中。 |
modal |
|
当 |
open |
| |
活动吸附点 |
| |
关闭阈值 |
一个介于 0 和 1 之间的数字,用于确定何时关闭抽屉。示例:阈值为 0.5 将在用户滑动抽屉高度的 50% 或更多时关闭抽屉。 | |
应缩放背景 |
| |
设置背景颜色缩放 |
当 | |
滚动锁定超时 |
在抽屉内滚动内容后,抽屉不可拖动的时间持续长度。 | |
固定 |
当 | |
可关闭的 |
|
当 |
defaultOpen |
默认打开,跳过初始进入动画。仍然响应 | |
方向 |
|
抽屉的方向。可以是 |
无主体样式 |
当 | |
仅把手 |
当 | |
阻止滚动恢复 |
| |
吸附点 |
从 0 到 100 的数字数组,对应于给定吸附点应占据屏幕的百分比。应从最小可见度开始。示例 | |
ui |
|
| 插槽 | 类型 |
|---|---|
default |
|
内容 |
|
页头 |
|
title |
|
description |
|
主体 |
|
页脚 |
|
| 事件 | 类型 |
|---|---|
update:open | |
close | |
拖动 |
|
close:prevent |
|
释放 | |
更新:活动吸附点 |
|
动画结束 |
|
export default defineAppConfig({
ui: {
drawer: {
slots: {
overlay: 'fixed inset-0 bg-elevated/75',
content: 'fixed bg-default ring ring-default flex focus:outline-none',
handle: [
'shrink-0 !bg-accented',
'transition-opacity'
],
container: 'w-full flex flex-col gap-4 p-4 overflow-y-auto',
header: '',
title: 'text-highlighted font-semibold',
description: 'mt-1 text-muted text-sm',
body: 'flex-1',
footer: 'flex flex-col gap-1.5'
},
variants: {
direction: {
top: {
content: 'mb-24 flex-col-reverse',
handle: 'mb-4'
},
right: {
content: 'flex-row',
handle: '!ml-4'
},
bottom: {
content: 'mt-24 flex-col',
handle: 'mt-4'
},
left: {
content: 'flex-row-reverse',
handle: '!mr-4'
}
},
inset: {
true: {
content: 'rounded-lg after:hidden overflow-hidden [--initial-transform:calc(100%+1.5rem)]'
}
},
snapPoints: {
true: ''
}
},
compoundVariants: [
{
direction: [
'top',
'bottom'
],
class: {
content: 'h-auto max-h-[96%]',
handle: '!w-12 !h-1.5 mx-auto'
}
},
{
direction: [
'top',
'bottom'
],
snapPoints: true,
class: {
content: 'h-full'
}
},
{
direction: [
'right',
'left'
],
class: {
content: 'w-auto max-w-[calc(100%-2rem)]',
handle: '!h-12 !w-1.5 mt-auto mb-auto'
}
},
{
direction: [
'right',
'left'
],
snapPoints: true,
class: {
content: 'w-full'
}
},
{
direction: 'top',
inset: true,
class: {
content: 'inset-x-4 top-4'
}
},
{
direction: 'top',
inset: false,
class: {
content: 'inset-x-0 top-0 rounded-b-lg'
}
},
{
direction: 'bottom',
inset: true,
class: {
content: 'inset-x-4 bottom-4'
}
},
{
direction: 'bottom',
inset: false,
class: {
content: 'inset-x-0 bottom-0 rounded-t-lg'
}
},
{
direction: 'left',
inset: true,
class: {
content: 'inset-y-4 left-4'
}
},
{
direction: 'left',
inset: false,
class: {
content: 'inset-y-0 left-0 rounded-r-lg'
}
},
{
direction: 'right',
inset: true,
class: {
content: 'inset-y-4 right-4'
}
},
{
direction: 'right',
inset: false,
class: {
content: 'inset-y-0 right-0 rounded-l-lg'
}
}
]
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
drawer: {
slots: {
overlay: 'fixed inset-0 bg-elevated/75',
content: 'fixed bg-default ring ring-default flex focus:outline-none',
handle: [
'shrink-0 !bg-accented',
'transition-opacity'
],
container: 'w-full flex flex-col gap-4 p-4 overflow-y-auto',
header: '',
title: 'text-highlighted font-semibold',
description: 'mt-1 text-muted text-sm',
body: 'flex-1',
footer: 'flex flex-col gap-1.5'
},
variants: {
direction: {
top: {
content: 'mb-24 flex-col-reverse',
handle: 'mb-4'
},
right: {
content: 'flex-row',
handle: '!ml-4'
},
bottom: {
content: 'mt-24 flex-col',
handle: 'mt-4'
},
left: {
content: 'flex-row-reverse',
handle: '!mr-4'
}
},
inset: {
true: {
content: 'rounded-lg after:hidden overflow-hidden [--initial-transform:calc(100%+1.5rem)]'
}
},
snapPoints: {
true: ''
}
},
compoundVariants: [
{
direction: [
'top',
'bottom'
],
class: {
content: 'h-auto max-h-[96%]',
handle: '!w-12 !h-1.5 mx-auto'
}
},
{
direction: [
'top',
'bottom'
],
snapPoints: true,
class: {
content: 'h-full'
}
},
{
direction: [
'right',
'left'
],
class: {
content: 'w-auto max-w-[calc(100%-2rem)]',
handle: '!h-12 !w-1.5 mt-auto mb-auto'
}
},
{
direction: [
'right',
'left'
],
snapPoints: true,
class: {
content: 'w-full'
}
},
{
direction: 'top',
inset: true,
class: {
content: 'inset-x-4 top-4'
}
},
{
direction: 'top',
inset: false,
class: {
content: 'inset-x-0 top-0 rounded-b-lg'
}
},
{
direction: 'bottom',
inset: true,
class: {
content: 'inset-x-4 bottom-4'
}
},
{
direction: 'bottom',
inset: false,
class: {
content: 'inset-x-0 bottom-0 rounded-t-lg'
}
},
{
direction: 'left',
inset: true,
class: {
content: 'inset-y-4 left-4'
}
},
{
direction: 'left',
inset: false,
class: {
content: 'inset-y-0 left-0 rounded-r-lg'
}
},
{
direction: 'right',
inset: true,
class: {
content: 'inset-y-4 right-4'
}
},
{
direction: 'right',
inset: false,
class: {
content: 'inset-y-0 right-0 rounded-l-lg'
}
}
]
}
}
})
]
})