Files
UI/.ua/backlog.json
T
2026-10-23 17:01:46 +08:00

214 lines
23 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"$schema": "迭代点子库,status: open=待做 / done=已完成;按数组顺序取第一条 open 执行",
"items": [
{
"id": "b01",
"title": "新增 tooltip 组件",
"motivation": "皮肤包 16 组件缺少悬浮提示,表单与工具栏场景常用;可复用自绘弹层与微动画既有模式",
"acceptance": "packages/skin/ui 新增 tooltip.ts:content/message 简单 API,hover/focus 触发,延迟约 150ms 出场,带出入场微动画与 prefers-reduced-motion 兜底,只消费 --ua-* 令牌;导出并注册进 ua.manifest.json,文档站 props 表与 llms-full.txt 同步更新;npm run check 全绿",
"status": "done",
"notes": "API 定为 tooltip(props, trigger) 包裹式;delay 默认 150ms,Esc 立即收起,气泡 pointer-events:none 防抖,aria-describedby + role=tooltip 保证读屏可读;demo 新增 sec-tooltip 区块。顺带修复 ua style 对 https:// 的误判(先剥离字面量再定位 //)"
},
{
"id": "b02",
"title": "新增 toast 消息提示组件",
"motivation": "alert 是常驻块级元素,缺少轻量转瞬即逝的全局反馈;操作成功/失败场景刚需",
"acceptance": "新增 toast.ts:toast.success/error/warning/info 四个工厂函数,入参 {message, duration?},默认 3s 自动消失,右上角堆叠,语义色复用四档 variant,出入场微动画 + prefers-reduced-motion 兜底;导出、manifest、文档、llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "对外暴露 toast.info/success/warning/error(message, duration?) 四工厂 + showToast(props) 完整入参版;全局堆叠容器懒创建,role=status 无障碍;退场用 transitionend 移除并带 320ms 兜底(覆盖 reduced-motion 下 transition 不触发的情况);样式复用 alert 语义色 color-mix 模式,reduced-motion 列表已补 .ua-toast;demo 新增 sec-toast 区块"
},
{
"id": "b03",
"title": "新增 skeleton 骨架屏组件",
"motivation": "异步加载占位是数据表格与卡片的标配,配合 dataTable 空状态形成完整加载体验",
"acceptance": "新增 skeleton.ts:skeleton({variant: 'text'|'rect'|'circle', rows?}),呼吸/扫光微动画 + prefers-reduced-motion 静态化,只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt 同步;demo 页加一节演示;npm run check 全绿",
"status": "done",
"notes": "skeleton({variant, rows}):text(多行,末行缩短)/ rect / circle 三形状,扫光动画(::after 位移 + 渐变),reduced-motion 下静态灰块(动画列表已补 .ua-skeleton 与 ::after);demo 新增 sec-skeleton 卡片组合演示"
},
{
"id": "b04",
"title": "新增独立 pagination 分页器组件并与 dataTable 演示联动",
"motivation": "table-core 已有 pagination 插件,但皮肤包没有独立分页器;dataTable 演示也未展示分页联动",
"acceptance": "新增 pagination.ts:受控 API {page, pageSize, total, onChange},页码过多时折叠省略号,键盘可达,禁用态完整;demo 页 dataTable 一节增加分页联动演示;导出、manifest、文档、llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "pagination(props):page 信号双向绑定(受控),≤7 页全展开、更多时首尾 + 当前页 ±1 折叠省略号(renderList 重建),原生 button 键盘可达,aria-current / aria-label 完整,首末页禁用 ‹ ›;dataTable 新增可选 page 信号桥接(双向同步 + 越界钳制,防无效派发);demo sec-table 新增 pageSize 3 × 24 行的联动卡片"
},
{
"id": "b05",
"title": "文档站演示区由 manifest 驱动自动生成",
"motivation": "目前 apps/docs/index.html 手写 props 表与演示,每加组件都要人肉同步;由 manifest 单一来源驱动可消除重复维护",
"acceptance": "ua CLI 新增 docs-gen 逻辑:读取各组件 ua.manifest.json 中的 components 参数表,自动生成 props 表 HTML 片段注入文档站模板;生成结果与现有手写版信息量对齐(props 名/类型/默认值/说明);npm run check 全绿且文档站可正常打开",
"status": "done",
"notes": "核查确认 props 表/示例/步骤/演示 iframe 早已由 ua docs 从 manifest docs.components 单一来源驱动;真正的缺口是信息量不含默认值——本轮 CLI propsTable 新增「默认值」列(props 条目可选 df 字段,缺省渲染 —),llms-full.txt 参数行同步追加「默认 x」标注;16 处可机读默认值从描述文案提取进 df;check 全绿 + 文档站结构完整性校验通过"
},
{
"id": "c01",
"title": "button / checkbox / switch 补 disabled 禁用态",
"motivation": "三个基础交互组件缺禁用能力,表单只读/权限控制场景刚需",
"acceptance": "ButtonProps / CheckboxProps / SwitchProps 新增可选 disabled(boolean | Signal<boolean>):静态布尔与信号绑定都支持,禁用时事件不触发、置灰 + not-allowed 光标;demo 表单区加静态禁用与信号联动示例;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "零新增辅助代码:复用 frame h() 对 false/undefined 移除属性、Signal 函数走响应式绑定的既有语义,disabled 透传即可;CSS 补通用禁用态(opacity 0.45 + not-allowed,开关轨道同级置灰);demo 用同一 locked 信号绑定 switch 的 checked 与 checkbox 的 disabled 展示联动"
},
{
"id": "c02",
"title": "textField / textarea 补 disabled 禁用态",
"motivation": "输入类组件与 c01 的交互组件对齐,表单只读场景闭环",
"acceptance": "TextFieldProps / TextareaProps 新增可选 disabled(boolean | Signal<boolean>),禁用时不响应输入、置灰 + not-allowed 光标;demo 禁用示例区补一个信号绑定输入框;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "与 c01 同模式:disabled 透传 h() 即得布尔/信号双支持;禁用态 CSS 追加 .ua-input / .ua-textarea;demo 复用 locked 信号联动开关与输入框"
},
{
"id": "d01",
"title": "新增下拉菜单 menu 组件",
"motivation": "工具栏/行操作场景缺省可折叠的动作集合,select 只能选值不能派发动作;可复用自绘弹层与微动画既有模式",
"acceptance": "packages/skin/ui 新增 menu.ts:触发器 + 自绘弹层,items 支持 disabled / danger / divider,点条目 / 点外部 / Esc 关闭,↑↓ 环形导航(禁用项跳过),Enter/Space 选中,Esc 收起后焦点回触发器;只消费 --ua-* 令牌并带 prefers-reduced-motion 兜底;导出 + manifest + 文档 + llms-full.txt + demo 同步;npm run check 全绿",
"status": "done",
"notes": "API menu({ label, items, variant, align, onSelect });MenuItem = { value, label, disabled?, danger?, divider? };弹层 ua-open 同时挂在根与弹层(根管箭头旋转、弹层管出入场),危险条目取 danger 色;键盘 ↑↓ 环形导航 + Esc 回焦触发器;demo 新增 sec-menu 双示例(含禁用/危险/分隔线与右对齐)"
},
{
"id": "d02",
"title": "select / radioGroup 补 disabled,修复 select 箭头旋转失效",
"motivation": "c01/c02 已覆盖 button/checkbox/switch/输入类,选择类仍缺禁用;同时发现 select 的 .ua-select.ua-open 选择器永不命中(ua-open 只挂在弹层),展开时箭头不旋转",
"acceptance": "SelectProps / RadioGroupProps 新增可选 disabled(boolean | Signal<boolean>);禁用时 select 触发器置灰且不展开弹层,radioGroup 整组置灰不可选;select 箭头在展开时旋转 180°;demo 加信号禁用与静态禁用示例;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "disabled 仍走 h() 透传(原生 button/input 拦截交互)零辅助代码;select 根节点补 class 函数加 ua-open,箭头旋转选择器命中;CSS 通用禁用态列表并入 .ua-select-trigger 与 .ua-radio,另加 :has() 让单选行整行弱化、禁用触发器 hover 不改边框色"
},
{
"id": "d03",
"title": "新增折叠面板 accordion 组件",
"motivation": "长表单/设置页需要分区收起,皮肤包无折叠容器;grid 行高过渡可零测量实现展开动画",
"acceptance": "新增 accordion.ts:items(value/title/content/disabled)+ 单开(手风琴)与 multiple 多开,active 信号可选受控,标题为原生 button(aria-expanded/controls + region 关联),内容区 0fr→1fr 过渡且 reduced-motion 静态化;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "contents 走 appendChild 既有语义,字符串/DOM/响应式函数通吃;未传 active 时内部 signal<string[]> 持有展开集合,受控模式只认 active 单开;CSS 用 grid-template-rows 0fr→1fr 过渡免测量,reduced-motion 列表补 ua-acc-*;demo 新增 sec-accordion(多开 + 禁用面板)"
},
{
"id": "d04",
"title": "dataTable 接加载态(骨架行 + 交互禁用)",
"motivation": "异步取数时表格直接闪空状态,缺少 loading 反馈;skeleton 已就位但未与表格打通",
"acceptance": "DataTableProps 新增 loading?: boolean | Signal<boolean>;为真时表体渲染骨架行(行数随 pageSize,列数含选择列)、筛选输入与上下页按钮禁用、容器 aria-busy=true;demo 加开关演示;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "busy() 读取器兼顾布尔与信号(信号在 renderList 内调用可被依赖收集);骨架行数量 Math.min(pageSize,6) 防过长;加载态优先于数据判断,避免闪出旧行;disabled 仍走 props 透传,零新增辅助组件"
},
{
"id": "d05",
"title": "新增面包屑 breadcrumb 组件",
"motivation": "多层级页面的位置指示缺失;顺带补齐导航类组件的语义结构(nav/ol/li + aria-current)",
"acceptance": "新增 breadcrumb.ts:nav + ol 语义结构,末项自动为当前页(不可点、aria-current=page),条目有 href 渲染 <a>、无 href 渲染 button 走 onNavigate,分隔符可配;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "API breadcrumb({ items: {label, href?}[], separator?, onNavigate? });分隔符 aria-hidden;reduced-motion 列表补 .ua-crumb-link;demo 新增 sec-breadcrumb('›' 分隔 + toast 反馈跳转);首轮 style 门禁拦下多行 /*** 注释(检查器只认紧邻单行块),已改单行"
},
{
"id": "d06",
"title": "门禁新增 ua verify:清单 ↔ 文档 ↔ 演示锚点一致性校验",
"motivation": "新增组件靠人工同步 manifest/docs/demo,易漏(本轮已出现 badge/alert/spinner 手把手只有 2 步);把不变量机器化,防回归",
"acceptance": "CLI 新增 verify 子命令:docs.components 的 id 唯一且 api.exports 有 `${id}(` 或以 `${id}.` 开头条目、props 条目必备 n/t/d、steps ≥3、example 必填、demo 锚点必须存在于 apps/demo/main.ts;纳入 npm run check 链条;AGENTS.md 同步;npm run check 全绿",
"status": "done",
"notes": "锚点校验改看演示源码而非打包产物(demo.html 是 esbuild 产物,锚点在运行时生成);顺带补齐 badge/alert/spinner 的第 3 条手把手(说明与 button/skeleton 的分工);check 链变为 build→style→typecheck→smoke→index→verify"
},
{
"id": "d07",
"title": "新增可交互气泡卡片 popover 组件",
"motivation": "tooltip 只做纯展示(pointer-events:none),筛选面板/快捷操作需要可点击的浮层;与 tooltip 形成展示/交互分工",
"acceptance": "新增 popover.ts:popover(props, trigger) 包裹式,默认点击开合,trigger:'hover' 走延迟出场,点外部 / Esc 收起(Esc 回焦触发器),content 支持任意 DOM 且内部点击不误关,onOpenChange 回调;aria-haspopup/aria-expanded 正确;只消费 --ua-* 令牌 + reduced-motion 兜底;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "触发器 click 内 stopPropagation,避免刚开就被 document 的外部点击关掉;aria-expanded 用 effect 写回(setAttribute 不吃函数值);弹层点击已阻止冒泡;demo 新增 sec-popover(含 checkbox+button 的筛选面板与 hover 触发示例)"
},
{
"id": "d08",
"title": "演示页加侧边目录与滚动高亮",
"motivation": "演示页已 14 个区块,长页滚动找区块靠手滑;目录同时给文档站 iframe 外的直接访问提供导航",
"acceptance": "demo 页改为 shell 双栏布局(左 sticky 目录 + 右内容),目录项挂载后从 .sec 自动反推(不手写清单),IntersectionObserver 高亮当前区块,窄屏(≤820px)隐藏目录,reduced-motion 下关平滑滚动;npm run check 全绿、demo 可正常构建",
"status": "done",
"notes": "目录由 buildToc 在 mount 后扫描 section.sec 生成(id + h2 文案),新增区块自动入目录无需维护;rootMargin -10%/-80% 让高亮更跟手;.page 去掉 max-width 由 .shell 统一控宽 1160"
},
{
"id": "d09",
"title": "新增滑块 slider 组件",
"motivation": "设置类场景(音量/阈值/预算)需要范围输入,皮肤包只有文本类输入;原生 range 自带键盘与触屏,接入成本低",
"acceptance": "新增 slider.ts:value 信号双向绑定(拖动写回 + 外部赋值同步),min/max/step 可配,disabled 支持布尔或信号,showValue 可关,onChange 回调;样式只消费 --ua-* 令牌(accent-color)且带聚焦光环与禁用态;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "组件不持有数值,一律委托调用方信号;effect 内等值不重写 input.value,避免拖动过程被打断;轨道配色用 accent-color 令牌化,无需自绘 thumb;demo 新增 sec-slider(音量 step 5 + 与 locked 信号联动的禁用滑块)"
},
{
"id": "d10",
"title": "toast 补手动关闭与同屏堆叠上限",
"motivation": "自动消失前用户无法提前关掉(如已看清错误),且连续触发会无限堆叠淹没页面",
"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 按钮"
},
{
"id": "d12",
"title": "新增可关闭标签 tag 组件",
"motivation": "badge 纯展示无法移除,筛选条件/已选关键字需要带 × 的标签;补齐 badge 的交互版",
"acceptance": "新增 tag.ts:label + variant 五档 + closable/onClose;closable 时渲染带 aria-label 的 × 按钮;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "组件无状态:onClose 只通知,节点由调用方按信号重渲染移除(与 form/formItem 返回句柄、dataTable 委托内核的哲学一致);demo 用 signal<string[]> + renderList 演示真实删除链路,另有四档语义色静态展示"
},
{
"id": "d13",
"title": "dataTable 列定义支持对齐与单元格格式化",
"motivation": "皮肤层只能 String() 原始值,数字列无法右对齐、年龄/金额无法加单位;内核不关心展示,字段应留在皮肤层",
"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",
"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 组合)"
},
{
"id": "d15",
"title": "CLI 包补 ua.manifest.json 并修正文档里的过期组件数",
"motivation": "工具链自身不在注册表里,Agent 无法通过 MCP/llms 发现 ua 的 6 个子命令;AGENTS.md 与 ARCHITECTURE.md 仍写 17 个组件(实际 27)",
"acceptance": "packages/tools/cli/ua.manifest.json(type: tooling)描述 index/list/info/docs/style/verify/mcp 七个子命令与约定,索引包数变 5;AGENTS.md 与 ARCHITECTURE.md 的组件数改成 27;llms.txt / llms-full.txt 重新生成且 CLI 段落可读;npm run check 全绿",
"status": "done",
"notes": "tooling 类型在 ORDER 表外,排序自然落到末尾(??9 兜底),无需改 CLI 排序逻辑;CLI 清单 docs.components 留空,故不参与 verify 的组件级校验;顺带确认 docs 生成对无组件包不报错"
},
{
"id": "d16",
"title": "dataTable 落地内核已定义的列宽 width",
"motivation": "table-core 的 ColumnDef 声明了 width?: number | 'flex',但皮肤层从未消费,列宽完全靠浏览器猜;契约与实现脱节",
"acceptance": "dataTable 渲染 colgroup:数字 width 落成固定像素 <col>,'flex' 或不设交给自适应,选择列固定 44px;demo 表格演示 name 160 / age 100 / city flex;manifest 列定义说明与手把手同步;npm run check 全绿",
"status": "done",
"notes": "用 colgroup 而非逐 th 设宽,保证表头与表体一致且不入侵单元格渲染;未开 table-layout:fixed(nowrap 内容会被裁),宽高仍由内容兜底;pinned 固定列仍未消费(多列 sticky 偏移需测量),留作后续条目"
},
{
"id": "d17",
"title": "表单错误态覆盖包裹式控件并修正校验时机事件",
"motivation": "① 现有 blur 不冒泡,checkbox/select/radioGroup 这类包裹式控件失焦永远不校验;② ua-invalid 只加在控件根,自绘 select 的边框在内部触发器上,视觉上无错误提示;③ 缺 aria-invalid,读屏感知不到",
"acceptance": "formItem 用 focusout 取代 blur、补 change(select/checkbox 变更即校验);错误态同时落到控件根与内部原生控件,并同步 aria-invalid;CSS 覆盖 .ua-invalid 后代 .ua-input/.ua-textarea/.ua-select-trigger/.ua-radio/.ua-slider-input;demo 表单加必填 select 演示;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "marked() 统一收集控件根 + 内部 input/textarea/select/.ua-select-trigger 两处落点;.ua-invalid 后代选择器让包裹式控件描红,无需组件各自改签名(低侵入);demo formlab 新增城市必填 select,提交结果显示城市"
},
{
"id": "d18",
"title": "把「皮肤包禁止硬编码颜色」做成 ua verify 门禁",
"motivation": "AGENTS.md 写着皮肤禁硬编码颜色,但一直靠自觉;ui.css 实际残留 5 处 #fff(按钮/徽标/开关滑块),暗色下对比也不理想",
"acceptance": "checkSkinCss():skin-dom 类型包里所有 .css 出现 #hex 字面量即报错并指出文件行号;5 处 #fff 改为 var(--ua-color-bg)(信号色底上的反色,暗色自动翻转);负向测试(临时加 .ua-tmp{color:#fff})能拦下,移除后回归全绿;AGENTS.md 门禁说明同步;npm run check 全绿",
"status": "done",
"notes": "rgba(...) 未禁:仅用于阴影与遮罩透明度叠加,不承载品牌色,禁掉会逼出更难读的写法(已在函数注释写明边界);实现上先随 python 写文件把正则转义搞坏两次,改用编辑工具直写并做负向验证确认门禁真实生效"
},
{
"id": "d19",
"title": "dataTable 选中态受控(selected 信号)",
"motivation": "选中集只在内核里,外部拿不到也无法预设,批量操作/清空选择只能靠内部工具栏;与 page 受控桥接形成对称能力",
"acceptance": "DataTableProps 新增 selected?: Signal<RowKey[]>,与内核选择双向同步(内容比较避免来回派发,外部数组拷贝保护内核);demo 首表传 picked 信号,展示已选计数与「清空选择」按钮;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "复用 page 桥接的同一套思路:两个 effect 各管一向,sameKeys 内容比较收敛;内部写回拷贝 [...internal],防外部改动内核数组;demo 需 import type { RowKey } 才能对齐 Signal 的逆变"
}
]
}