BottomSheet
从屏幕底部滑出的移动端面板组件
底部抽屉(BottomSheet)用于在当前页面上方展示临时内容,常见于移动端操作面板、筛选器、表单确认和上下文操作。
import {
BottomSheet,
BottomSheetClose,
BottomSheetContent,
BottomSheetContentBody,
BottomSheetDescription,
BottomSheetFooter,
BottomSheetHeader,
BottomSheetKnob,
BottomSheetOverlay,
BottomSheetRoot,
BottomSheetTitle,
BottomSheetTrigger
} from '@skyroc/web-ui';架构说明
BottomSheet 基于 Vaul 的 Root、Content、Overlay、Close 等能力,并复用项目 Dialog 的标题、描述、页脚、触发器和关闭按钮样式。
BottomSheetRoot:Vaul Root,负责打开状态、拖拽、snap point、背景缩放等行为。BottomSheetContent:底部面板内容,内部自动挂载 Portal、Overlay、ContentBody 和 Knob。BottomSheetKnob:面板顶部的拖拽指示条。BottomSheet:组合式主组件,内置 Trigger、Content、Header、Title、Description、Close 和 Footer。
何时使用
- 移动端需要从屏幕底部展示补充内容或操作面板时。
- 页面上下文不应完全跳转,但需要临时聚焦一个任务时。
- 内容适合纵向阅读、拖拽关闭或配合底部操作按钮时。
- 桌面端复杂表单或长内容弹窗优先考虑 Dialog 或 Drawer。
基础用法
通过 trigger 设置打开触发器,通过 title、description、children 和 footer 组织内容。
页脚与关闭
设置 footer 可在底部放置操作按钮。需要点击按钮关闭面板时,可使用 BottomSheetClose asChild 包裹自定义按钮。
设置 showClose 后,头部会显示默认关闭按钮;通过 closeProps 可以调整关闭按钮属性。
拖拽与打开状态
BottomSheet 继承 Vaul Root 的行为属性,可通过 open / onOpenChange 控制打开状态,通过 snapPoints、activeSnapPoint、setActiveSnapPoint 配置吸附点,通过 dismissible、handleOnly、closeThreshold 控制关闭交互。
自定义样式
通过 classNames 控制 overlay、content、contentBody、knob、header、title、description、footer、close 等 slot。
通过 size 可以统一控制 BottomSheet 的内容容器、正文间距、拖拽指示条、标题、描述、关闭按钮和页脚等结构尺寸。需要局部覆盖时,可继续通过 contentProps、各 slot props 或 classNames 精细调整。
API
BottomSheet
通用属性参考:基于 Vaul Root,并额外提供项目化的 header、content、footer、trigger 和 slot 样式配置。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| trigger | 触发 BottomSheet 打开的元素 | ReactNode | - |
| title | 面板标题 | ReactNode | - |
| description | 面板描述文本 | ReactNode | - |
| children | 面板主体内容 | ReactNode | - |
| footer | 页脚内容,通常放置操作按钮 | ReactNode | - |
| showClose | 是否显示默认关闭按钮 | boolean | false |
| size | 尺寸,影响内容容器、正文间距、拖拽指示条、标题、描述、关闭按钮和页脚等样式 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | - |
| classNames | 各 slot 的自定义 class | BottomSheetClassNames | - |
| contentProps | 传递给 BottomSheetContent 的额外 props | BottomSheetContentProps | - |
| closeProps | 传递给 BottomSheetClose 的额外 props | BottomSheetCloseProps | - |
| headerProps | 传递给 BottomSheetHeader 的额外 props | BottomSheetHeaderProps | - |
| titleProps | 传递给 BottomSheetTitle 的额外 props | BottomSheetTitleProps | - |
| descriptionProps | 传递给 BottomSheetDescription 的额外 props | BottomSheetDescriptionProps | - |
| footerProps | 传递给 BottomSheetFooter 的额外 props | BottomSheetFooterProps | - |
| open | 受控模式下的打开状态 | boolean | - |
| defaultOpen | 非受控模式下的默认打开状态 | boolean | - |
| onOpenChange | 打开状态变化时触发 | (open: boolean) => void | - |
| shouldScaleBackground | 打开时是否缩放背景 | boolean | true |
| modal | 是否使用模态交互;为 false 时允许与外部元素交互 | boolean | - |
| dismissible | 是否允许拖拽、点击外部、Esc 等方式关闭 | boolean | - |
| direction | 抽屉方向 | 'top' | 'bottom' | 'left' | 'right' | 'bottom' |
| snapPoints | 吸附点列表,可使用比例或 px 字符串 | (number | string)[] | - |
| activeSnapPoint | 当前激活的吸附点 | number | string | null | - |
| setActiveSnapPoint | 更新当前吸附点 | (snapPoint: number | string | null) => void | - |
| fadeFromIndex | 从哪个吸附点开始应用遮罩淡入 | number | - |
| closeThreshold | 拖拽关闭阈值,0 到 1 之间 | number | - |
| handleOnly | 是否只允许通过 handle/knob 拖拽 | boolean | - |
| fixed | 键盘打开时是否只改变高度而不整体上移 | boolean | - |
| nested | 是否作为嵌套抽屉使用 | boolean | - |
| container | Portal 挂载容器 | HTMLElement | null | - |
| onDrag | 拖拽过程中触发 | (event: PointerEvent<HTMLDivElement>, percentageDragged: number) => void | - |
| onRelease | 拖拽释放时触发 | (event: PointerEvent<HTMLDivElement>, open: boolean) => void | - |
| onClose | 关闭时触发 | () => void | - |
| onAnimationEnd | 打开或关闭动画结束后触发 | (open: boolean) => void | - |
子组件
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| BottomSheetRoot | Vaul Root,负责抽屉状态和交互行为 | DialogProps | - |
| BottomSheetTrigger | 打开触发器,复用 DialogTrigger | DialogTriggerProps | - |
| BottomSheetContent | 面板内容容器,自动包含 Portal、Overlay、ContentBody 和 Knob | BottomSheetContentProps | - |
| BottomSheetContentBody | 内容内部包裹容器 | BottomSheetContentBodyProps | - |
| BottomSheetKnob | 顶部拖拽指示条 | BottomSheetKnobProps | - |
| BottomSheetOverlay | 遮罩层 | BottomSheetOverlayProps | - |
| BottomSheetHeader | 头部容器,复用 DialogHeader | BottomSheetHeaderProps | - |
| BottomSheetTitle | 标题组件 | BottomSheetTitleProps | - |
| BottomSheetDescription | 描述组件 | BottomSheetDescriptionProps | - |
| BottomSheetFooter | 页脚容器,复用 DialogFooter | BottomSheetFooterProps | - |
| BottomSheetClose | 关闭触发器,内部使用 Vaul Close | BottomSheetCloseProps | - |
类型
BottomSheetProps
主组件属性。继承 Vaul DialogProps,并额外提供项目化内容、标题、描述、页脚和 slot 样式配置。
| 字段 | 类型 | 说明 |
|---|---|---|
| activeSnapPoint | number | string | null | 当前激活吸附点。来自 Vaul DialogProps。 |
| children | React.ReactNode | 面板主体内容。 |
| classNames | BottomSheetClassNames | 各 slot 的自定义 class。 |
| closeProps | BottomSheetCloseProps | 传递给关闭按钮的属性。 |
| closeThreshold | number | 拖拽关闭阈值。来自 Vaul DialogProps。 |
| container | HTMLElement | null | Portal 挂载容器。来自 Vaul DialogProps。 |
| contentProps | BottomSheetContentProps | 传递给内容容器的属性。 |
| defaultOpen | boolean | 默认打开状态。来自 Vaul DialogProps。 |
| description | React.ReactNode | 描述内容。 |
| descriptionProps | BottomSheetDescriptionProps | 传递给描述组件的属性。 |
| direction | 'top' | 'bottom' | 'left' | 'right' | 抽屉方向。来自 Vaul DialogProps。 |
| dismissible | boolean | 是否允许用户关闭。来自 Vaul DialogProps。 |
| fadeFromIndex | number | 遮罩淡入起始吸附点索引。仅在 snapPoints 存在时使用。 |
| fixed | boolean | 键盘打开时是否固定位置。来自 Vaul DialogProps。 |
| footer | React.ReactNode | 页脚内容。 |
| footerProps | BottomSheetFooterProps | 传递给页脚容器的属性。 |
| handleOnly | boolean | 是否只允许通过 handle 拖拽。来自 Vaul DialogProps。 |
| headerProps | BottomSheetHeaderProps | 传递给头部容器的属性。 |
| modal | boolean | 是否模态。来自 Vaul DialogProps。 |
| nested | boolean | 是否嵌套抽屉。来自 Vaul DialogProps。 |
| onAnimationEnd | (open: boolean) => void | 动画结束后触发。来自 Vaul DialogProps。 |
| onClose | () => void | 关闭时触发。来自 Vaul DialogProps。 |
| onDrag | (event: React.PointerEvent<HTMLDivElement>, percentageDragged: number) => void | 拖拽中触发。来自 Vaul DialogProps。 |
| onOpenChange | (open: boolean) => void | 打开状态变化时触发。来自 Vaul DialogProps。 |
| onRelease | (event: React.PointerEvent<HTMLDivElement>, open: boolean) => void | 拖拽释放时触发。来自 Vaul DialogProps。 |
| open | boolean | 受控打开状态。来自 Vaul DialogProps。 |
| setActiveSnapPoint | (snapPoint: number | string | null) => void | 更新当前吸附点。来自 Vaul DialogProps。 |
| shouldScaleBackground | boolean | 是否缩放背景。默认 true。 |
| showClose | boolean | 是否展示关闭按钮。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 统一控制 BottomSheet 结构尺寸。 |
| snapPoints | (number | string)[] | 吸附点列表。来自 Vaul DialogProps。 |
| title | React.ReactNode | 标题内容。 |
| titleProps | BottomSheetTitleProps | 传递给标题组件的属性。 |
| trigger | React.ReactNode | 触发打开的元素。 |
BottomSheetClassNames
BottomSheet 可定制 slot 的 class 集合。
| 字段 | 类型 | 说明 |
|---|---|---|
| close | ClassValue | 关闭按钮 class,来自 DialogSlots。 |
| closeIcon | ClassValue | 关闭图标 class,来自 BottomSheetSlots。 |
| content | ClassValue | 面板内容容器 class。 |
| contentBody | ClassValue | 面板内部内容包裹区 class。 |
| description | ClassValue | 描述区域 class。 |
| footer | ClassValue | 页脚区域 class。 |
| header | ClassValue | 头部区域 class。 |
| knob | ClassValue | 拖拽指示条 class。 |
| overlay | ClassValue | 遮罩层 class。 |
| title | ClassValue | 标题区域 class。 |
BottomSheetContentProps
内容容器属性。继承 Vaul ContentProps,并通过 StyledComponentProps 增加 className 和 size。
| 字段 | 类型 | 说明 |
|---|---|---|
| children | React.ReactNode | 内容子节点。 |
| className | ClassValue | 内容容器 class。 |
| classNames | Pick<BottomSheetClassNames, 'content' | 'contentBody' | 'knob' | 'overlay'> | 内容区域相关 slot class。 |
| forceMount | true | 强制挂载内容。来自 Radix Dialog Content props。 |
| onCloseAutoFocus | (event: Event) => void | 关闭后自动聚焦前触发。来自 ContentProps。 |
| onEscapeKeyDown | (event: KeyboardEvent) => void | 按 Escape 时触发。来自 ContentProps。 |
| onOpenAutoFocus | (event: Event) => void | 打开后自动聚焦前触发。来自 ContentProps。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 内容容器尺寸。 |
BottomSheetContentBodyProps
内容内部包裹容器属性,等价于 HTMLComponentProps<"div">。
| 字段 | 类型 | 说明 |
|---|---|---|
| children | React.ReactNode | 子节点。 |
| className | ClassValue | 内容包裹区 class。 |
| id | string | DOM id。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetKnobProps
拖拽指示条属性,等价于 HTMLComponentProps<"div">。
| 字段 | 类型 | 说明 |
|---|---|---|
| className | ClassValue | 拖拽指示条 class。 |
| id | string | DOM id。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetOverlayProps
遮罩层属性,等价于 Omit<DialogOverlayProps, "component">。
| 字段 | 类型 | 说明 |
|---|---|---|
| className | ClassValue | 遮罩层 class。 |
| forceMount | true | 强制挂载遮罩层。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetHeaderProps
头部容器属性,等价于 DialogHeaderProps。
| 字段 | 类型 | 说明 |
|---|---|---|
| children | React.ReactNode | 头部内容。 |
| className | ClassValue | 头部 class。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetDescriptionProps
描述组件属性,等价于 Omit<DialogDescriptionProps, "component">。
| 字段 | 类型 | 说明 |
|---|---|---|
| children | React.ReactNode | 描述内容。 |
| className | ClassValue | 描述 class。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetTitleProps
标题组件属性,等价于 Omit<DialogTitleProps, "component">。
| 字段 | 类型 | 说明 |
|---|---|---|
| children | React.ReactNode | 标题内容。 |
| className | ClassValue | 标题 class。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetCloseProps
关闭按钮属性,等价于 Omit<DialogCloseProps, "component">。
| 字段 | 类型 | 说明 |
|---|---|---|
| asChild | boolean | 将关闭行为绑定到子元素。 |
| children | React.ReactNode | 自定义关闭按钮内容。 |
| className | ClassValue | 关闭按钮 class。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetFooterProps
页脚容器属性,等价于 HTMLComponentProps<"div">。
| 字段 | 类型 | 说明 |
|---|---|---|
| children | React.ReactNode | 页脚内容。 |
| className | ClassValue | 页脚 class。 |
| size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 尺寸属性。 |
BottomSheetSlots
BottomSheet 自身的 slot
DialogSlots
复用 Dialog 的 slot