Movatterモバイル変換


[0]ホーム

URL:


Nuxt UI v3 is officially released.
Buy Nuxt UI Pro
A drawer that smoothly slides in & out of the screen.

Usage

Use aButton or any other component in the default slot of the Drawer.

Then, use the#content slot to add the content displayed when the Drawer is open.

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

You can also use the#header,#body and#footer slots to customize the Drawer's content.

Title

Use thetitle prop to set the title of the Drawer's header.

<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

Use thedescription prop to set the description of the Drawer's header.

<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

Use thedirection prop to control the direction of the Drawer. Defaults tobottom.

<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

Use theinset prop to inset the Drawer from the edges.

<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

Use thehandle prop to control whether the Drawer has a handle or not. Defaults totrue.

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

Overlay

Use theoverlay prop to control whether the Drawer has an overlay or not. Defaults totrue.

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

Scale background

Use theshould-scale-background prop to scale the background when the Drawer is open, creating a visual depth effect.

<template>  <UDrawer should-scale-background>    <UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />    <template #content>      <Placeholder class="h-48 m-4" />    </template>  </UDrawer></template>
Make sure to add thevaul-drawer-wrapper directive to a parent element of your app to make this work.
app.vue
<template>  <UApp>    <div class="bg-(--ui-bg)" vaul-drawer-wrapper>      <NuxtLayout>        <NuxtPage />      </NuxtLayout>    </div>  </UApp></template>
nuxt.config.ts
export default defineNuxtConfig({  app: {    rootAttrs: {      'vaul-drawer-wrapper': '',      'class': 'bg-(--ui-bg)'    }  }})

Examples

Control open state

You can control the open state by using thedefault-open prop or thev-model:open directive.

<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>
In this example, leveragingdefineShortcuts, you can toggle the Drawer by pressingO.
This allows you to move the trigger outside of the Drawer or remove it entirely.

Prevent closing

Set thedismissible prop tofalse to prevent the Drawer from being closed when clicking outside of it or pressing escape.

<script setup lang="ts">const open= ref(false)</script><template>  <UDrawer    v-model:open="open"    :dismissible="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-(--ui-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>
In this example, theheader slot is used to add a close button which is not done by default.

With footer slot

Use the#footer slot to add content after the Drawer's body.

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

With command palette

You can use aCommandPalette component inside the Drawer's content.

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

API

Props

Prop Default Type
as

'div'

any

The 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

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

boolean

Render the drawer in a portal.

dismissible

true

boolean

Whenfalse, the drawer will not close when clicking outside or pressing escape.

defaultOpen

boolean

open

boolean

modal

boolean

activeSnapPoint

null | string | number

closeThreshold

number

direction

'bottom'

"bottom" | "top" | "right" | "left"

fadeFromIndex

number

fixed

boolean

nested

boolean

scrollLockTimeout

number

shouldScaleBackground

boolean

snapPoints

(string | number)[]

ui

Partial<{ overlay: string; content: string; handle: string; container: string; header: string; title: string; description: string; body: string; footer: string; }>

Slots

Slot Type
default

{}

handle

{}

content

{}

header

{}

title

{}

description

{}

body

{}

footer

{}

Emits

Event Type
close

[]

drag

[percentageDragged: number]

update:open

[open: boolean]

release

[open: boolean]

update:activeSnapPoint

[val: string | number]

animationEnd

[open: boolean]

Theme

app.config.ts
export default defineAppConfig({  ui: {    drawer: {      slots: {        overlay: 'fixed inset-0 bg-(--ui-bg-elevated)/75',        content: 'fixed bg-(--ui-bg) ring ring-(--ui-border) flex focus:outline-none',        handle: 'shrink-0 rounded-full bg-(--ui-bg-accented)',        container: 'w-full flex flex-col gap-4 p-4 overflow-y-auto',        header: '',        title: 'text-(--ui-text-highlighted) font-semibold',        description: 'mt-1 text-(--ui-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-[calc(var(--ui-radius)*2)] after:hidden'          }        }      },      compoundVariants: [        {          direction: [            'top',            'bottom'          ],          class: {            content: 'h-auto max-h-[96%]',            handle: 'w-12 h-1.5 mx-auto'          }        },        {          direction: [            'right',            'left'          ],          class: {            content: 'w-auto max-w-[calc(100%-2rem)]',            handle: 'h-12 w-1.5 mt-auto mb-auto'          }        },        {          direction: 'top',          inset: true,          class: {            content: 'inset-x-4 top-4'          }        },        {          direction: 'top',          inset: false,          class: {            content: 'inset-x-0 top-0 rounded-b-[calc(var(--ui-radius)*2)]'          }        },        {          direction: 'bottom',          inset: true,          class: {            content: 'inset-x-4 bottom-4'          }        },        {          direction: 'bottom',          inset: false,          class: {            content: 'inset-x-0 bottom-0 rounded-t-[calc(var(--ui-radius)*2)]'          }        },        {          direction: 'left',          inset: true,          class: {            content: 'inset-y-4 left-4'          }        },        {          direction: 'left',          inset: false,          class: {            content: 'inset-y-0 left-0 rounded-r-[calc(var(--ui-radius)*2)]'          }        },        {          direction: 'right',          inset: true,          class: {            content: 'inset-y-4 right-4'          }        },        {          direction: 'right',          inset: false,          class: {            content: 'inset-y-0 right-0 rounded-l-[calc(var(--ui-radius)*2)]'          }        }      ]    }  }})
vite.config.ts
import { defineConfig } from 'vite'import vuefrom '@vitejs/plugin-vue'import uifrom '@nuxt/ui/vite'export default defineConfig({  plugins: [    vue(),    ui({      ui: {        drawer: {          slots: {            overlay: 'fixed inset-0 bg-(--ui-bg-elevated)/75',            content: 'fixed bg-(--ui-bg) ring ring-(--ui-border) flex focus:outline-none',            handle: 'shrink-0 rounded-full bg-(--ui-bg-accented)',            container: 'w-full flex flex-col gap-4 p-4 overflow-y-auto',            header: '',            title: 'text-(--ui-text-highlighted) font-semibold',            description: 'mt-1 text-(--ui-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-[calc(var(--ui-radius)*2)] after:hidden'              }            }          },          compoundVariants: [            {              direction: [                'top',                'bottom'              ],              class: {                content: 'h-auto max-h-[96%]',                handle: 'w-12 h-1.5 mx-auto'              }            },            {              direction: [                'right',                'left'              ],              class: {                content: 'w-auto max-w-[calc(100%-2rem)]',                handle: 'h-12 w-1.5 mt-auto mb-auto'              }            },            {              direction: 'top',              inset: true,              class: {                content: 'inset-x-4 top-4'              }            },            {              direction: 'top',              inset: false,              class: {                content: 'inset-x-0 top-0 rounded-b-[calc(var(--ui-radius)*2)]'              }            },            {              direction: 'bottom',              inset: true,              class: {                content: 'inset-x-4 bottom-4'              }            },            {              direction: 'bottom',              inset: false,              class: {                content: 'inset-x-0 bottom-0 rounded-t-[calc(var(--ui-radius)*2)]'              }            },            {              direction: 'left',              inset: true,              class: {                content: 'inset-y-4 left-4'              }            },            {              direction: 'left',              inset: false,              class: {                content: 'inset-y-0 left-0 rounded-r-[calc(var(--ui-radius)*2)]'              }            },            {              direction: 'right',              inset: true,              class: {                content: 'inset-y-4 right-4'              }            },            {              direction: 'right',              inset: false,              class: {                content: 'inset-y-0 right-0 rounded-l-[calc(var(--ui-radius)*2)]'              }            }          ]        }      }    })  ]})
vite.config.ts
import { defineConfig } from 'vite'import vuefrom '@vitejs/plugin-vue'import uiProfrom '@nuxt/ui-pro/vite'export default defineConfig({  plugins: [    vue(),    uiPro({      ui: {        drawer: {          slots: {            overlay: 'fixed inset-0 bg-(--ui-bg-elevated)/75',            content: 'fixed bg-(--ui-bg) ring ring-(--ui-border) flex focus:outline-none',            handle: 'shrink-0 rounded-full bg-(--ui-bg-accented)',            container: 'w-full flex flex-col gap-4 p-4 overflow-y-auto',            header: '',            title: 'text-(--ui-text-highlighted) font-semibold',            description: 'mt-1 text-(--ui-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-[calc(var(--ui-radius)*2)] after:hidden'              }            }          },          compoundVariants: [            {              direction: [                'top',                'bottom'              ],              class: {                content: 'h-auto max-h-[96%]',                handle: 'w-12 h-1.5 mx-auto'              }            },            {              direction: [                'right',                'left'              ],              class: {                content: 'w-auto max-w-[calc(100%-2rem)]',                handle: 'h-12 w-1.5 mt-auto mb-auto'              }            },            {              direction: 'top',              inset: true,              class: {                content: 'inset-x-4 top-4'              }            },            {              direction: 'top',              inset: false,              class: {                content: 'inset-x-0 top-0 rounded-b-[calc(var(--ui-radius)*2)]'              }            },            {              direction: 'bottom',              inset: true,              class: {                content: 'inset-x-4 bottom-4'              }            },            {              direction: 'bottom',              inset: false,              class: {                content: 'inset-x-0 bottom-0 rounded-t-[calc(var(--ui-radius)*2)]'              }            },            {              direction: 'left',              inset: true,              class: {                content: 'inset-y-4 left-4'              }            },            {              direction: 'left',              inset: false,              class: {                content: 'inset-y-0 left-0 rounded-r-[calc(var(--ui-radius)*2)]'              }            },            {              direction: 'right',              inset: true,              class: {                content: 'inset-y-4 right-4'              }            },            {              direction: 'right',              inset: false,              class: {                content: 'inset-y-0 right-0 rounded-l-[calc(var(--ui-radius)*2)]'              }            }          ]        }      }    })  ]})

[8]ページ先頭

©2009-2025 Movatter.jp