iter: d14 新增抽屉 drawer 组件
This commit is contained in:
@@ -160,6 +160,14 @@
|
|||||||
"acceptance": "新增皮肤层 TableColumn<T> extends ColumnDef<T>,追加 align?: left|center|right 与 format?: (value,row)=>string;DataTableProps.columns 改用它;表头与单元格共用对齐类;format 走 textContent 天然免注入;demo 表格演示年龄右对齐+单位、城市居中;manifest/文档/llms-full.txt 同步;npm run check 全绿",
|
"acceptance": "新增皮肤层 TableColumn<T> extends ColumnDef<T>,追加 align?: left|center|right 与 format?: (value,row)=>string;DataTableProps.columns 改用它;表头与单元格共用对齐类;format 走 textContent 天然免注入;demo 表格演示年龄右对齐+单位、城市居中;manifest/文档/llms-full.txt 同步;npm run check 全绿",
|
||||||
"status": "done",
|
"status": "done",
|
||||||
"notes": "严格分层:内核 ColumnDef 不动,展示字段只在 skin 的 TableColumn 上扩展(extends 而非改核心);对齐类同时作用于 th(取代原 class 函数,合并 ua-sortable);demo 首表展示 align:right + format(v=>`${v} 岁`)、城市居中"
|
"notes": "严格分层:内核 ColumnDef 不动,展示字段只在 skin 的 TableColumn 上扩展(extends 而非改核心);对齐类同时作用于 th(取代原 class 函数,合并 ua-sortable);demo 首表展示 align:right + format(v=>`${v} 岁`)、城市居中"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d14",
|
||||||
|
"title": "新增抽屉 drawer 组件",
|
||||||
|
"motivation": "详情查看/编辑需要不打断主视图的侧滑容器,modal 是强打断确认;移动端尤其常用",
|
||||||
|
"acceptance": "新增 drawer.ts:open 信号驱动,placement 左右、width 可调(窄屏 92vw 上限)、footer 操作区、遮罩 / × / Esc 关闭、打开聚焦首个可交互元素;只消费 --ua-* 令牌 + reduced-motion 兜底;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
|
||||||
|
"status": "done",
|
||||||
|
"notes": "遮罩开合用 opacity+visibility(与 tooltip/popover 同模式),面板靠 transform:translateX 滑入;width 走 inline style(调用方数据驱动的表现值不进 CSS);demo 新增 sec-drawer(订单详情 + footer 关闭 + tag/badge 组合)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-4
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-23T08:55:54.330Z",
|
"generatedAt": "2026-10-23T08:56:29.143Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@ui-arch/ui",
|
"name": "@ui-arch/ui",
|
||||||
"type": "skin-dom",
|
"type": "skin-dom",
|
||||||
"description": "DOM 皮肤组件库:button / textField / textarea / slider / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / tag / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
"description": "DOM 皮肤组件库:button / textField / textarea / slider / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / tag / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / drawer / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
||||||
"tags": [
|
"tags": [
|
||||||
"components",
|
"components",
|
||||||
"dom",
|
"dom",
|
||||||
@@ -166,6 +166,7 @@
|
|||||||
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
||||||
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
||||||
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
||||||
|
"drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭",
|
||||||
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
||||||
"formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄",
|
"formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄",
|
||||||
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core"
|
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core"
|
||||||
@@ -203,7 +204,8 @@
|
|||||||
"DataTableProps",
|
"DataTableProps",
|
||||||
"PopoverProps",
|
"PopoverProps",
|
||||||
"SliderProps",
|
"SliderProps",
|
||||||
"TagProps"
|
"TagProps",
|
||||||
|
"DrawerProps"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"usage": "const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')",
|
"usage": "const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')",
|
||||||
@@ -213,7 +215,7 @@
|
|||||||
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
||||||
],
|
],
|
||||||
"docs": {
|
"docs": {
|
||||||
"intro": "26 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
"intro": "27 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
||||||
"steps": [
|
"steps": [
|
||||||
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
||||||
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
||||||
@@ -1085,6 +1087,61 @@
|
|||||||
"底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm'"
|
"底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm'"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "drawer",
|
||||||
|
"title": "抽屉 Drawer",
|
||||||
|
"demo": "sec-drawer",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "title",
|
||||||
|
"t": "string",
|
||||||
|
"r": 1,
|
||||||
|
"d": "标题"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "open",
|
||||||
|
"t": "Signal<boolean>",
|
||||||
|
"r": 1,
|
||||||
|
"d": "开关信号:true 滑出 / false 收起"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "placement",
|
||||||
|
"t": "'left' | 'right'",
|
||||||
|
"d": "滑出方向;缺省 right",
|
||||||
|
"df": "right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "width",
|
||||||
|
"t": "number",
|
||||||
|
"d": "面板宽度像素(窄屏最多 92vw);缺省 320",
|
||||||
|
"df": "320"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "footer",
|
||||||
|
"t": "unknown",
|
||||||
|
"d": "底部操作区内容;缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onClose",
|
||||||
|
"t": "() => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "关闭回调(点遮罩 / × / Esc 触发)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "...children",
|
||||||
|
"t": "unknown[]",
|
||||||
|
"d": "面板内容子节点"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "const open = signal(false); drawer({ title: '详情', open, width: 360, onClose: () => open(false) }, h('p', null, '内容'))",
|
||||||
|
"steps": [
|
||||||
|
"创建布尔信号 open = signal(false),传给 drawer;其余参数为面板内容子节点",
|
||||||
|
"打开后从侧边滑出:placement: 'right'(默认)或 'left',width 控制宽度(窄屏自动收到 92vw)",
|
||||||
|
"点遮罩、× 或按 Esc 关闭并写回 false;打开时焦点落在面板首个可交互元素",
|
||||||
|
"底部操作按钮放进 footer;与 modal 的分工:modal 是强打断的确认,drawer 保留主视图上下文做详情/编辑",
|
||||||
|
"面板内容与 modal 一样接受任意 DOM 与响应式函数"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "form",
|
"id": "form",
|
||||||
"title": "表单 Form / 表单字段 FormItem",
|
"title": "表单 Form / 表单字段 FormItem",
|
||||||
|
|||||||
@@ -18,3 +18,4 @@
|
|||||||
{"time": "2026-10-23T16:54:36+08:00", "id": "d11", "result": "done", "summary": "modal 新增 size 三档(sm/md/lg)与 footer 操作区(右对齐 + 上分割线),缺省行为不变;demo/manifest/llms 同步,check 全绿"}
|
{"time": "2026-10-23T16:54:36+08:00", "id": "d11", "result": "done", "summary": "modal 新增 size 三档(sm/md/lg)与 footer 操作区(右对齐 + 上分割线),缺省行为不变;demo/manifest/llms 同步,check 全绿"}
|
||||||
{"time": "2026-10-23T16:55:10+08:00", "id": "d12", "result": "done", "summary": "新增可关闭标签 tag(五档语义色 + closable/onClose,无状态委托调用方重渲染);demo 用 renderList 演示真实删除;manifest/llms 同步,check 全绿"}
|
{"time": "2026-10-23T16:55:10+08:00", "id": "d12", "result": "done", "summary": "新增可关闭标签 tag(五档语义色 + closable/onClose,无状态委托调用方重渲染);demo 用 renderList 演示真实删除;manifest/llms 同步,check 全绿"}
|
||||||
{"time": "2026-10-23T16:55:55+08:00", "id": "d13", "result": "done", "summary": "dataTable 列定义新增皮肤层 TableColumn(align + format),内核 ColumnDef 不变;表头/单元格共用对齐类,format 走 textContent;demo/manifest/llms 同步,check 全绿"}
|
{"time": "2026-10-23T16:55:55+08:00", "id": "d13", "result": "done", "summary": "dataTable 列定义新增皮肤层 TableColumn(align + format),内核 ColumnDef 不变;表头/单元格共用对齐类,format 走 textContent;demo/manifest/llms 同步,check 全绿"}
|
||||||
|
{"time": "2026-10-23T16:56:36+08:00", "id": "d14", "result": "done", "summary": "新增抽屉 drawer:open 信号驱动侧滑、placement/width 可配、footer 操作区、遮罩/×/Esc 关闭并聚焦首元素;demo/manifest/llms 同步,check 全绿"}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
card,
|
card,
|
||||||
checkbox,
|
checkbox,
|
||||||
dataTable,
|
dataTable,
|
||||||
|
drawer,
|
||||||
form,
|
form,
|
||||||
formItem,
|
formItem,
|
||||||
menu,
|
menu,
|
||||||
@@ -99,6 +100,9 @@ const formResult = signal('');
|
|||||||
// —— 分页联动状态:dataTable 与独立 pagination 共用同一页码信号 ——
|
// —— 分页联动状态:dataTable 与独立 pagination 共用同一页码信号 ——
|
||||||
const tablePage = signal(1);
|
const tablePage = signal(1);
|
||||||
|
|
||||||
|
// —— 抽屉开关 ——
|
||||||
|
const drawerOpen = signal(false);
|
||||||
|
|
||||||
// —— 标签列表:关闭即写回信号,容器按信号重渲染 ——
|
// —— 标签列表:关闭即写回信号,容器按信号重渲染 ——
|
||||||
const tags = signal(['设计', '前端', '组件库']);
|
const tags = signal(['设计', '前端', '组件库']);
|
||||||
|
|
||||||
@@ -164,6 +168,23 @@ const content = h(
|
|||||||
radioGroup({ options: [{ value: 'x', label: '静态禁用' }], value: size, disabled: true }),
|
radioGroup({ options: [{ value: 'x', label: '静态禁用' }], value: size, disabled: true }),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
section(
|
||||||
|
'sec-drawer',
|
||||||
|
'抽屉',
|
||||||
|
'drawer:open 信号驱动侧滑面板,保留主视图上下文;遮罩 / × / Esc 关闭。',
|
||||||
|
button({ label: '打开抽屉', onClick: () => drawerOpen(true) }),
|
||||||
|
drawer(
|
||||||
|
{
|
||||||
|
title: '订单详情',
|
||||||
|
open: drawerOpen,
|
||||||
|
width: 360,
|
||||||
|
onClose: () => drawerOpen(false),
|
||||||
|
footer: h('div', { class: 'row' }, button({ label: '关闭', variant: 'ghost', onClick: () => drawerOpen(false) })),
|
||||||
|
},
|
||||||
|
h('p', null, '这里是抽屉内容,可放任意 DOM。'),
|
||||||
|
h('div', { class: 'row' }, tag({ label: '#A1024', variant: 'info' }), badge({ label: '已发货', variant: 'success' })),
|
||||||
|
),
|
||||||
|
),
|
||||||
section(
|
section(
|
||||||
'sec-tag',
|
'sec-tag',
|
||||||
'标签',
|
'标签',
|
||||||
|
|||||||
+31
-2
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-23T08:55:54.330Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-23T08:56:29.143Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
## @ui-arch/ui(skin-dom)v0.1.0
|
## @ui-arch/ui(skin-dom)v0.1.0
|
||||||
|
|
||||||
26 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。
|
27 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。
|
||||||
|
|
||||||
### button — 按钮 Button
|
### button — 按钮 Button
|
||||||
|
|
||||||
@@ -615,6 +615,34 @@ const open = signal(false); modal({ title: '确认操作', open, onClose: () =>
|
|||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-tabs-modal
|
现场演示:apps/docs/demo.html#sec-tabs-modal
|
||||||
|
|
||||||
|
### drawer — 抽屉 Drawer
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- title(必填):string —— 标题
|
||||||
|
- open(必填):Signal<boolean> —— 开关信号:true 滑出 / false 收起
|
||||||
|
- placement(默认 right):'left' | 'right' —— 滑出方向;缺省 right
|
||||||
|
- width(默认 320):number —— 面板宽度像素(窄屏最多 92vw);缺省 320
|
||||||
|
- footer:unknown —— 底部操作区内容;缺省不渲染
|
||||||
|
- onClose(回调):() => void —— 关闭回调(点遮罩 / × / Esc 触发)
|
||||||
|
- ...children:unknown[] —— 面板内容子节点
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
const open = signal(false); drawer({ title: '详情', open, width: 360, onClose: () => open(false) }, h('p', null, '内容'))
|
||||||
|
```
|
||||||
|
|
||||||
|
手把手:
|
||||||
|
|
||||||
|
1. 创建布尔信号 open = signal(false),传给 drawer;其余参数为面板内容子节点
|
||||||
|
2. 打开后从侧边滑出:placement: 'right'(默认)或 'left',width 控制宽度(窄屏自动收到 92vw)
|
||||||
|
3. 点遮罩、× 或按 Esc 关闭并写回 false;打开时焦点落在面板首个可交互元素
|
||||||
|
4. 底部操作按钮放进 footer;与 modal 的分工:modal 是强打断的确认,drawer 保留主视图上下文做详情/编辑
|
||||||
|
5. 面板内容与 modal 一样接受任意 DOM 与响应式函数
|
||||||
|
|
||||||
|
现场演示:apps/docs/demo.html#sec-drawer
|
||||||
|
|
||||||
### form — 表单 Form / 表单字段 FormItem
|
### form — 表单 Form / 表单字段 FormItem
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
@@ -704,6 +732,7 @@ API 速览:
|
|||||||
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
||||||
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
||||||
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
||||||
|
- drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭
|
||||||
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
||||||
- formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄
|
- formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄
|
||||||
- dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core
|
- dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
## @ui-arch/ui (v0.1.0)
|
## @ui-arch/ui (v0.1.0)
|
||||||
|
|
||||||
- 类型: skin-dom · 目录: packages/skin/ui
|
- 类型: skin-dom · 目录: packages/skin/ui
|
||||||
- 描述: DOM 皮肤组件库:button / textField / textarea / slider / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / tag / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。
|
- 描述: DOM 皮肤组件库:button / textField / textarea / slider / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / tag / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / drawer / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。
|
||||||
- API:
|
- API:
|
||||||
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用
|
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用
|
||||||
- textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号禁用
|
- textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号禁用
|
||||||
@@ -71,6 +71,7 @@
|
|||||||
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
||||||
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
||||||
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
||||||
|
- drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭
|
||||||
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
||||||
- formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄
|
- formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄
|
||||||
- dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core
|
- dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { effect, h } from '@ui-arch/frame';
|
||||||
|
import type { DrawerProps } from './types.js';
|
||||||
|
|
||||||
|
/*** 抽屉:open 信号驱动侧滑面板,点遮罩 / × / Esc 关闭;与 modal 的分工是不打断主视图、可从侧边保留上下文 ***/
|
||||||
|
export function drawer(props: DrawerProps, ...children: unknown[]): HTMLElement {
|
||||||
|
const close = (): void => {
|
||||||
|
if (!props.open()) return; // 卫语句:已关闭不重复触发回调
|
||||||
|
props.open(false);
|
||||||
|
props.onClose?.();
|
||||||
|
};
|
||||||
|
const overlay = h('div', {
|
||||||
|
class: () => `ua-drawer-overlay${props.open() ? ' ua-open' : ''}`,
|
||||||
|
onClick: (e: Event) => {
|
||||||
|
if (e.target === overlay) close(); // 卫语句:仅点遮罩本身关闭
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const panel = h(
|
||||||
|
'aside',
|
||||||
|
{
|
||||||
|
class: `ua-drawer ua-drawer-${props.placement ?? 'right'}`,
|
||||||
|
style: `width:${props.width ?? 320}px`,
|
||||||
|
role: 'dialog',
|
||||||
|
'aria-hidden': () => String(!props.open()),
|
||||||
|
},
|
||||||
|
h('div', { class: 'ua-drawer-head' }, h('h3', { class: 'ua-drawer-title' }, props.title), h('button', { class: 'ua-drawer-close', 'aria-label': '关闭抽屉', onClick: close }, '×')),
|
||||||
|
h('div', { class: 'ua-drawer-body' }, ...children),
|
||||||
|
props.footer ? h('div', { class: 'ua-drawer-foot' }, props.footer) : null,
|
||||||
|
);
|
||||||
|
overlay.append(panel);
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!props.open()) return;
|
||||||
|
(panel.querySelector('button, input, textarea') as HTMLElement | null)?.focus(); // 打开时聚焦首个可交互元素
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape' && props.open()) close();
|
||||||
|
});
|
||||||
|
return overlay;
|
||||||
|
}
|
||||||
@@ -23,5 +23,6 @@ export { spinner } from './spinner.js';
|
|||||||
export { progress } from './progress.js';
|
export { progress } from './progress.js';
|
||||||
export { tabs } from './tabs.js';
|
export { tabs } from './tabs.js';
|
||||||
export { modal } from './modal.js';
|
export { modal } from './modal.js';
|
||||||
|
export { drawer } from './drawer.js';
|
||||||
export { form, formItem } from './form.js';
|
export { form, formItem } from './form.js';
|
||||||
export { dataTable } from './dataTable.js';
|
export { dataTable } from './dataTable.js';
|
||||||
|
|||||||
@@ -232,6 +232,16 @@ export interface ModalProps {
|
|||||||
onClose?: () => void; // 关闭回调(点遮罩、× 或 Esc 触发)
|
onClose?: () => void; // 关闭回调(点遮罩、× 或 Esc 触发)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 抽屉入参 ***/
|
||||||
|
export interface DrawerProps {
|
||||||
|
title: string; // 标题
|
||||||
|
open: Signal<boolean>; // 开关信号:true 滑出 / false 收起
|
||||||
|
placement?: 'left' | 'right'; // 滑出方向;缺省 right
|
||||||
|
width?: number; // 面板宽度像素;缺省 320
|
||||||
|
footer?: unknown; // 底部操作区内容;缺省不渲染
|
||||||
|
onClose?: () => void; // 关闭回调(遮罩 / × / Esc 触发)
|
||||||
|
}
|
||||||
|
|
||||||
/*** 表单校验规则:按序执行,首个失败即停止 ***/
|
/*** 表单校验规则:按序执行,首个失败即停止 ***/
|
||||||
export interface FormRule {
|
export interface FormRule {
|
||||||
required?: boolean; // 非空校验(trim 后判定)
|
required?: boolean; // 非空校验(trim 后判定)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@ui-arch/ui",
|
"name": "@ui-arch/ui",
|
||||||
"type": "skin-dom",
|
"type": "skin-dom",
|
||||||
"description": "DOM 皮肤组件库:button / textField / textarea / slider / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / tag / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
"description": "DOM 皮肤组件库:button / textField / textarea / slider / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / tag / alert / toast / tooltip / popover / skeleton / spinner / progress / tabs / pagination / modal / drawer / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
||||||
"tags": [
|
"tags": [
|
||||||
"components",
|
"components",
|
||||||
"dom",
|
"dom",
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
||||||
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
||||||
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
||||||
|
"drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭",
|
||||||
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
||||||
"formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄",
|
"formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄",
|
||||||
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core"
|
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core"
|
||||||
@@ -74,7 +75,8 @@
|
|||||||
"DataTableProps",
|
"DataTableProps",
|
||||||
"PopoverProps",
|
"PopoverProps",
|
||||||
"SliderProps",
|
"SliderProps",
|
||||||
"TagProps"
|
"TagProps",
|
||||||
|
"DrawerProps"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"usage": "const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')",
|
"usage": "const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')",
|
||||||
@@ -84,7 +86,7 @@
|
|||||||
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
||||||
],
|
],
|
||||||
"docs": {
|
"docs": {
|
||||||
"intro": "26 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
"intro": "27 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
||||||
"steps": [
|
"steps": [
|
||||||
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
||||||
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
||||||
@@ -956,6 +958,61 @@
|
|||||||
"底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm'"
|
"底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm'"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "drawer",
|
||||||
|
"title": "抽屉 Drawer",
|
||||||
|
"demo": "sec-drawer",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "title",
|
||||||
|
"t": "string",
|
||||||
|
"r": 1,
|
||||||
|
"d": "标题"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "open",
|
||||||
|
"t": "Signal<boolean>",
|
||||||
|
"r": 1,
|
||||||
|
"d": "开关信号:true 滑出 / false 收起"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "placement",
|
||||||
|
"t": "'left' | 'right'",
|
||||||
|
"d": "滑出方向;缺省 right",
|
||||||
|
"df": "right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "width",
|
||||||
|
"t": "number",
|
||||||
|
"d": "面板宽度像素(窄屏最多 92vw);缺省 320",
|
||||||
|
"df": "320"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "footer",
|
||||||
|
"t": "unknown",
|
||||||
|
"d": "底部操作区内容;缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onClose",
|
||||||
|
"t": "() => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "关闭回调(点遮罩 / × / Esc 触发)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "...children",
|
||||||
|
"t": "unknown[]",
|
||||||
|
"d": "面板内容子节点"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "const open = signal(false); drawer({ title: '详情', open, width: 360, onClose: () => open(false) }, h('p', null, '内容'))",
|
||||||
|
"steps": [
|
||||||
|
"创建布尔信号 open = signal(false),传给 drawer;其余参数为面板内容子节点",
|
||||||
|
"打开后从侧边滑出:placement: 'right'(默认)或 'left',width 控制宽度(窄屏自动收到 92vw)",
|
||||||
|
"点遮罩、× 或按 Esc 关闭并写回 false;打开时焦点落在面板首个可交互元素",
|
||||||
|
"底部操作按钮放进 footer;与 modal 的分工:modal 是强打断的确认,drawer 保留主视图上下文做详情/编辑",
|
||||||
|
"面板内容与 modal 一样接受任意 DOM 与响应式函数"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "form",
|
"id": "form",
|
||||||
"title": "表单 Form / 表单字段 FormItem",
|
"title": "表单 Form / 表单字段 FormItem",
|
||||||
|
|||||||
+79
-1
@@ -1014,6 +1014,82 @@
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* —— 抽屉(侧滑面板) —— */
|
||||||
|
.ua-drawer-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 90;
|
||||||
|
background: rgba(0, 0, 0, 0.36);
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: opacity 0.22s ease, visibility 0.22s;
|
||||||
|
}
|
||||||
|
.ua-drawer-overlay.ua-open {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.ua-drawer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 92vw;
|
||||||
|
background: var(--ua-color-surface);
|
||||||
|
border-left: 1px solid var(--ua-color-border);
|
||||||
|
box-shadow: 0 0 40px rgba(0, 0, 0, 0.22);
|
||||||
|
transition: transform 0.24s ease;
|
||||||
|
}
|
||||||
|
.ua-drawer-right {
|
||||||
|
right: 0;
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
.ua-drawer-left {
|
||||||
|
left: 0;
|
||||||
|
border-left: none;
|
||||||
|
border-right: 1px solid var(--ua-color-border);
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
.ua-drawer-overlay.ua-open .ua-drawer-right,
|
||||||
|
.ua-drawer-overlay.ua-open .ua-drawer-left {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
.ua-drawer-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--ua-space-2);
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid var(--ua-color-border);
|
||||||
|
}
|
||||||
|
.ua-drawer-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: var(--ua-font-size-lg);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.ua-drawer-close {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: var(--ua-color-text-muted);
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.ua-drawer-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 16px;
|
||||||
|
color: var(--ua-color-text);
|
||||||
|
font-size: var(--ua-font-size-md);
|
||||||
|
}
|
||||||
|
.ua-drawer-foot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--ua-space-2);
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-top: 1px solid var(--ua-color-border);
|
||||||
|
}
|
||||||
|
|
||||||
/* —— 弹窗 —— */
|
/* —— 弹窗 —— */
|
||||||
.ua-modal-overlay {
|
.ua-modal-overlay {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -1182,7 +1258,9 @@
|
|||||||
.ua-skeleton::after,
|
.ua-skeleton::after,
|
||||||
.ua-form-error,
|
.ua-form-error,
|
||||||
.ua-modal,
|
.ua-modal,
|
||||||
.ua-modal-overlay {
|
.ua-modal-overlay,
|
||||||
|
.ua-drawer,
|
||||||
|
.ua-drawer-overlay {
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user