From 9cfdb43a4f8536828be0f5e8839816bf5efb5c6c Mon Sep 17 00:00:00 2001 From: ui-arch-bot Date: Fri, 23 Oct 2026 17:02:24 +0800 Subject: [PATCH] =?UTF-8?q?iter:=20d20=20progress=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=B8=8D=E7=A1=AE=E5=AE=9A=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/backlog.json | 8 ++++++++ .ua/index.json | 11 ++++++----- .ua/iterations.log | 1 + apps/demo/main.ts | 1 + llms-full.txt | 16 +++++++++------- llms.txt | 2 +- packages/skin/ui/src/progress.ts | 27 +++++++++++++++++++++++---- packages/skin/ui/src/types.ts | 4 ++-- packages/skin/ui/ua.manifest.json | 9 +++++---- packages/skin/ui/ui.css | 17 +++++++++++++++++ 10 files changed, 73 insertions(+), 23 deletions(-) diff --git a/.ua/backlog.json b/.ua/backlog.json index 975a2c9..cba646d 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -208,6 +208,14 @@ "acceptance": "DataTableProps 新增 selected?: Signal,与内核选择双向同步(内容比较避免来回派发,外部数组拷贝保护内核);demo 首表传 picked 信号,展示已选计数与「清空选择」按钮;manifest/文档/llms-full.txt 同步;npm run check 全绿", "status": "done", "notes": "复用 page 桥接的同一套思路:两个 effect 各管一向,sameKeys 内容比较收敛;内部写回拷贝 [...internal],防外部改动内核数组;demo 需 import type { RowKey } 才能对齐 Signal 的逆变" + }, + { + "id": "d20", + "title": "progress 支持不确定态(未知时长加载)", + "motivation": "请求时长未知时只能硬编造一个假百分比,或用 spinner 但失去条状形态;不确定态是进度条的标准形态", + "acceptance": "ProgressProps.value 改为可选;缺省时渲染不确定态(35% 填充块 1.2s 往复)、不显示百分比、role=progressbar + aria-label=进行中;定量态补 aria-valuemin/max/now;reduced-motion 静态化;demo 加不确定态示例;manifest/文档/llms-full.txt 同步;npm run check 全绿", + "status": "done", + "notes": "以「不传 value」作为不确定态的开关,不新增布尔参数(少一个可矛盾的状态);定量态的 aria-valuenow 用函数属性随信号更新;reduced-motion 下靠既有 .ua-progress-fill 规则把动画归零,退化为静态 35% 条" } ] } diff --git a/.ua/index.json b/.ua/index.json index 5ccb2de..910f9bd 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-23T09:01:45.831Z", + "generatedAt": "2026-10-23T09:02:18.149Z", "repoRoot": ".", "packages": [ { @@ -163,7 +163,7 @@ "skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画", "pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达", "spinner(props):加载指示器,size 控制直径", - "progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色", + "progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复", "tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签", "modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区", "drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭", @@ -950,8 +950,7 @@ { "n": "value", "t": "Signal", - "r": 1, - "d": "进度 0-100 信号,变化自动平滑过渡" + "d": "当前进度 0-100 信号,变化自动平滑过渡(越界钳制)" }, { "n": "showText", @@ -962,10 +961,12 @@ ], "example": "const pct = signal(0); progress({ value: pct }); setInterval(() => pct((pct() + 5) % 105), 400)", "steps": [ + "时长未知的加载直接 progress() 不传 value:填充块匀速往复,不显示百分比,读屏读作进行中", "创建数值信号 pct = signal(0),范围 0-100(越界自动钳制)", "传入 value,填充宽度与百分比文字随信号平滑过渡", "进行中填充带流动条纹,到 100% 自动变 success 色并停止条纹", - "不需要百分比文字时传 showText: false" + "不需要百分比文字时传 showText: false", + "定量进度用 value 信号:满格自动切 success 色并停止条纹,role=progressbar + aria-valuenow 同步" ] }, { diff --git a/.ua/iterations.log b/.ua/iterations.log index 1940b87..27ec38f 100644 --- a/.ua/iterations.log +++ b/.ua/iterations.log @@ -24,3 +24,4 @@ {"time": "2026-10-23T16:59:46+08:00", "id": "d17", "result": "done", "summary": "表单错误态打通包裹式控件:focusout/change 取代 blur + aria-invalid + .ua-invalid 后代选择器覆盖 select/radio/slider;demo 加必填 select;check 全绿"} {"time": "2026-10-23T17:00:55+08:00", "id": "d18", "result": "done", "summary": "ua verify 新增皮肤硬编码颜色门禁(#hex 禁止,rgba 仅限阴影/遮罩),清掉 ui.css 5 处 #fff 改令牌;负向测试确认能拦;check 全绿"} {"time": "2026-10-23T17:01:46+08:00", "id": "d19", "result": "done", "summary": "dataTable 新增 selected 受控信号:与内核选择双向同步(内容比较 + 拷贝保护),demo 展示已选计数与清空;check 全绿"} +{"time": "2026-10-23T17:02:24+08:00", "id": "d20", "result": "done", "summary": "progress 支持不确定态:value 可选,缺省时 35% 填充块往复 + aria-label 进行中;定量态补 aria-valuenow;demo/manifest/llms 同步,check 全绿"} diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 9a89152..9adc563 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -230,6 +230,7 @@ const content = h( alert({ message: '警告提示:操作可能存在风险。', variant: 'warning' }), h('div', { class: 'row' }, spinner(), spinner({ size: 26 })), progress({ value: percent }), + h('div', { class: 'row' }, h('span', { class: 'out' }, '不确定态:'), progress()), ), section( 'sec-tooltip', diff --git a/llms-full.txt b/llms-full.txt index c209794..488407e 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-23T09:01:45.831Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-23T09:02:18.149Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -530,7 +530,7 @@ spinner(); spinner({ size: 26 }) 参数: -- value(必填):Signal —— 进度 0-100 信号,变化自动平滑过渡 +- value:Signal —— 当前进度 0-100 信号,变化自动平滑过渡(越界钳制) - showText(默认 true):boolean —— 是否显示百分比文字;缺省 true 示例: @@ -541,10 +541,12 @@ const pct = signal(0); progress({ value: pct }); setInterval(() => pct((pct() + 手把手: -1. 创建数值信号 pct = signal(0),范围 0-100(越界自动钳制) -2. 传入 value,填充宽度与百分比文字随信号平滑过渡 -3. 进行中填充带流动条纹,到 100% 自动变 success 色并停止条纹 -4. 不需要百分比文字时传 showText: false +1. 时长未知的加载直接 progress() 不传 value:填充块匀速往复,不显示百分比,读屏读作进行中 +2. 创建数值信号 pct = signal(0),范围 0-100(越界自动钳制) +3. 传入 value,填充宽度与百分比文字随信号平滑过渡 +4. 进行中填充带流动条纹,到 100% 自动变 success 色并停止条纹 +5. 不需要百分比文字时传 showText: false +6. 定量进度用 value 信号:满格自动切 success 色并停止条纹,role=progressbar + aria-valuenow 同步 现场演示:apps/docs/demo.html#sec-feedback @@ -742,7 +744,7 @@ API 速览: - skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画 - pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达 - spinner(props):加载指示器,size 控制直径 -- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色 +- progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复 - tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签 - modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区 - drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭 diff --git a/llms.txt b/llms.txt index 41af853..5eb43f5 100644 --- a/llms.txt +++ b/llms.txt @@ -68,7 +68,7 @@ - skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画 - pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达 - spinner(props):加载指示器,size 控制直径 - - progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色 + - progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复 - tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签 - modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区 - drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭 diff --git a/packages/skin/ui/src/progress.ts b/packages/skin/ui/src/progress.ts index 3cdad3b..3dfe25c 100644 --- a/packages/skin/ui/src/progress.ts +++ b/packages/skin/ui/src/progress.ts @@ -1,18 +1,37 @@ import { effect, h } from '@ui-arch/frame'; import type { ProgressProps } from './types.js'; -/*** 进度条:轨道 + 渐变流动填充,value 信号驱动 0-100;满格切换 success 色并停止条纹 ***/ -export function progress(props: ProgressProps): HTMLElement { +/*** 进度条:value 信号驱动 0-100 定量态,缺省 value 则退化为不确定态(无限往复);满格切 success 色并停条纹 ***/ +export function progress(props: ProgressProps = {}): HTMLElement { + // —— 状态集中声明 —— + const value = props.value; // 可选:不传即不确定态 const fill = h('div', { class: 'ua-progress-fill' }); const track = h('div', { class: 'ua-progress-track' }, fill); const text = h('span', { class: 'ua-progress-text' }); + const withText = props.showText !== false && !!value; // 不确定态没有百分比可显示 + + if (!value) { + fill.classList.add('ua-indeterminate'); // 宽度与往复动画交给 CSS + return h('div', { class: 'ua-progress', role: 'progressbar', 'aria-label': '进行中' }, track); + } effect(() => { - const v = clamp(props.value()); // 依赖收集:信号变化自动写回 + const v = clamp(value()); // 依赖收集:信号变化自动写回 fill.style.width = `${v}%`; fill.classList.toggle('ua-done', v >= 100); // 满格切语义色 text.textContent = `${Math.round(v)}%`; }); - return h('div', { class: 'ua-progress' }, track, props.showText === false ? null : text); + return h( + 'div', + { + class: 'ua-progress', + role: 'progressbar', + 'aria-valuemin': '0', + 'aria-valuemax': '100', + 'aria-valuenow': () => String(Math.round(clamp(value()))), + }, + track, + withText ? text : null, + ); } /*** 进度钳制到 0-100 ***/ diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index c728bdd..acd2f5c 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -213,8 +213,8 @@ export interface SpinnerProps { /*** 进度条入参 ***/ export interface ProgressProps { - value: Signal; // 当前进度 0-100 的信号 - showText?: boolean; // 是否右侧显示百分比;缺省 true + value?: Signal; // 当前进度 0-100 的信号;不传即不确定态(未知时长的加载) + showText?: boolean; // 是否右侧显示百分比;缺省 true(不确定态忽略) } /*** 标签页入参 ***/ diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index c47162d..2f6ff04 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -34,7 +34,7 @@ "skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画", "pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达", "spinner(props):加载指示器,size 控制直径", - "progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色", + "progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复", "tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签", "modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区", "drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭", @@ -821,8 +821,7 @@ { "n": "value", "t": "Signal", - "r": 1, - "d": "进度 0-100 信号,变化自动平滑过渡" + "d": "当前进度 0-100 信号,变化自动平滑过渡(越界钳制)" }, { "n": "showText", @@ -833,10 +832,12 @@ ], "example": "const pct = signal(0); progress({ value: pct }); setInterval(() => pct((pct() + 5) % 105), 400)", "steps": [ + "时长未知的加载直接 progress() 不传 value:填充块匀速往复,不显示百分比,读屏读作进行中", "创建数值信号 pct = signal(0),范围 0-100(越界自动钳制)", "传入 value,填充宽度与百分比文字随信号平滑过渡", "进行中填充带流动条纹,到 100% 自动变 success 色并停止条纹", - "不需要百分比文字时传 showText: false" + "不需要百分比文字时传 showText: false", + "定量进度用 value 信号:满格自动切 success 色并停止条纹,role=progressbar + aria-valuenow 同步" ] }, { diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index 8bdd687..f56b93b 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -893,6 +893,23 @@ animation: none; opacity: 0; } +/* 不确定态:固定比例填充块匀速往复,宽度与位移都由动画控制 */ +.ua-progress-fill.ua-indeterminate { + width: 35%; + transition: none; + animation: ua-indeterminate 1.2s ease-in-out infinite; +} +.ua-progress-fill.ua-indeterminate::after { + animation: none; +} +@keyframes ua-indeterminate { + from { + transform: translateX(-100%); + } + to { + transform: translateX(285%); + } +} @keyframes ua-stripe { to { background-position: 17px 0;