From d1a2864470621437fe64e1fc5b7769daa689ede9 Mon Sep 17 00:00:00 2001 From: ui-arch-bot Date: Fri, 23 Oct 2026 16:54:36 +0800 Subject: [PATCH] =?UTF-8?q?iter:=20d11=20modal=20=E8=A1=A5=20size=20?= =?UTF-8?q?=E6=A1=A3=E4=BD=8D=E4=B8=8E=20footer=20=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/backlog.json | 8 ++++++++ .ua/index.json | 18 +++++++++++++++--- .ua/iterations.log | 1 + apps/demo/main.ts | 17 ++++++++++++++--- llms-full.txt | 7 +++++-- llms.txt | 2 +- packages/skin/ui/src/modal.ts | 3 ++- packages/skin/ui/src/types.ts | 2 ++ packages/skin/ui/ua.manifest.json | 16 ++++++++++++++-- packages/skin/ui/ui.css | 14 ++++++++++++++ 10 files changed, 76 insertions(+), 12 deletions(-) diff --git a/.ua/backlog.json b/.ua/backlog.json index a166119..2e64451 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -136,6 +136,14 @@ "acceptance": "ToastProps 新增 closable(显示 × 立即退场);同屏最多 4 条,超出挤掉最早一条;退场逻辑抽为内部 dismiss() 供自动/手动共用;is-closable 才接收指针事件,其余保持 pointer-events:none;manifest/文档/llms-full.txt/demo 同步;npm run check 全绿", "status": "done", "notes": "dismiss() 统一 transitionend + 320ms 双保险;超限用 firstElementChild.remove() 立即腾位(不走动画,保证硬上限);demo sec-toast 增加「可关闭」按钮演示 showToast({closable:true})" + }, + { + "id": "d11", + "title": "modal 补 size 档位与 footer 操作区", + "motivation": "弹窗内容宽窄差异大(确认框 vs 表单),底部操作按钮只能塞进 body 导致分割线语义缺失", + "acceptance": "ModalProps 新增 size?: 'sm'|'md'|'lg'(340/420/640)与 footer?: unknown;footer 渲染右对齐操作区带上分割线;缺省行为与旧版一致;manifest/文档/llms-full.txt/demo 同步;npm run check 全绿", + "status": "done", + "notes": "宽度档位用 .ua-modal-sm/lg 覆盖基准 md,基础宽 min(420px,90vw) 不动;footer 仅在传入时渲染(null 分支不进 DOM);demo 的弹窗改为 size:'lg' + 取消/确定 footer 按钮" } ] } diff --git a/.ua/index.json b/.ua/index.json index 9431158..aa50c58 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-23T08:53:43.712Z", + "generatedAt": "2026-10-23T08:54:30.235Z", "repoRoot": ".", "packages": [ { @@ -164,7 +164,7 @@ "spinner(props):加载指示器,size 控制直径", "progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色", "tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签", - "modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭", + "modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区", "form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit", "formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄", "dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core" @@ -1012,6 +1012,17 @@ "r": 1, "d": "标题" }, + { + "n": "size", + "t": "'sm' | 'md' | 'lg'", + "d": "宽度档位:sm 340px / md 420px / lg 640px;缺省 md", + "df": "md" + }, + { + "n": "footer", + "t": "unknown", + "d": "底部操作区内容(常见为按钮组,右对齐);缺省不渲染" + }, { "n": "onClose", "t": "() => void", @@ -1029,7 +1040,8 @@ "创建布尔信号 open = signal(false),弹窗初始隐藏", "把 open 传给 modal,其余参数为弹窗内容子节点", "打开:open(true);关闭:open(false),点遮罩或 × 会自动写回 false", - "入场自带遮罩淡入 + 弹窗上浮动画" + "入场自带遮罩淡入 + 弹窗上浮动画", + "底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm'" ] }, { diff --git a/.ua/iterations.log b/.ua/iterations.log index 4728ee8..ee4460f 100644 --- a/.ua/iterations.log +++ b/.ua/iterations.log @@ -15,3 +15,4 @@ {"time": "2026-10-23T16:52:28+08:00", "id": "d08", "result": "done", "summary": "演示页改双栏 shell:sticky 侧边目录由 .sec 自动反推 + IntersectionObserver 滚动高亮,窄屏隐藏、reduced-motion 关平滑滚动;check 全绿"} {"time": "2026-10-23T16:53:06+08:00", "id": "d09", "result": "done", "summary": "新增滑块 slider:value 信号双向绑定 + min/max/step + 布尔|信号禁用 + showValue,原生 range 承载键盘与触屏,accent-color 令牌化;manifest/llms/demo 同步,check 全绿"} {"time": "2026-10-23T16:53:44+08:00", "id": "d10", "result": "done", "summary": "toast 新增 closable(× 手动关闭)与同屏上限 4 条,退场统一走 dismiss() 双兜底;is-closable 才接收指针;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 全绿"} diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 354919b..d4c88fe 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -241,9 +241,20 @@ const content = h( h('span', { class: 'out' }, () => `当前页签:${activeTab()}`), button({ label: '打开弹窗', onClick: () => modalOpen(true) }), modal( - { title: '确认操作', open: modalOpen, onClose: () => modalOpen(false) }, - h('p', null, '这是一个由信号驱动的弹窗组件。'), - h('div', { class: 'row' }, button({ label: '知道了', onClick: () => modalOpen(false) }), badge({ label: 'modal', variant: 'success' })), + { + title: '确认操作', + open: modalOpen, + size: 'lg', + onClose: () => modalOpen(false), + footer: h( + 'div', + { class: 'row' }, + button({ label: '取消', variant: 'ghost', onClick: () => modalOpen(false) }), + button({ label: '确定', onClick: () => { modalOpen(false); toast.success('已确认'); } }), + ), + }, + h('p', null, '这是一个由信号驱动的弹窗组件(size: lg + footer 操作区)。'), + h('div', { class: 'row' }, badge({ label: 'modal', variant: 'success' }), badge({ label: 'footer', variant: 'default' })), ), ), section( diff --git a/llms-full.txt b/llms-full.txt index cdd8998..38f2d60 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-23T08:53:43.712Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-23T08:54:30.235Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -569,6 +569,8 @@ const page = signal(1); pagination({ page, pageSize: 3, total: 24, onChange: (p) - open(必填):Signal —— 开关信号:true 显示 / false 隐藏 - title(必填):string —— 标题 +- size(默认 md):'sm' | 'md' | 'lg' —— 宽度档位:sm 340px / md 420px / lg 640px;缺省 md +- footer:unknown —— 底部操作区内容(常见为按钮组,右对齐);缺省不渲染 - onClose(回调):() => void —— 关闭回调(点遮罩、× 或 Esc 触发) - ...children:unknown[] —— 弹窗内容子节点 @@ -584,6 +586,7 @@ const open = signal(false); modal({ title: '确认操作', open, onClose: () => 2. 把 open 传给 modal,其余参数为弹窗内容子节点 3. 打开:open(true);关闭:open(false),点遮罩或 × 会自动写回 false 4. 入场自带遮罩淡入 + 弹窗上浮动画 +5. 底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm' 现场演示:apps/docs/demo.html#sec-tabs-modal @@ -673,7 +676,7 @@ API 速览: - spinner(props):加载指示器,size 控制直径 - progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色 - tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签 -- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭 +- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区 - form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit - formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄 - dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core diff --git a/llms.txt b/llms.txt index 35f700c..d3f5137 100644 --- a/llms.txt +++ b/llms.txt @@ -69,7 +69,7 @@ - spinner(props):加载指示器,size 控制直径 - progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色 - tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签 - - modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭 + - modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区 - form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit - formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄 - dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core diff --git a/packages/skin/ui/src/modal.ts b/packages/skin/ui/src/modal.ts index f5e0a7c..6f43582 100644 --- a/packages/skin/ui/src/modal.ts +++ b/packages/skin/ui/src/modal.ts @@ -16,9 +16,10 @@ export function modal(props: ModalProps, ...children: unknown[]): HTMLElement { }); const box = h( 'div', - { class: 'ua-modal' }, + { class: `ua-modal ua-modal-${props.size ?? 'md'}` }, h('div', { class: 'ua-modal-head' }, h('h3', { class: 'ua-modal-title' }, props.title), h('button', { class: 'ua-modal-close', onClick: close }, '×')), h('div', { class: 'ua-modal-body' }, ...children), + props.footer ? h('div', { class: 'ua-modal-foot' }, props.footer) : null, // 底部操作区:右对齐,仅传了才渲染 ); overlay.append(box); effect(() => { diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 83ef5d5..4528c2b 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -213,6 +213,8 @@ export interface TabsProps { export interface ModalProps { title: string; // 标题 open: Signal; // 开关信号:true 显示 / false 隐藏 + size?: 'sm' | 'md' | 'lg'; // 宽度档位;缺省 md(420px),sm 340px / lg 640px + footer?: unknown; // 底部操作区内容(常见为按钮组);缺省不渲染 onClose?: () => void; // 关闭回调(点遮罩、× 或 Esc 触发) } diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 554e2be..e6026df 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -35,7 +35,7 @@ "spinner(props):加载指示器,size 控制直径", "progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色", "tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签", - "modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭", + "modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区", "form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit", "formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄", "dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core" @@ -883,6 +883,17 @@ "r": 1, "d": "标题" }, + { + "n": "size", + "t": "'sm' | 'md' | 'lg'", + "d": "宽度档位:sm 340px / md 420px / lg 640px;缺省 md", + "df": "md" + }, + { + "n": "footer", + "t": "unknown", + "d": "底部操作区内容(常见为按钮组,右对齐);缺省不渲染" + }, { "n": "onClose", "t": "() => void", @@ -900,7 +911,8 @@ "创建布尔信号 open = signal(false),弹窗初始隐藏", "把 open 传给 modal,其余参数为弹窗内容子节点", "打开:open(true);关闭:open(false),点遮罩或 × 会自动写回 false", - "入场自带遮罩淡入 + 弹窗上浮动画" + "入场自带遮罩淡入 + 弹窗上浮动画", + "底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm'" ] }, { diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index 236098b..c92d436 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -984,6 +984,20 @@ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2); animation: ua-pop-in 0.22s cubic-bezier(0.2, 0.9, 0.3, 1.2); } +/* 宽度档位:md 为基准,sm / lg 覆盖 */ +.ua-modal-sm { + width: min(340px, 90vw); +} +.ua-modal-lg { + width: min(640px, 92vw); +} +.ua-modal-foot { + display: flex; + justify-content: flex-end; + gap: var(--ua-space-2); + padding: 12px 16px; + border-top: 1px solid var(--ua-color-border); +} @keyframes ua-fade { from { opacity: 0;