diff --git a/.ua/backlog.json b/.ua/backlog.json index 77f7789..5e46db8 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -1124,6 +1124,14 @@ "acceptance": "StatisticProps 新增 countdown(目标时间戳毫秒),传了即每秒显示 DD 天 HH:MM:SS(不足一天省天数),value 转为可选;到点或组件被移除后自动 clearInterval,不留常驻定时器;倒计时不走滚动动画;文档与演示同步;npm run check 全绿", "notes": "自补:功能补齐;停表条件放在 tick 内自检 isConnected,无需额外卸载钩子", "status": "done" + }, + { + "id": "g96", + "title": "抽屉支持上下滑出方位", + "motivation": "drawer 的 placement 只有 left/right,移动端最常见的「底部弹层」只能拿 modal 或者自己写 fixed 容器凑,既丢了焦点陷阱与 Esc 栈,样式也不统一", + "acceptance": "placement 扩到 'left' | 'right' | 'top' | 'bottom';上下滑出时 width 语义变为高度、横向自动铺满(max-width 放开),入场位移走 translateY,四条方位共用同一套遮罩/焦点陷阱/Esc 栈;文档与演示(底部抽屉实例)同步;npm run check 全绿", + "notes": "自补:组件能力补齐;只加 CSS 分支与一个三元表达尺寸,逻辑零改动", + "status": "open" } ] } diff --git a/.ua/index.json b/.ua/index.json index dbb6bbc..4db4b5e 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T05:13:47.565Z", + "generatedAt": "2026-10-26T05:14:42.470Z", "repoRoot": ".", "packages": [ { @@ -1619,14 +1619,14 @@ }, { "n": "placement", - "t": "'left' | 'right'", + "t": "'left' | 'right' | 'top' | 'bottom'", "d": "滑出方向;缺省 right", "df": "right" }, { "n": "width", "t": "number", - "d": "面板宽度像素(窄屏最多 92vw);缺省 320", + "d": "面板尺寸(像素):左右滑出为宽度,上下滑出为高度;缺省 320", "df": "320" }, { diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 6e13e3d..90be537 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -139,6 +139,7 @@ const name = signal(''); // —— 表单控件状态集中声明 —— const agreed = signal(false); const nightMode = signal(true); +const sheetOpen = signal(false); // 底部抽屉开合 const sound = signal(true); // 无可见文案的开关演示 const city = signal(''); const size = signal('m'); @@ -279,8 +280,11 @@ const content = h( section( 'sec-drawer', '抽屉', - 'drawer:open 信号驱动侧滑面板,保留主视图上下文;遮罩 / × / Esc 关闭。', - button({ label: '打开抽屉', onClick: () => drawerOpen(true) }), + 'drawer:open 信号驱动侧滑面板,保留主视图上下文;四向滑出(左右按宽度、上下按高度),遮罩 / × / Esc 关闭。', + h('div', { class: 'row' }, + button({ label: '从右侧打开', onClick: () => drawerOpen(true) }), + button({ label: '从底部打开', variant: 'ghost', onClick: () => sheetOpen(true) }), + ), drawer( { title: '订单详情', @@ -292,6 +296,10 @@ const content = h( h('p', null, '这里是抽屉内容,可放任意 DOM。'), h('div', { class: 'row' }, tag({ label: '#A1024', variant: 'info' }), badge({ label: '已发货', variant: 'success' })), ), + drawer( + { title: '快捷操作', open: sheetOpen, placement: 'bottom', width: 220, onClose: () => sheetOpen(false) }, + h('p', null, '从底部滑出的操作面板:适合移动端底部弹层,宽度自动铺满、高度由 width 指定。'), + ), ), section( 'sec-tag', diff --git a/llms-full.txt b/llms-full.txt index c32c79c..a196b6a 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T05:13:47.565Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T05:14:42.470Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -868,8 +868,8 @@ const open = signal(false); modal({ title: '确认操作', open, onClose: () => - title(必填):string —— 标题 - open(必填):Signal —— 开关信号:true 滑出 / false 收起 -- placement(默认 right):'left' | 'right' —— 滑出方向;缺省 right -- width(默认 320):number —— 面板宽度像素(窄屏最多 92vw);缺省 320 +- placement(默认 right):'left' | 'right' | 'top' | 'bottom' —— 滑出方向;缺省 right +- width(默认 320):number —— 面板尺寸(像素):左右滑出为宽度,上下滑出为高度;缺省 320 - footer:unknown —— 底部操作区内容;缺省不渲染 - onClose(回调):() => void —— 关闭回调(点遮罩 / × / Esc 触发) - ...children:unknown[] —— 面板内容子节点 diff --git a/packages/skin/ui/src/drawer.ts b/packages/skin/ui/src/drawer.ts index 717e992..e53f4ae 100644 --- a/packages/skin/ui/src/drawer.ts +++ b/packages/skin/ui/src/drawer.ts @@ -22,7 +22,7 @@ export function drawer(props: DrawerProps, ...children: unknown[]): HTMLElement 'aside', { class: `ua-drawer ua-drawer-${props.placement ?? 'right'}`, - style: `width:${props.width ?? 320}px`, + style: `${props.placement === 'top' || props.placement === 'bottom' ? 'height' : 'width'}:${props.width ?? 320}px`, // 上下滑出改高度 role: 'dialog', 'aria-labelledby': titleId, 'aria-hidden': () => String(!props.open()), diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index c82793c..0f1f4a0 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -343,8 +343,8 @@ export interface ModalProps { export interface DrawerProps { title: string; // 标题 open: Signal; // 开关信号:true 滑出 / false 收起 - placement?: 'left' | 'right'; // 滑出方向;缺省 right - width?: number; // 面板宽度像素;缺省 320 + placement?: 'left' | 'right' | 'top' | 'bottom'; // 滑出方向;缺省 right + width?: number; // 面板尺寸像素:左右滑出为宽度,上下滑出为高度;缺省 320 footer?: unknown; // 底部操作区内容;缺省不渲染 onClose?: () => void; // 关闭回调(遮罩 / × / Esc 触发) } diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index f965e70..1e579b5 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -1490,14 +1490,14 @@ }, { "n": "placement", - "t": "'left' | 'right'", + "t": "'left' | 'right' | 'top' | 'bottom'", "d": "滑出方向;缺省 right", "df": "right" }, { "n": "width", "t": "number", - "d": "面板宽度像素(窄屏最多 92vw);缺省 320", + "d": "面板尺寸(像素):左右滑出为宽度,上下滑出为高度;缺省 320", "df": "320" }, { diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index f3b6f6e..b188d9f 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -1576,8 +1576,30 @@ button.ua-step-dot:hover { border-right: 1px solid var(--ua-color-border); transform: translateX(-100%); } +.ua-drawer-top, +.ua-drawer-bottom { + left: 0; + right: 0; + bottom: auto; + max-width: none; + border-left: none; + border-bottom: 1px solid var(--ua-color-border); +} +.ua-drawer-top { + top: 0; + transform: translateY(-100%); +} +.ua-drawer-bottom { + top: auto; + bottom: 0; + border-bottom: none; + border-top: 1px solid var(--ua-color-border); + transform: translateY(100%); +} .ua-drawer-overlay.ua-open .ua-drawer-right, -.ua-drawer-overlay.ua-open .ua-drawer-left { +.ua-drawer-overlay.ua-open .ua-drawer-left, +.ua-drawer-overlay.ua-open .ua-drawer-top, +.ua-drawer-overlay.ua-open .ua-drawer-bottom { transform: none; } .ua-drawer-head {