v0.1.9

Drawer 抽屉

引入import { DrawerPurePanel } from '@apollo-design/ui';

屏幕边缘滑出的浮层面板。

何时使用

  • 需要从屏幕边缘滑出、承载较多内容或表单的场景。
  • 需要在当前节点内渲染(:get-container="false")而不是挂到 body。
  • 需要可拖拽改尺寸(resizable)或多层嵌套(子抽屉推挤父级)。

代码演示

加载中…

basic-right #

基础抽屉,点击触发按钮抽屉从右滑出,点击遮罩区关闭。

加载中…

closable-placement #

关闭按钮位置。

加载中…

component-token #

打开后切换四个组件 Token,实时观察底部间距、拖拽手柄和层级。

加载中…

config-provider #

打开后切换 ConfigProvider 配置,观察关闭按钮、正文间距和底部样式跟随替换或撤除。

加载中…

extra #

通过 extra slot 可以设置标题栏右侧的操作区。

加载中…

form-in-drawer #

在抽屉中填写姓名与邮箱,通过底部操作区提交。必填和邮箱格式由 Form 校验;校验失败保留输入,成功后关闭并显示本地提交结果。取消或关闭后重新打开,会清空草稿与校验信息。

加载中…

loading #

抽屉内容加载中。

加载中…

mask #

遮罩模糊。

加载中…

multi-level-drawer #

嵌套抽屉:打开子抽屉时父级会被推挤(DrawerContext 的 push/pull 链)。

加载中…

no-mask #

不显示遮罩。

加载中…

placement #

自定义位置,点击触发按钮抽屉从相应的位置滑出,点击遮罩区关闭。

加载中…

render-in-current #

getContainer={false} 时抽屉不 portal,渲染在当前节点里(需要自己定位父容器)。

加载中…

render-panel #

调试用面板。

加载中…

resizable #

可拖拽调整尺寸的抽屉(resizable),使用 resize / resizeStart / resizeEnd 事件显示当前尺寸,并用 maxSize 限制上界。

加载中…

scroll-debug #

长内容滚动。

加载中…

size #

抽屉尺寸,可选 default(378px)与 large(736px)。

加载中…

style-class #

语义化样式。

加载中…

user-profile #

在抽屉中使用 Avatar 与 Descriptions 分组展示示例用户的基本资料和工作信息。面板适应可用宽度,可通过底部按钮或抽屉关闭按钮关闭,再次打开仍展示同一份静态资料。

见 demo/(18 个,覆盖 antd 的主要用户场景)。

API

参数 说明 类型 默认值
open 是否显示 boolean false
placement 方位 'top' | 'right' | 'bottom' | 'left' 'right'
size 尺寸预设或数值/字符串 'default' | 'large' | number | string 'default'(378)
defaultSize 默认尺寸(垂直方位的默认值来源) number 378
maxSize 拖拽时允许的最大尺寸 number —
title 标题(文本;富标题走 #title slot,slot 优先) string —
footer 页脚(⚠️ 已改为 #footer slot,空 slot 等价隐藏) slot —
extra 标题栏右侧(⚠️ 已改为 #extra slot) slot —
closable 关闭按钮。false ⇒ 不渲染;对象可给 placement: 'start' | 'end' boolean | null | {...} true
closeIcon 自定义关闭图标(⚠️ 已改为 #closeIcon slot) slot —
loading 内容区骨架态 boolean false
mask 遮罩。false ⇒ 无遮罩(根类加 no-mask) boolean | { enabled?, blur?, closable? } true
maskClosable 点遮罩关闭(⚠️ deprecated ⇒ mask.closable) boolean true
push 推挤:true / { distance }(只有子抽屉会真的推) boolean | PushState { distance: 180 }
resizable 可拖拽改尺寸;通知使用下表的 Vue 事件 boolean false
getContainer 容器;false 为内联渲染,其他值由 Portal 解析 GetContainer 默认 body 内的容器
destroyOnHidden 关闭后卸载 boolean false
forceRender 未打开时也挂载正文,不参与活动 Escape 栈 boolean false
focusable 焦点归还和陷阱;显式 trap 优先,默认仅非内联且有遮罩时锁定 { focusTriggerAfterClose?, trap? } —
focusTriggerAfterClose 覆盖 focusable 中的关闭后焦点归还开关 boolean true
keyboard ESC 可关 boolean true
autoFocus 打开时自动聚焦面板 boolean true
rootStyle 根样式,原生 style 的优先级更高 CSSProperties —
zIndex 显式根层级;未传时使用当前主题和嵌套层级 number —
classNames / styles 12 个语义槽 — —

open 是受控开关。关闭按钮、遮罩和 Escape 仅发出关闭请求,父组件在接受后更新 open。destroyOnHidden 等待真实离场完成后卸载;条件卸载也清理焦点锁、拖拽和推挤登记。

显式 size(及当前方位的 width/height 别名)保持受控,拖拽只发出尺寸请求;未传时使用默认尺寸并在本地调整。撤除受控尺寸时保留最后实际接受的值,不应用被拒绝的请求。

Events

事件 说明 载荷
close 遮罩点击、关闭按钮或活动层的 Escape 请求 原始 Event(MouseEvent / KeyboardEvent)
afterOpenChange 打开或关闭动效完成 open: boolean
resizeStart 开始拖拽 size: number,像素
resize 拖拽后的当前尺寸 size: number,像素
resizeEnd 正常结束拖拽后的实测尺寸 size: number,像素

事件支持 Vue 的 .once 和监听数组。关闭、禁用拖拽、切换方位或卸载会释放拖拽监听,不伪造正常结束通知。

deprecated(9 条,dev 告警):headerStyle / bodyStyle / footerStyle → styles.*;contentWrapperStyle → styles.wrapper;maskStyle → styles.mask; drawerStyle → styles.section;destroyOnClose → destroyOnHidden; width / height → size。

语义化槽位(12 个)

root / mask / wrapper / section / header / title / extra / body / footer / close / dragger
(+ deprecated 的 content ⇒ section)

Slots(C8-R2)

Slot 说明 参数
title 富标题(文本 title prop 等价,slot 优先) —
footer 页脚(空 slot 等价隐藏) —
extra 标题栏右侧额外内容 —
closeIcon 自定义关闭图标 —

内容插槽通过 DrawerSlots 导出类型;存在即优先(含空内容)。标题在每次 Drawer 渲染中求值一次,面板内容与自动 aria-labelledby 共用同一快照;显式 aria-labelledby 指向外部标题时保留调用方值。头尾内容显隐不重挂载正文,保留输入状态和 DOM 身份。

getContainer 使用 Portal 的完整 GetContainer 类型,包括 false、选择器、元素与容器函数;Vue 模板可直接传 :get-container="false"。Drawer 的原生 class / style(含数组、对象及字符串)、data 属性与普通 DOM 事件属于外层根;id、role 和 ARIA 属于实际 dialog。面板外观使用 classNames.section / styles.section。PurePanel 的原生 attrs 全部属于自身根。

Ref 与开发版迁移

DrawerRef 暴露 nativeElement: HTMLElement | null 和 focus(options?: FocusOptions),指向当前外层根;销毁前后元素可为空。使用实际模板引用:

<script setup lang="ts">
import { Drawer, type DrawerRef } from '@apollo-design/ui';
import { ref, useTemplateRef } from 'vue';
const drawer = useTemplateRef<DrawerRef>('drawer');
const open = ref(false);
const completed = (open: boolean) => console.log(open);
</script>
<template>
  <button @click="open = true">Open drawer</button>
  <Drawer ref="drawer" :open="open" resizable @close="open = false" @after-open-change="completed" @resize="size => console.log(size)">
    <button @click="drawer?.focus()">Focus drawer root</button>
  </Drawer>
</template>

当前未发布开发版直接将 :after-open-change="fn" 迁为 @after-open-change="fn"(h() 使用 onAfterOpenChange),将 :resizable="{ onResize, onResizeStart, onResizeEnd }" 迁为 resizable 与三个同名事件。删除未消费的 DrawerResizableConfig、panelRef 和 destroyInactivePanel;元素访问改用 DrawerRef,销毁使用 destroyOnHidden。PurePanel 类型不再声称消费内部 ariaId、size 或 contentWrapperStyle。原先误落面板的原生 class/style 直接移到根,面板定制改用 section 语义字段。本组无兼容层或弃用期;表中的既有样式和销毁别名仍保留。

Theme(Component Token)

4 个(CSS 变量 --apollo-drawer-*):zIndexPopup(= zIndexPopupBase = 1000,不加偏移)/ footerPaddingBlock(8) / footerPaddingInline(16) / draggerSize(4)。

Drawer 和 PurePanel 读取当前 ConfigProvider.components.drawer 的 className/style、classNames/styles 及适用的 closable/mask/focusable;实例语义样式覆盖 Provider,原生根 style 最后覆盖。四个 Token 通过 theme.components.Drawer 配置,已消费的主题别名、嵌套、整对象替换和撤除都会更新当前根的 CSS 变量;显式 zIndex 仍优先。component-token 与 config-provider Demo 可直接切换观察。

设计说明

尺寸轴随方位切换

left/right 用 width,top/bottom 用 height;size 预设 'large' = 736、 'default' = 378。⚠️ rc 的 378 兜底只对水平方位生效,垂直方位的默认值来自 defaultSize。

嵌套抽屉的推挤链

每个打开的子抽屉独立登记推挤;只有全部关闭或卸载后父级复位。父级按方位 translate(push.distance 默认 180,支持 CSS 长度字符串)。 顶层抽屉自己不会位移 —— push 只决定距离。

样式引入

import '@apollo-design/theme/dist/tokens.css';  // 主题变量,必须先引
import '@apollo-design/ui/drawer/style.css';    // 按需
// 或
import '@apollo-design/ui/style.css';           // 汇总
Powered by Vue 3 + ViteApollo Design · Vue NativeGitHub