Skip to content
Lenso UI

Drawer

Drawer 从 HeroUI v2 到 v3 的迁移指南。

完整的 API 参考、样式指南与高级示例请参阅 v3 Drawer 文档。本指南只关注从 HeroUI v2 的迁移。

结构变化

在 v2 中,Drawer 与 Modal 共享相同的 API,使用 DrawerContent、DrawerHeader、DrawerBody 和 DrawerFooter,并采用渲染回调模式:

import { Drawer, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, Button, useDisclosure } from "@lenso/ui";
export default function App() {  const { isOpen, onOpen, onOpenChange } = useDisclosure();
  return (    <>      <Button onPress={onOpen}>Open Drawer</Button>      <Drawer isOpen={isOpen} onOpenChange={onOpenChange} placement="right">        <DrawerContent>          {(onClose) => (            <>              <DrawerHeader>Drawer Title</DrawerHeader>              <DrawerBody>                <p>Drawer content goes here.</p>              </DrawerBody>              <DrawerFooter>                <Button onPress={onClose}>Close</Button>              </DrawerFooter>            </>          )}        </DrawerContent>      </Drawer>    </>  );}

在 v3 中,Drawer 改用复合组件模式,提供显式子组件与内置触发器支持:

import { Drawer, Button } from "@lenso/ui";
export default function App() {  return (    <Drawer>      <Button>Open Drawer</Button>      <Drawer.Backdrop>        <Drawer.Content placement="right">          <Drawer.Dialog>            <Drawer.Handle />            <Drawer.CloseTrigger />            <Drawer.Header>              <Drawer.Heading>Drawer Title</Drawer.Heading>            </Drawer.Header>            <Drawer.Body>              <p>Drawer content goes here.</p>            </Drawer.Body>            <Drawer.Footer>              <Button slot="close">Close</Button>            </Drawer.Footer>          </Drawer.Dialog>        </Drawer.Content>      </Drawer.Backdrop>    </Drawer>  );}

主要变化

1. 组件结构

v2: Drawer 包裹 DrawerContent,并使用渲染回调模式;触发器需要通过 useDisclosure 单独管理
v3: 复合组件:Drawer、Drawer.Backdrop、Drawer.Content、Drawer.Dialog、Drawer.Header、Drawer.Heading、Drawer.Body、Drawer.Footer、Drawer.Handle、Drawer.CloseTrigger。Drawer 的第一个子节点会成为触发器。

2. 触发模式

v2: 使用外部触发器,并通过 useDisclosure 钩子与 isOpen / onOpenChange 管理状态
v3: 内置触发器,Drawer 的第一个子节点会自动成为触发器。受控状态可通过 useOverlayState 钩子管理。

3. v3 新特性

  • 拖动关闭:在手柄、头部和底部区域内置基于指针的拖动手势
  • 拖动手柄:Drawer.Handle 是视觉拖动指示器组件
  • 内置关闭触发器:Drawer.CloseTrigger 会渲染一个关闭按钮
  • 基于 slot 的关闭:带有 slot="close" 的按钮会自动关闭 Drawer

4. Prop 变更

v2 propv3 等效项说明
isOpenDrawer.Backdrop 的 isOpen或使用 useOverlayState
onOpenChangeDrawer.Backdrop 的 onOpenChange或使用 useOverlayState
onClose-在按钮上使用 onOpenChange 或 slot="close"
placementDrawer.Content 的 placement"right" → "right"、"left" → "left"、"top" → "top"、"bottom" → "bottom"。默认值从 "right" 改为 "bottom"
size-已移除(请在 Drawer.Dialog 上使用 Tailwind CSS)
radius-已移除(请改用 Tailwind CSS)
backdropDrawer.Backdrop 的 variant值保持一致:"opaque"、"blur"、"transparent"
isDismissableDrawer.Backdrop 的 isDismissable保持一致
isKeyboardDismissDisabledDrawer.Backdrop 的 isKeyboardDismissDisabled保持一致
shouldBlockScroll-v3 中始终会阻止滚动
hideCloseButton-省略 Drawer.CloseTrigger 即可隐藏
closeButton-将自定义内容传给 Drawer.CloseTrigger
motionProps-已移除(v3 使用基于 CSS 的动画)
disableAnimation-已移除
portalContainer-已移除
classNames-在各个复合组件上使用 className

5. Hook 变更

v2: 使用 useDisclosure 钩子管理打开 / 关闭状态
v3: 使用 useOverlayState 钩子(替代 useDisclosure)

// v2const { isOpen, onOpen, onOpenChange } = useDisclosure();
// v3const state = useOverlayState();// state.isOpen, state.open(), state.close(), state.toggle()

迁移示例

基本抽屉

import { Drawer, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, Button, useDisclosure } from "@lenso/ui";
const { isOpen, onOpen, onOpenChange } = useDisclosure();
<>  <Button onPress={onOpen}>Open</Button>  <Drawer isOpen={isOpen} onOpenChange={onOpenChange}>    <DrawerContent>      {(onClose) => (        <>          <DrawerHeader>Title</DrawerHeader>          <DrawerBody>Content</DrawerBody>          <DrawerFooter>            <Button onPress={onClose}>Close</Button>          </DrawerFooter>        </>      )}    </DrawerContent>  </Drawer></>

位置

<Drawer isOpen={isOpen} onOpenChange={onOpenChange} placement="left">  <DrawerContent>    {(onClose) => (      <>        <DrawerHeader>Left Drawer</DrawerHeader>        <DrawerBody>Content</DrawerBody>      </>    )}  </DrawerContent></Drawer>

遮罩变体

<Drawer isOpen={isOpen} onOpenChange={onOpenChange} backdrop="blur">  <DrawerContent>    {(onClose) => (      <>        <DrawerHeader>Blurred Backdrop</DrawerHeader>        <DrawerBody>Content</DrawerBody>      </>    )}  </DrawerContent></Drawer>

受控状态

import { useDisclosure } from "@lenso/ui";
const { isOpen, onOpen, onOpenChange } = useDisclosure();
<>  <Button onPress={onOpen}>Open</Button>  <Drawer isOpen={isOpen} onOpenChange={onOpenChange}>    <DrawerContent>      {(onClose) => (        <>          <DrawerHeader>Controlled</DrawerHeader>          <DrawerBody>Content</DrawerBody>          <DrawerFooter>            <Button onPress={onClose}>Close</Button>          </DrawerFooter>        </>      )}    </DrawerContent>  </Drawer></>

不可关闭

<Drawer  isOpen={isOpen}  onOpenChange={onOpenChange}  isDismissable={false}  hideCloseButton>  <DrawerContent>    {(onClose) => (      <>        <DrawerHeader>Confirm Action</DrawerHeader>        <DrawerBody>Are you sure?</DrawerBody>        <DrawerFooter>          <Button onPress={onClose}>Confirm</Button>        </DrawerFooter>      </>    )}  </DrawerContent></Drawer>

样式变化

v2:classNames prop

<Drawer  classNames={{    wrapper: "custom-wrapper",    base: "custom-base",    backdrop: "custom-backdrop",    header: "custom-header",    body: "custom-body",    footer: "custom-footer",    closeButton: "custom-close",  }}/>

v3:直接使用 className prop

<Drawer>  <Button>Open</Button>  <Drawer.Backdrop className="custom-backdrop">    <Drawer.Content>      <Drawer.Dialog className="custom-base">        <Drawer.CloseTrigger className="custom-close" />        <Drawer.Header className="custom-header">          <Drawer.Heading>Title</Drawer.Heading>        </Drawer.Header>        <Drawer.Body className="custom-body">Content</Drawer.Body>        <Drawer.Footer className="custom-footer">Actions</Drawer.Footer>      </Drawer.Dialog>    </Drawer.Content>  </Drawer.Backdrop></Drawer>

组件结构

v3 Drawer 遵循以下结构:

Drawer (Root)  ├── [Trigger element] (first child becomes trigger)  └── Drawer.Backdrop      └── Drawer.Content (placement)          └── Drawer.Dialog              ├── Drawer.Handle (optional, drag indicator)              ├── Drawer.CloseTrigger (optional, close button)              ├── Drawer.Header              │   └── Drawer.Heading              ├── Drawer.Body (scrollable)              └── Drawer.Footer

总结

  1. 组件结构:渲染回调模式 → 带显式子组件的复合组件
  2. 触发模式:外部 useDisclosure + onPress → 内置触发器(Drawer 的第一个子节点)
  3. 状态钩子:useDisclosure → useOverlayState,并使用 open()、close()、toggle() 方法
  4. 位置:Drawer 上的 prop → Drawer.Content 上的 prop。默认值从 "right" 改为 "bottom"
  5. 遮罩:backdrop prop → Drawer.Backdrop 的 variant prop
  6. 关闭按钮:hideCloseButton / closeButton props → 省略或自定义 Drawer.CloseTrigger
  7. 基于 slot 的关闭:带有 slot="close" 的按钮会自动关闭 Drawer
  8. 新功能:通过 Drawer.Handle 拖动关闭,并支持基于速度的关闭
  9. 动画:motionProps(Framer Motion)→ 基于 CSS 的动画
  10. 已移除样式 prop:size、radius → 使用 Tailwind CSS
  11. 已移除 classNames:在各个复合组件上使用 className