一个 React 抽屉,可从任意边缘滑入,采用 Liquid Glass 表面并支持手势关闭。
Drawer 是贴边滑入的面板。它内部渲染 Portal、遮罩、弹窗、标题、描述、可选正文以及自定义操作区,并支持滑动关闭与可选吸附点。
与居中的 Dialog 不同,Drawer 从屏幕边缘滑入,适合移动端设置面板、筛选条件和侧边导航。
默认从底部滑入。open 控制显示,footer 放置自己的按钮;按钮不会自动关闭抽屉,需要像示例一样更新 open。
direction 支持四个取值:down 从底部滑入,up 从顶部滑入,left 和 right 从对应侧边滑入。默认 down,组件会同时调整弹窗位置、圆角、进入动画和滑动关闭方向。
触摸设备上可以朝 direction 对应的方向拖动抽屉来关闭;桌面端仍可使用遮罩、Esc 或操作区按钮。
Open the drawer, then swipe it downward on a touch screen.
snapPoints、defaultSnapPoint、snapPoint、onSnapPointChange 与 snapToSequentialPoints 用于配置吸附高度,让底部或顶部抽屉在预设高度之间停靠。
disablePointerDismissal 会阻止点击遮罩关闭抽屉,Esc 键和操作区按钮仍然可用;适合包含未保存内容的流程。
Drawer 使用与 Dialog 相同的对话框语义:焦点移动到面板内,打开期间被限制在面板中,关闭后返回触发器。title 为面板命名,description 提供描述。
Escape、点击背景与滑动手势都会关闭抽屉;关闭可能丢失工作时,请设置 disablePointerDismissal 并提供可见的取消操作。避免让滑动手势与横向滚动内容冲突。浮层焦点规则见可访问性指南。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | ReactNode | — | 抽屉标题,必填。 |
description | ReactNode | — | 标题下方的说明文字。 |
children | ReactNode | — | 描述与操作区之间的正文内容。 |
footer | ReactNode | — | 底部操作区,可放置任意数量的控件。 |
direction | "down" | "up" | "left" | "right" | "down" | 滑入边缘,同时作为滑动关闭方向。 |
open | boolean | — | 受控的打开状态。 |
defaultOpen | boolean | false | 非受控时的初始打开状态。 |
onOpenChange | (open: boolean, details) => void | — | 抽屉打开或关闭时调用。 |
onOpenChangeComplete | (open: boolean) => void | — | 打开或关闭动画结束后调用。 |
modal | boolean | "trap-focus" | true | 控制焦点锁定与页面滚动锁定。 |
disablePointerDismissal | boolean | false | 禁止点击遮罩关闭。 |
actionsRef | RefObject<Actions | null> | — | 命令式的 close 与 unmount 操作。 |
handle | DrawerHandle | — | 将抽屉与外部触发器关联。 |
triggerId | string | null | — | 与抽屉关联的触发器 ID。 |
defaultTriggerId | string | null | — | 初始打开时使用的触发器 ID。 |
snapPoints | DrawerSnapPoint[] | — | 吸附高度,可用视口比例、像素数字或 px/rem 字符串表示。 |
snapToSequentialPoints | boolean | false | 根据拖动距离而不是速度选择下一个吸附点。 |
snapPoint | DrawerSnapPoint | null | — | 受控的当前吸附点。 |
defaultSnapPoint | DrawerSnapPoint | null | — | 非受控时的初始吸附点。 |
onSnapPointChange | (snapPoint: DrawerSnapPoint | null, details) => void | — | 当前吸附点变化时调用。 |
className | string | — | 合并到弹窗的 Tailwind 类名。 |
style | CSSProperties | — | 内联样式,合并到与 className 相同的元素。 |
ui | { backdrop?: ClassValue; viewport?: ClassValue; popup?: ClassValue; glass?: ClassValue; overlay?: ClassValue; content?: ClassValue; title?: ClassValue; description?: ClassValue; body?: ClassValue; footer?: ClassValue } | — | 按 data-slot 组织的内部槽位类名。 |
| 属性 | 说明 |
|---|---|
data-open | 抽屉打开时存在。 |
data-closed | 抽屉关闭时存在。 |
data-starting-style | 抽屉开始进入动画时存在。 |
data-ending-style | 抽屉正在退出动画时存在。 |
data-expanded | 抽屉处于展开吸附点时存在。 |
data-nested | 抽屉嵌套在另一个抽屉内时存在。 |
data-nested-drawer-open | 抽屉内部还有另一个抽屉打开时存在。 |
data-nested-drawer-swiping | 嵌套抽屉正在滑动时存在。 |
data-swipe-dismiss | 抽屉通过滑动关闭时存在。 |
data-swipe-direction | 表示滑动方向(up、down、left 或 right)。 |
data-swiping | 抽屉正在滑动时存在。 |
data-slot | 标记内部部件(backdrop、viewport、popup、glass、overlay、content、title、description、body、footer),用于样式选择。 |
组件使用共享玻璃材质。可以在父级调整 --ui-glass-* token,用 className 控制布局,并通过 ui 按 data-slot 调整内部槽位。组件级数值位于 src/theme/drawer.ts。Token 分层与预设见主题指南。