切换主题
YDrawer
保留拖拽、handle、snap与方向能力的管理端Drawer;适合需要这些交互的任务。
Usage
Import / Basic
vue
<script setup lang="ts">
import { ref } from 'vue'
import { YDrawer } from '@yoyoboot/ui-patterns/drawer'
const open = ref(false), draft = ref(''), saving = ref(false)
const guard = { dirty: () => !!draft.value, busy: () => saving.value }
</script>
<template>
<UButton @click="open = true">打开任务</UButton>
<YDrawer v-model:open="open" title="编辑信息" :guard="guard">
<template #body><UInput v-model="draft" aria-label="名称" /></template>
<template #footer="{ close }"><UButton @click="close()">取消</UButton></template>
</YDrawer>
</template>Title & description / slots
title与description传递到正式primitive。default仍是上游trigger插槽,任务内容使用body;header/title/description/actions/close/footer可按需覆盖,content是完整escape hatch。转发插槽附带安全close方法,不能借自定义取消按钮绕过guard。
direction / handle / snapPoints / nested
vue
<YDrawer v-model:open="open" direction="bottom" :handle="true"
:snap-points="[0.5, 1]" v-model:active-snap-point="snap">
<template #body>分段高度内容</template>
</YDrawer>同一组件可用right/top/left/bottom。nested用于真正的Drawer嵌套;不复制vaul的drag/release、snap或closeThreshold算法。第二形态可运行示例见包内Task Surfaces fixture,不为示例强制改变现有业务UX。
Task guard / dirty / busy
guard 可省略;只读详情不需要草稿或保存按钮。guard读取业务状态,不复制它:
ts
const guard = { dirty: () => draft.value !== baseline.value, busy: () => saving.value }关闭按钮、Escape、允许的outside dismiss和slot/exposed close()共享关闭策略:busy拒绝;dirty显示放弃确认;确认返回后再次检查busy。业务保存成功或身份/页面失效时,父组件直接令open=false是权威终止,不再询问放弃;普通取消必须调用安全close,而不是直接写open=false。
Responsive / long content
组件类型不会随viewport改变。通过ui.content选择宽度,body滚动,footer保留safe-area。页面自己的提交、验证和回读继续由Feature负责。
Nested / focus
子任务应渲染在父任务的body/content插槽内,使用现有YModal或选择组件。上游负责portal/focus trap;父任务guard可把子任务open纳入busy,防止操作绕过。关闭子任务后焦点回到父触发器。focusFirstField()定位首个可编辑输入,focusFirstError()定位aria-invalid字段并滚到可见位置;自定义content完全接管内容结构时,需要自行提供等价焦点接线。
API
Props
| 属性 | 合同 |
|---|---|
| open / defaultOpen | 受控状态 / 非受控初值;defaultOpen默认false |
| guard | 可选TaskSurfaceGuard,dirty/busy均为回调 |
| title / description | 标题与说明 |
| dismissible / modal / overlay | 默认true;语义沿上游;busy时正常dismiss受保护 |
| close / closeIcon | close默认true,可false隐藏或传Button配置;busy叠加disabled |
| portal | 默认true;支持false、目标选择器或HTMLElement |
| inset | 沿上游默认false |
| content / ui / class | 内容事件与布局escape hatch;不会创建新Overlay Runtime |
| direction | bottom默认;可top/left/right |
| handle / handleOnly | handle默认true;handleOnly沿上游false |
| snapPoints / activeSnapPoint | 保留上游类型和受控更新 |
| nested / closeThreshold | 保留上游嵌套/关闭阈值 |
| fixed / preventScrollRestoration / noBodyStyles | 保留上游能力,未传时由上游取默认 |
| shouldScaleBackground / setBackgroundColorOnScale / scrollLockTimeout | 保留上游能力,非Yoyo自研行为 |
Models / Events
update:open(boolean):受控开关请求;close()事件:通过安全关闭提交后触发;close:prevent:被guard或上游dismiss策略阻止。 Drawer还转发 update:activeSnapPoint(number|string)、drag(number)、release(boolean)、animationEnd(boolean)。
Slots / Exposed methods
Slots:default、content、header、title、description、actions、close、body、footer。body/footer及转发插槽得到safe close;不提供业务save。
暴露:close(): Promise<boolean>、focusFirstField(): Promise<void>、focusFirstError(): Promise<void>。父级权威关闭或KeepAlive deactivation使待确认关闭失效,旧确认不得结束新的任务。
Theme / Visual
安装 @yoyoboot/ui-patterns/visual/management.css 和 visual/motion.css。header/footer/body由management foundation持有,不要求Feature复制背景色。组件仅显式标记自己的Task Surface,不要求安装Starter外壳;ui可以覆盖具体布局。支持light/dark、既有density和reduced-motion,无Skin runtime。
Accessibility
提供有意义的title/description;无障碍关联、遮罩、焦点限制与恢复沿Nuxt UI。busy禁止正常取消但不替业务取消服务端写入;字段错误由Feature标记。Esc在dismissible=false时由上游阻止;close按钮和显式close()仍可在guard许可时取消。
Underlying primitive
YDrawer 基于 Nuxt UI UDrawer。官方文档;当前核对版本4.11.2。
Source / Verification
- 源码:
packages/ui-patterns/src/drawer/YDrawer.vue。 - 同源可运行示例:
packages/ui-patterns/tests/fixtures/task-surfaces/App.vue,消费正式公开入口。 - 单测:
packages/ui-patterns/tests/task-surfaces.spec.ts;浏览器:Startertests-e2e/task-surfaces.spec.ts。 - 本页是该组件的 canonical 文档;VitePress 只投影这份正文,不维护第二份公开副本。可运行示例仍消费正式公开入口。
TypeScript 类型
公开入口同时导出 YDrawerProps 与 YDrawerContent,用于调用方命名 props/content。Content 沿用 Nuxt UI 公开合同,消费者不需要直接依赖 reka-ui 或 vaul-vue。