Use a Button or any other component in the default slot of the Drawer.
Depois, use o slot #content para adicionar o conteúdo exibido quando o Drawer está aberto.
<template>
<NDrawer>
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</NDrawer>
</template>
Você também pode usar os slots #header, #body e #footer para personalizar o conteúdo do Drawer.
Use a prop title para definir o título do cabeçalho do Drawer.
<template>
<NDrawer title="Drawer with title">
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #body>
<Placeholder class="h-48" />
</template>
</NDrawer>
</template>
Use a prop description para definir a descrição do cabeçalho do Drawer.
<template>
<NDrawer
title="Drawer with description"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
>
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #body>
<Placeholder class="h-48" />
</template>
</NDrawer>
</template>
Use a prop close para exibir um botão de fechar no Drawer. O padrão é false.
Você pode passar qualquer propriedade do componente Button para personalizá-lo.
<template>
<NDrawer
title="Drawer with close button"
:close="{
color: 'primary',
variant: 'outline',
class: 'rounded-full'
}"
>
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #body>
<Placeholder class="h-48" />
</template>
</NDrawer>
</template>
Use a prop close-icon para personalizar o Icon do botão de fechar. O padrão é i-lucide-x.
<template>
<NDrawer title="Drawer with close button" close close-icon="i-lucide-arrow-right">
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #body>
<Placeholder class="h-48" />
</template>
</NDrawer>
</template>
Use a prop direction para controlar a direção do Drawer. O padrão é bottom.
<template>
<NDrawer direction="right">
<NButton 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>
</NDrawer>
</template>
Use a prop inset para recuar o Drawer em relação às bordas.
<template>
<NDrawer direction="right" inset>
<NButton 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>
</NDrawer>
</template>
Use a prop handle para controlar se o Drawer tem uma alça ou não. O padrão é true.
<template>
<NDrawer :handle="false">
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</NDrawer>
</template>
Use a prop handle-only para permitir que o Drawer seja arrastado apenas pela alça.
<template>
<NDrawer handle-only>
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</NDrawer>
</template>
Use a prop overlay para controlar se o Drawer tem um overlay ou não. O padrão é true.
<template>
<NDrawer :overlay="false">
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</NDrawer>
</template>
Use a prop modal para controlar se o Drawer bloqueia a interação com o conteúdo externo. O padrão é true.
modal é definido como false, o overlay é automaticamente desabilitado e o conteúdo externo se torna interativo.<template>
<NDrawer :modal="false">
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</NDrawer>
</template>
Use a prop dismissible para controlar se o Drawer pode ser dispensado ao clicar fora dele ou pressionar escape. O padrão é true.
close:prevent será emitido quando o usuário tentar fechá-lo.modal: false com dismissible: false para tornar o fundo do Drawer interativo sem fechá-lo.Use a prop should-scale-background para escalar o fundo quando o Drawer está aberto, criando um efeito visual de profundidade. Você pode definir a prop set-background-color-on-scale como false para evitar a mudança da cor de fundo.
<template>
<NDrawer should-scale-background set-background-color-on-scale>
<NButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #content>
<Placeholder class="h-48 m-4" />
</template>
</NDrawer>
</template>
data-vaul-drawer-wrapper a um elemento pai do seu app para que isso funcione.<template>
<NApp>
<div class="bg-default" data-vaul-drawer-wrapper>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</NApp>
</template>
export default defineNuxtConfig({
app: {
rootAttrs: {
'data-vaul-drawer-wrapper': '',
'class': 'bg-default'
}
}
})
Você pode controlar o estado de abertura usando a prop default-open ou a diretiva v-model:open.
defineShortcuts, você pode abrir e fechar o Drawer pressionando O.Você pode renderizar um componente Modal no desktop e um Drawer no mobile, por exemplo.
Você pode aninhar gavetas umas dentro das outras usando a prop nested.
Use o slot #footer para adicionar conteúdo após o corpo do Drawer.
Você pode usar um componente CommandPalette dentro do conteúdo do Drawer.
useLazyFetch com immediate: false para buscar os dados apenas quando o Drawer abre.| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
title | string | |
description | string | |
inset | false | boolean Whether to inset the drawer from the edges. |
content | DialogContentProps & Partial<EmitsToProps<DialogContentImplEmits>>The content of the drawer. | |
overlay | true | boolean Render an overlay behind the drawer. |
handle | true | boolean Render a handle on the drawer. |
portal | true | string | false | true | HTMLElementRender the drawer in a portal. |
nested | false | boolean Whether the drawer is nested in another drawer. |
modal | true | boolean When |
open | boolean | |
activeSnapPoint | null | string | number | |
closeThreshold | numberNumber between 0 and 1 that determines when the drawer should be closed. Example: threshold of 0.5 would close the drawer if the user swiped for 50% of the height of the drawer or more. | |
shouldScaleBackground | boolean | |
setBackgroundColorOnScale | boolean When | |
scrollLockTimeout | numberDuration for which the drawer is not draggable after scrolling content inside of the drawer. | |
fixed | boolean When | |
dismissible | true | boolean When |
defaultOpen | boolean Opened by default, skips initial enter animation. Still reacts to | |
direction | 'bottom' | "left" | "right" | "bottom" | "top"Direction of the drawer. Can be |
noBodyStyles | boolean When | |
handleOnly | boolean When | |
preventScrollRestoration | boolean | |
snapPoints | (string | number)[]Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up.
Should go from least visible. Example | |
ui | { overlay?: SlotClass; content?: SlotClass; handle?: SlotClass; container?: SlotClass; header?: SlotClass; title?: SlotClass; description?: SlotClass; body?: SlotClass; footer?: SlotClass; } |
| Slot | Type |
|---|---|
default | {} |
content | {} |
header | {} |
title | {} |
description | {} |
body | {} |
footer | {} |
| Event | Type |
|---|---|
update:open | [open: boolean] |
drag | [percentageDragged: number] |
close | [] |
close:prevent | [] |
release | [open: boolean] |
update:activeSnapPoint | [val: string | number] |
animationEnd | [open: boolean] |
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 '@nitro/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'
}
}
]
}
}
})
]
})