import { Button, CloseButton, Drawer, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Drawer.Root>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open Drawer
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</Drawer.Body>
<Drawer.Footer>
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</Drawer.Footer>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}
用法
¥Usage
import { Drawer } from "@chakra-ui/react"
<Drawer.Root>
<Drawer.Backdrop />
<Drawer.Trigger />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.CloseTrigger />
<Drawer.Header>
<Drawer.Title />
</Drawer.Header>
<Drawer.Body />
<Drawer.Footer />
</Drawer.Content>
</Drawer.Positioner>
</Drawer.Root>
示例
¥Examples
受控
¥Controlled
使用 open
和 onOpenChange
属性控制抽屉组件。
¥Use the open
and onOpenChange
props to control the drawer component.
"use client"
import { Button, CloseButton, Drawer, Portal } from "@chakra-ui/react"
import { useState } from "react"
const Demo = () => {
const [open, setOpen] = useState(false)
return (
<Drawer.Root open={open} onOpenChange={(e) => setOpen(e.open)}>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open Drawer
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</Drawer.Body>
<Drawer.Footer>
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</Drawer.Footer>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}
尺寸
¥Sizes
使用 size
属性更改抽屉组件的大小。
¥Use the size
prop to change the size of the drawer component.
import {
Button,
CloseButton,
Drawer,
For,
HStack,
Kbd,
Portal,
} from "@chakra-ui/react"
const Demo = () => {
return (
<HStack wrap="wrap">
<For each={["xs", "sm", "md", "lg", "xl", "full"]}>
{(size) => (
<Drawer.Root key={size} size={size}>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open ({size})
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
Press the <Kbd>esc</Kbd> key to close the drawer.
</Drawer.Body>
<Drawer.Footer>
<Drawer.ActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</Drawer.ActionTrigger>
<Button>Save</Button>
</Drawer.Footer>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)}
</For>
</HStack>
)
}
上下文
¥Context
使用 DrawerContext
组件从抽屉外部访问抽屉状态和方法。
¥Use the DrawerContext
component to access the drawer state and methods from
outside the drawer.
"use client"
import { Button, CloseButton, Drawer, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Drawer.Root>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open Drawer
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Context>
{(store) => (
<Drawer.Body pt="6" spaceY="3">
<p>Drawer is open: {store.open ? "true" : "false"}</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
do eiusmod tempor incididunt ut labore et dolore magna
aliqua.
</p>
<button onClick={() => store.setOpen(false)}>Close</button>
</Drawer.Body>
)}
</Drawer.Context>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}
偏移
¥Offset
在 Drawer.Positioner
上使用 padding
CSS 属性来调整抽屉组件的偏移量。
¥Use the padding
CSS property on Drawer.Positioner
to adjust the offset of
the drawer component.
import { Button, CloseButton, Drawer, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Drawer.Root>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open Drawer
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner padding="4">
<Drawer.Content rounded="md">
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</Drawer.Body>
<Drawer.Footer>
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</Drawer.Footer>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}
放置位置
¥Placement
使用 placement
属性更改抽屉组件的位置。
¥Use the placement
prop to change the placement of the drawer component.
import {
Button,
CloseButton,
Drawer,
For,
HStack,
Portal,
} from "@chakra-ui/react"
const Demo = () => {
return (
<HStack wrap="wrap">
<For each={["bottom", "top", "start", "end"]}>
{(placement) => (
<Drawer.Root key={placement} placement={placement}>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open ({placement})
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content
roundedTop={placement === "bottom" ? "l3" : undefined}
roundedBottom={placement === "top" ? "l3" : undefined}
>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
do eiusmod tempor incididunt ut labore et dolore magna
aliqua.
</Drawer.Body>
<Drawer.Footer>
<Drawer.ActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</Drawer.ActionTrigger>
<Button>Save</Button>
</Drawer.Footer>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)}
</For>
</HStack>
)
}
初始焦点
¥Initial Focus
使用 initialFocusEl
属性设置抽屉组件的初始焦点。
¥Use the initialFocusEl
prop to set the initial focus of the drawer component.
"use client"
import {
Button,
CloseButton,
Drawer,
Input,
Portal,
Stack,
} from "@chakra-ui/react"
import { useRef } from "react"
const Demo = () => {
const ref = useRef<HTMLInputElement>(null)
return (
<Drawer.Root initialFocusEl={() => ref.current}>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open Drawer
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<Stack mt="5">
<Input defaultValue="Naruto" placeholder="First name" />
<Input ref={ref} placeholder="Email" />
</Stack>
</Drawer.Body>
<Drawer.Footer>
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</Drawer.Footer>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}
自定义容器
¥Custom Container
以下是如何在自定义容器中渲染抽屉组件的示例。
¥Here's an example of how to render the drawer component in a custom container.
考虑将 closeOnInteractOutside
设置为 false
,以防止抽屉在与外部交互时关闭。
¥Consider setting closeOnInteractOutside
to false
to prevent the drawer from
closing when interacting outside the drawer.
Render drawer here
"use client"
import {
Button,
CloseButton,
Drawer,
Portal,
Stack,
type StackProps,
Text,
} from "@chakra-ui/react"
import { forwardRef, useRef } from "react"
const DrawerContainer = forwardRef<HTMLDivElement, StackProps>(
function DrawerContainer(props, ref) {
return (
<Stack
pos="relative"
overflow="hidden"
align="flex-start"
p="8"
minH="400px"
layerStyle="fill.subtle"
outline="2px solid gray"
ref={ref}
{...props}
/>
)
},
)
const Demo = () => {
const portalRef = useRef<HTMLDivElement | null>(null)
return (
<Drawer.Root closeOnInteractOutside={false}>
<DrawerContainer ref={portalRef}>
<Text>Render drawer here</Text>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm" bg="bg">
Open Drawer
</Button>
</Drawer.Trigger>
</DrawerContainer>
<Portal container={portalRef}>
<Drawer.Backdrop pos="absolute" boxSize="full" />
<Drawer.Positioner pos="absolute" boxSize="full">
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Header>
<Drawer.Body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</Drawer.Body>
<Drawer.Footer>
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}
标题操作
¥Header Actions
以下是在抽屉组件标题中渲染操作的示例。
¥Here's an example of rendering actions in the header of the drawer component.
import {
Button,
ButtonGroup,
CloseButton,
Drawer,
Portal,
} from "@chakra-ui/react"
const Demo = () => {
return (
<Drawer.Root size="md">
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open Drawer
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.CloseTrigger asChild pos="initial">
<CloseButton />
</Drawer.CloseTrigger>
<Drawer.Title flex="1">Drawer Title</Drawer.Title>
<ButtonGroup>
<Button variant="outline">Cancel</Button>
<Button>Save</Button>
</ButtonGroup>
</Drawer.Header>
<Drawer.Body>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</Drawer.Body>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
)
}
带条件变量的抽屉
¥Drawer with conditional variants
以下是如何根据不同断点更改变体的示例。
¥Here is an example of how to change variants based on the different breakpoints.
本示例使用 mdDown
断点更改抽屉在小屏幕上的位置。推荐使用此方法,因为两种条件都会转换为 CSS 媒体查询,从而避免基础样式合并问题。
¥This example uses the mdDown
breakpoint to change the drawer's placement on
smaller screens. This approach is recommended because both conditions are
translated into CSS media queries, which helps avoid base style merging issues.
如果你确实想使用基本条件,则还需要定义相应的大小。例如:<Drawer.Root placement={{ base: "bottom", md: "end" }} size={{ base: "xs", md: "md" }}>
¥If you really want to use the base condition instead, you’ll also need to define
corresponding sizes. For example:
<Drawer.Root placement={{ base: "bottom", md: "end" }} size={{ base: "xs", md: "md" }}>
Open drawer and resize screen to mobile size
import {
Button,
CloseButton,
Drawer,
Kbd,
Portal,
Text,
} from "@chakra-ui/react"
const Demo = () => {
return (
<>
<Text mb="4">Open drawer and resize screen to mobile size</Text>
<Drawer.Root placement={{ mdDown: "bottom", md: "end" }}>
<Drawer.Trigger asChild>
<Button variant="outline" size="sm">
Open Drawer
</Button>
</Drawer.Trigger>
<Portal>
<Drawer.Backdrop />
<Drawer.Positioner>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
Press the <Kbd>esc</Kbd> key to close the drawer.
</Drawer.Body>
<Drawer.Footer>
<Drawer.ActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</Drawer.ActionTrigger>
<Button>Save</Button>
</Drawer.Footer>
<Drawer.CloseTrigger asChild>
<CloseButton size="sm" />
</Drawer.CloseTrigger>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
</>
)
}
属性
¥Props
根元素
¥Root
Prop | Default | Type |
---|---|---|
closeOnEscape | true | boolean Whether to close the dialog when the escape key is pressed |
closeOnInteractOutside | true | boolean Whether to close the dialog when the outside is clicked |
defaultOpen | false | boolean The initial open state of the dialog when rendered. Use when you don't need to control the open state of the dialog. |
lazyMount | false | boolean Whether to enable lazy mounting |
modal | true | boolean Whether to prevent pointer interaction outside the element and hide all content below it |
preventScroll | true | boolean Whether to prevent scrolling behind the dialog when it's opened |
role | '\'dialog\'' | 'dialog' | 'alertdialog' The dialog's role |
skipAnimationOnMount | false | boolean Whether to allow the initial presence animation. |
trapFocus | true | boolean Whether to trap focus inside the dialog when it's opened |
unmountOnExit | false | boolean Whether to unmount on exit. |
colorPalette | 'gray' | 'gray' | 'red' | 'orange' | 'yellow' | 'green' | 'teal' | 'blue' | 'cyan' | 'purple' | 'pink' The color palette of the component |
size | 'xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' The size of the component |
placement | 'end' | 'start' | 'end' | 'top' | 'bottom' The placement of the component |
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
unstyled | boolean Whether to remove the component's style. | |
aria-label | string Human readable label for the dialog, in event the dialog title is not rendered | |
finalFocusEl | () => MaybeElement Element to receive focus when the dialog is closed | |
id | string The unique identifier of the machine. | |
ids | Partial<{
trigger: string
positioner: string
backdrop: string
content: string
closeTrigger: string
title: string
description: string
}> The ids of the elements in the dialog. Useful for composition. | |
immediate | boolean Whether to synchronize the present change immediately or defer it to the next frame | |
initialFocusEl | () => MaybeElement Element to receive focus when the dialog is opened | |
onEscapeKeyDown | (event: KeyboardEvent) => void Function called when the escape key is pressed | |
onExitComplete | VoidFunction Function called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => void Function called when the focus is moved outside the component | |
onInteractOutside | (event: InteractOutsideEvent) => void Function called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => void Function to call when the dialog's open state changes | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void Function called when the pointer is pressed down outside the component | |
open | boolean The controlled open state of the dialog | |
persistentElements | (() => Element | null)[] Returns the persistent elements that: - should not have pointer-events disabled - should not trigger the dismiss event | |
present | boolean Whether the node is present (controlled by the user) | |
restoreFocus | boolean Whether to restore focus to the element that had focus before the dialog was opened | |
contained | 'true' | 'false' The contained of the component |