diff --git a/.ua/backlog.json b/.ua/backlog.json index 7172422..d11d8c7 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -88,6 +88,14 @@ "acceptance": "DataTableProps 新增 loading?: boolean | Signal;为真时表体渲染骨架行(行数随 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 渲染 、无 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 门禁拦下多行 /*** 注释(检查器只认紧邻单行块),已改单行" } ] } diff --git a/.ua/index.json b/.ua/index.json index d59473b..169713c 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-23T08:48:29.721Z", + "generatedAt": "2026-10-23T08:49:44.245Z", "repoRoot": ".", "packages": [ { @@ -130,7 +130,7 @@ { "name": "@ui-arch/ui", "type": "skin-dom", - "description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / menu / radioGroup / accordion / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", + "description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", "tags": [ "components", "dom", @@ -151,6 +151,7 @@ "menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭", "radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用", "accordion(props):折叠面板,标题按钮 + grid 行高过渡,单开(手风琴)或多开,支持禁用面板与 active 信号受控", + "breadcrumb(props):面包屑,nav + ol 语义结构,末项为当前页(aria-current),有 href 走原生链接、无 href 交给 onNavigate", "card(props, ...children):卡片容器", "badge(props):徽标,variant = default | success | warning | danger", "alert(props):提示条,variant = info | success | warning | danger", @@ -178,6 +179,8 @@ "MenuProps", "AccordionItem", "AccordionProps", + "BreadcrumbItem", + "BreadcrumbProps", "OptionItem", "CardProps", "BadgeProps", @@ -204,7 +207,7 @@ "语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色" ], "docs": { - "intro": "22 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。", + "intro": "23 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。", "steps": [ "安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)", "引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'", @@ -551,6 +554,38 @@ "内容区用 grid 行高 0fr → 1fr 过渡,无需测量高度;reduced-motion 用户自动静态切换" ] }, + { + "id": "breadcrumb", + "title": "面包屑 Breadcrumb", + "demo": "sec-breadcrumb", + "props": [ + { + "n": "items", + "t": "BreadcrumbItem[]", + "r": 1, + "d": "路径条目:{ label, href? },末项自动视为当前页" + }, + { + "n": "separator", + "t": "string", + "d": "分隔符;缺省 '/'", + "df": "/" + }, + { + "n": "onNavigate", + "t": "(item, index) => void", + "k": 1, + "d": "无 href 条目的点击回调" + } + ], + "example": "breadcrumb({ items: [{ label: '首页', href: '/' }, { label: '设置' }, { label: '账户' }], onNavigate: (it) => console.log(it.label) })", + "steps": [ + "按层级顺序准备 items,最后一项即当前页,自动渲染为不可点的灰色文字并带 aria-current='page'", + "需要真实跳转就给条目加 href(渲染 );单页应用想自行接管时不给 href,改用 onNavigate 回调", + "改分隔符传 separator,如 '›';分隔符对读屏隐藏", + "语义结构为 nav + ol + li,可直接放进页头或卡片标题栏" + ] + }, { "id": "card", "title": "卡片 Card", diff --git a/.ua/iterations.log b/.ua/iterations.log index 2b78f10..0ab4210 100644 --- a/.ua/iterations.log +++ b/.ua/iterations.log @@ -9,3 +9,4 @@ {"time": "2026-10-23T17:02:00+08:00", "id": "d02", "result": "done", "summary": "select/radioGroup 补 disabled(布尔|信号),并修复 select 根节点缺 ua-open 导致箭头不旋转的既有缺陷;CSS :has() 行级弱化;demo/manifest/llms 同步,check 全绿"} {"time": "2026-10-23T16:47:56+08:00", "id": "d03", "result": "done", "summary": "新增折叠面板 accordion:单开/多开 + active 受控 + 禁用面板,grid 行高 0fr→1fr 过渡免测量,aria 关联完整;manifest/llms/demo 同步,check 全绿"} {"time": "2026-10-23T16:48:36+08:00", "id": "d04", "result": "done", "summary": "dataTable 新增 loading(布尔|信号):表体换骨架行、筛选与翻页禁用、aria-busy;demo 加开关,manifest/llms 同步,check 全绿"} +{"time": "2026-10-23T16:49:45+08:00", "id": "d05", "result": "done", "summary": "新增面包屑 breadcrumb:nav/ol 语义结构 + 末项 aria-current,href 走 、无 href 走 onNavigate,分隔符可配;manifest/llms/demo 同步,check 全绿(中途被 style 门禁拦下多行注释,已改单行)"} diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 8dface7..3aff419 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -3,6 +3,7 @@ import { accordion, alert, badge, + breadcrumb, button, card, checkbox, @@ -237,6 +238,16 @@ const app = h( h('span', { class: 'out' }, () => (menuPick() ? `已选:${menuPick()}` : '未选择')), ), ), + section( + 'sec-breadcrumb', + '面包屑', + 'breadcrumb:nav + ol 语义结构,末项自动标为当前页,无 href 的条目走 onNavigate。', + breadcrumb({ + items: [{ label: '首页', href: '#sec-counter' }, { label: '组件库' }, { label: '导航类' }, { label: '面包屑' }], + separator: '›', + onNavigate: (item) => toast.info(`跳转到 ${item.label}`), + }), + ), section( 'sec-accordion', '折叠面板', diff --git a/llms-full.txt b/llms-full.txt index cc34e06..f4fab03 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-23T08:48:29.721Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-23T08:49:44.245Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -40,7 +40,7 @@ ## @ui-arch/ui(skin-dom)v0.1.0 -22 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。 +23 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。 ### button — 按钮 Button @@ -258,6 +258,29 @@ accordion({ items: [{ value: 'a', title: '账户设置', content: '内容…' }, 现场演示:apps/docs/demo.html#sec-accordion +### breadcrumb — 面包屑 Breadcrumb + +参数: + +- items(必填):BreadcrumbItem[] —— 路径条目:{ label, href? },末项自动视为当前页 +- separator(默认 /):string —— 分隔符;缺省 '/' +- onNavigate(回调):(item, index) => void —— 无 href 条目的点击回调 + +示例: + +``` +breadcrumb({ items: [{ label: '首页', href: '/' }, { label: '设置' }, { label: '账户' }], onNavigate: (it) => console.log(it.label) }) +``` + +手把手: + +1. 按层级顺序准备 items,最后一项即当前页,自动渲染为不可点的灰色文字并带 aria-current='page' +2. 需要真实跳转就给条目加 href(渲染 );单页应用想自行接管时不给 href,改用 onNavigate 回调 +3. 改分隔符传 separator,如 '›';分隔符对读屏隐藏 +4. 语义结构为 nav + ol + li,可直接放进页头或卡片标题栏 + +现场演示:apps/docs/demo.html#sec-breadcrumb + ### card — 卡片 Card 参数: @@ -576,6 +599,7 @@ API 速览: - menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭 - radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用 - accordion(props):折叠面板,标题按钮 + grid 行高过渡,单开(手风琴)或多开,支持禁用面板与 active 信号受控 +- breadcrumb(props):面包屑,nav + ol 语义结构,末项为当前页(aria-current),有 href 走原生链接、无 href 交给 onNavigate - card(props, ...children):卡片容器 - badge(props):徽标,variant = default | success | warning | danger - alert(props):提示条,variant = info | success | warning | danger diff --git a/llms.txt b/llms.txt index 1f2bac2..0f15986 100644 --- a/llms.txt +++ b/llms.txt @@ -45,7 +45,7 @@ ## @ui-arch/ui (v0.1.0) - 类型: skin-dom · 目录: packages/skin/ui -- 描述: DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / menu / radioGroup / accordion / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。 +- 描述: DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。 - API: - button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用 - textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号禁用 @@ -56,6 +56,7 @@ - menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭 - radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用 - accordion(props):折叠面板,标题按钮 + grid 行高过渡,单开(手风琴)或多开,支持禁用面板与 active 信号受控 + - breadcrumb(props):面包屑,nav + ol 语义结构,末项为当前页(aria-current),有 href 走原生链接、无 href 交给 onNavigate - card(props, ...children):卡片容器 - badge(props):徽标,variant = default | success | warning | danger - alert(props):提示条,variant = info | success | warning | danger diff --git a/packages/skin/ui/src/breadcrumb.ts b/packages/skin/ui/src/breadcrumb.ts new file mode 100644 index 0000000..2fb141e --- /dev/null +++ b/packages/skin/ui/src/breadcrumb.ts @@ -0,0 +1,20 @@ +import { h } from '@ui-arch/frame'; +import type { BreadcrumbProps } from './types.js'; + +/*** 面包屑:nav + ol 语义结构,末项为当前页(不可点、aria-current);有 href 渲染 ,无 href 渲染 button 交 onNavigate ***/ +export function breadcrumb(props: BreadcrumbProps): HTMLElement { + const items = props.items; + const sep = props.separator ?? '/'; + + const entries = items.map((item, i) => { + const last = i === items.length - 1; // 末项即当前页 + const node = last + ? h('span', { class: 'ua-crumb-current', 'aria-current': 'page' }, item.label) + : item.href + ? h('a', { class: 'ua-crumb-link', href: item.href }, item.label) + : h('button', { type: 'button', class: 'ua-crumb-link', onClick: () => props.onNavigate?.(item, i) }, item.label); + return h('li', { class: 'ua-crumb-item' }, node, last ? null : h('span', { class: 'ua-crumb-sep', 'aria-hidden': 'true' }, sep)); + }); + + return h('nav', { class: 'ua-breadcrumb', 'aria-label': '面包屑导航' }, h('ol', { class: 'ua-crumb-list' }, ...entries)); +} diff --git a/packages/skin/ui/src/index.ts b/packages/skin/ui/src/index.ts index 03a2425..8b45099 100644 --- a/packages/skin/ui/src/index.ts +++ b/packages/skin/ui/src/index.ts @@ -8,6 +8,7 @@ export { select } from './select.js'; export { menu } from './menu.js'; export { radioGroup } from './radioGroup.js'; export { accordion } from './accordion.js'; +export { breadcrumb } from './breadcrumb.js'; export { card } from './card.js'; export { badge } from './badge.js'; export { alert } from './alert.js'; diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 3b7d040..5a362dc 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -98,6 +98,19 @@ export interface MenuProps { onSelect?: (value: string) => void; // 选中回调(禁用项不触发) } +/*** 面包屑条目 ***/ +export interface BreadcrumbItem { + label: string; // 条目文案 + href?: string; // 跳转地址;缺省时渲染 button 交给 onNavigate +} + +/*** 面包屑入参 ***/ +export interface BreadcrumbProps { + items: BreadcrumbItem[]; // 路径条目,末项视为当前页(不可点) + separator?: string; // 分隔符;缺省 '/' + onNavigate?: (item: BreadcrumbItem, index: number) => void; // 无 href 条目的点击回调 +} + /*** 折叠面板条目 ***/ export interface AccordionItem { value: string; // 面板标识(回调与展开集合用) diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index a8d0a16..5fd5859 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -1,7 +1,7 @@ { "name": "@ui-arch/ui", "type": "skin-dom", - "description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / menu / radioGroup / accordion / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", + "description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / menu / radioGroup / accordion / breadcrumb / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", "tags": [ "components", "dom", @@ -22,6 +22,7 @@ "menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭", "radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用", "accordion(props):折叠面板,标题按钮 + grid 行高过渡,单开(手风琴)或多开,支持禁用面板与 active 信号受控", + "breadcrumb(props):面包屑,nav + ol 语义结构,末项为当前页(aria-current),有 href 走原生链接、无 href 交给 onNavigate", "card(props, ...children):卡片容器", "badge(props):徽标,variant = default | success | warning | danger", "alert(props):提示条,variant = info | success | warning | danger", @@ -49,6 +50,8 @@ "MenuProps", "AccordionItem", "AccordionProps", + "BreadcrumbItem", + "BreadcrumbProps", "OptionItem", "CardProps", "BadgeProps", @@ -75,7 +78,7 @@ "语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色" ], "docs": { - "intro": "22 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。", + "intro": "23 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。", "steps": [ "安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)", "引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'", @@ -422,6 +425,38 @@ "内容区用 grid 行高 0fr → 1fr 过渡,无需测量高度;reduced-motion 用户自动静态切换" ] }, + { + "id": "breadcrumb", + "title": "面包屑 Breadcrumb", + "demo": "sec-breadcrumb", + "props": [ + { + "n": "items", + "t": "BreadcrumbItem[]", + "r": 1, + "d": "路径条目:{ label, href? },末项自动视为当前页" + }, + { + "n": "separator", + "t": "string", + "d": "分隔符;缺省 '/'", + "df": "/" + }, + { + "n": "onNavigate", + "t": "(item, index) => void", + "k": 1, + "d": "无 href 条目的点击回调" + } + ], + "example": "breadcrumb({ items: [{ label: '首页', href: '/' }, { label: '设置' }, { label: '账户' }], onNavigate: (it) => console.log(it.label) })", + "steps": [ + "按层级顺序准备 items,最后一项即当前页,自动渲染为不可点的灰色文字并带 aria-current='page'", + "需要真实跳转就给条目加 href(渲染 );单页应用想自行接管时不给 href,改用 onNavigate 回调", + "改分隔符传 separator,如 '›';分隔符对读屏隐藏", + "语义结构为 nav + ol + li,可直接放进页头或卡片标题栏" + ] + }, { "id": "card", "title": "卡片 Card", diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index e88c1f2..b893faf 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -384,6 +384,51 @@ background: var(--ua-color-border); } +/* —— 面包屑 —— */ +.ua-breadcrumb { + font-size: var(--ua-font-size-md); +} +.ua-crumb-list { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--ua-space-2); + margin: 0; + padding: 0; + list-style: none; +} +.ua-crumb-item { + display: inline-flex; + align-items: center; + gap: var(--ua-space-2); +} +.ua-crumb-link { + padding: 0; + border: none; + background: none; + color: var(--ua-color-accent); + font-size: inherit; + text-decoration: none; + cursor: pointer; + transition: opacity 0.15s; +} +.ua-crumb-link:hover { + opacity: 0.75; + text-decoration: underline; +} +.ua-crumb-link:focus-visible { + outline: none; + border-radius: var(--ua-radius-sm); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--ua-color-accent) 18%, transparent); +} +.ua-crumb-current { + color: var(--ua-color-text-muted); +} +.ua-crumb-sep { + color: var(--ua-color-text-muted); + opacity: 0.6; +} + /* —— 折叠面板 —— */ .ua-accordion { display: flex; @@ -953,6 +998,7 @@ .ua-acc-head, .ua-acc-caret, .ua-acc-body, + .ua-crumb-link, .ua-switch-track, .ua-switch-thumb, .ua-tab,