Compare commits
2 Commits
47bea0b69c
...
4f7d1e86b3
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f7d1e86b3 | |||
| fc7a1e8299 |
@@ -284,6 +284,14 @@
|
|||||||
"acceptance": "packages/skin/ui 新增 inputNumber.ts:−/+ 按钮 + 输入框三段式,value 信号双向绑定,min/max/step 超界自动夹取,键盘 ↑↓ 调节,非法输入回滚,disabled 支持布尔或信号;只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt 同步;demo 加一节演示;npm run check 全绿",
|
"acceptance": "packages/skin/ui 新增 inputNumber.ts:−/+ 按钮 + 输入框三段式,value 信号双向绑定,min/max/step 超界自动夹取,键盘 ↑↓ 调节,非法输入回滚,disabled 支持布尔或信号;只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt 同步;demo 加一节演示;npm run check 全绿",
|
||||||
"status": "done",
|
"status": "done",
|
||||||
"notes": "inputNumber(props):−/+ 复用 ghost button(自带微动画与 disabled 透传),输入框 inputmode=decimal,change 时 parseFloat 解析、非法回滚当前值;↑↓ 步进并 preventDefault;clamp 统一夹取 min/max(缺省 ±Infinity);aria-label 三件套 + role=group;CSS 步进按钮收窄、tabular-nums;demo 新增 sec-inputnumber(与禁用态实例共用 qty 信号);manifest/docs/llms-full/README 同步为 29 组件"
|
"notes": "inputNumber(props):−/+ 复用 ghost button(自带微动画与 disabled 透传),输入框 inputmode=decimal,change 时 parseFloat 解析、非法回滚当前值;↑↓ 步进并 preventDefault;clamp 统一夹取 min/max(缺省 ±Infinity);aria-label 三件套 + role=group;CSS 步进按钮收窄、tabular-nums;demo 新增 sec-inputnumber(与禁用态实例共用 qty 信号);manifest/docs/llms-full/README 同步为 29 组件"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e02",
|
||||||
|
"title": "新增 stepper 步骤条组件",
|
||||||
|
"motivation": "下单、注册等多步流程需要进度指示与回跳,现有导航类组件(tabs/breadcrumb/pagination)都不覆盖分步语义;纯展示 + 可选点击回跳,改动范围可控",
|
||||||
|
"acceptance": "packages/skin/ui 新增 stepper.ts:stepper({steps, active, clickable?, onChange?}),done/current/upcoming 三态视觉,clickable 时已完成步骤可点回跳并写回 active 信号,aria-current=step;只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt、README 清单同步;demo 加一节演示;npm run check 全绿",
|
||||||
|
"status": "done",
|
||||||
|
"notes": "stepper(props):active 信号变化整条重渲染(effect 首跑赋值 + replaceWith,条目少开销可忽略);done 显示 ✓、current 光环聚焦、upcoming 弱化,aria-current=step;clickable 时已完成步骤渲染为 button 回跳写回信号;连接线用 .ua-step + .ua-step::before 横向短线;demo 新增 sec-stepper(上一步/下一步按钮 computed 禁用联动);README 同步 30 组件"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+38
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-23T09:49:54.480Z",
|
"generatedAt": "2026-10-23T09:57:15.241Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -167,6 +167,7 @@
|
|||||||
"spinner(props):加载指示器,size 控制直径",
|
"spinner(props):加载指示器,size 控制直径",
|
||||||
"progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复",
|
"progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复",
|
||||||
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
||||||
|
"stepper(props):步骤条,done/current/upcoming 三态,active 信号双向绑定,clickable 时已完成步骤可回跳",
|
||||||
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
||||||
"drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭",
|
"drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭",
|
||||||
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
||||||
@@ -1107,6 +1108,42 @@
|
|||||||
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "stepper",
|
||||||
|
"title": "步骤条 Stepper",
|
||||||
|
"demo": "sec-stepper",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "steps",
|
||||||
|
"t": "StepperItem[]",
|
||||||
|
"r": 1,
|
||||||
|
"d": "步骤列表,每项 { label, description? }"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "active",
|
||||||
|
"t": "Signal<number>",
|
||||||
|
"r": 1,
|
||||||
|
"d": "当前步索引(0 基)信号,双向绑定:回跳写回、外部赋值同步"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "clickable",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "已完成步骤是否可点击回跳;缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onChange",
|
||||||
|
"t": "(index: number) => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "步骤切换回调(点击回跳触发)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "const step = signal(1); stepper({ steps: [{ label: '下单' }, { label: '支付' }, { label: '完成' }], active: step, clickable: true })",
|
||||||
|
"steps": [
|
||||||
|
"用 signal(0) 创建当前步索引,传入 active;索引之前的步骤显示 ✓ 已完成态",
|
||||||
|
"流程推进时外部赋值即可:step(step() + 1),组件三态自动跟随",
|
||||||
|
"clickable: true 时已完成步骤可点击回跳并写回信号,配合 onChange 同步业务状态"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "pagination",
|
"id": "pagination",
|
||||||
"title": "分页器 Pagination",
|
"title": "分页器 Pagination",
|
||||||
|
|||||||
@@ -35,3 +35,4 @@
|
|||||||
{"ts": "2026-10-23T09:11:42+00:00", "round": 28, "id": "d28", "result": "done", "commit": "", "summary": "drawer 焦点陷阱对齐 modal"}
|
{"ts": "2026-10-23T09:11:42+00:00", "round": 28, "id": "d28", "result": "done", "commit": "", "summary": "drawer 焦点陷阱对齐 modal"}
|
||||||
{"ts": "2026-10-23T09:12:49+00:00", "round": 29, "id": "d29", "result": "done", "commit": "", "summary": "menu Home/End 跳转对齐 select"}
|
{"ts": "2026-10-23T09:12:49+00:00", "round": 29, "id": "d29", "result": "done", "commit": "", "summary": "menu Home/End 跳转对齐 select"}
|
||||||
{"ts":"2026-10-23T17:50:14+08:00","id":"e01","result":"done","commit":"20d7b94","summary":"新增 inputNumber 数字输入:−/+ 按钮 + 键盘上下步进,信号双向绑定,min/max/step 夹取,门禁全绿"}
|
{"ts":"2026-10-23T17:50:14+08:00","id":"e01","result":"done","commit":"20d7b94","summary":"新增 inputNumber 数字输入:−/+ 按钮 + 键盘上下步进,信号双向绑定,min/max/step 夹取,门禁全绿"}
|
||||||
|
{"ts":"2026-10-23T17:57:31+08:00","id":"e02","result":"done","commit":"fc7a1e8","summary":"新增 stepper 步骤条:三态流转 + active 信号双向绑定 + clickable 回跳,门禁全绿"}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Git 提交规范(COMMITTING.md)
|
||||||
|
|
||||||
|
> 适用于所有人工提交与自动化迭代提交。`ua commit-lint` 门禁与 `.git/hooks/commit-msg` 钩子按本文校验,违规提交被拒绝。
|
||||||
|
|
||||||
|
## 格式
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
[body:动机与要点,每行 ≤ 80 字符]
|
||||||
|
|
||||||
|
[footer:Refs: <编号> / BREAKING CHANGE: <说明>]
|
||||||
|
```
|
||||||
|
|
||||||
|
## type 枚举
|
||||||
|
|
||||||
|
| type | 用途 |
|
||||||
|
|---|---|
|
||||||
|
| `feat` | 新功能(组件、命令、插件、门禁) |
|
||||||
|
| `fix` | 缺陷修复 |
|
||||||
|
| `docs` | 纯文档变更 |
|
||||||
|
| `style` | 格式调整(不影响语义:空白、注释格式) |
|
||||||
|
| `refactor` | 重构(既非新增也非修复) |
|
||||||
|
| `test` | 测试相关 |
|
||||||
|
| `chore` | 构建、工具、元数据、依赖 |
|
||||||
|
| `iter` | 迭代流水线产物(自动化专用,scope 必须为 backlog 条目 id) |
|
||||||
|
|
||||||
|
## scope 枚举
|
||||||
|
|
||||||
|
- 包名:`table-core` / `frame` / `ui` / `tokens` / `cli`(跨包可省略)
|
||||||
|
- 应用:`demo` / `docs`
|
||||||
|
- 元数据:`backlog` / `repo` / `protocol`
|
||||||
|
- `iter` 提交的 scope 固定为条目 id:`b02`、`c01`、`d29` …
|
||||||
|
|
||||||
|
## subject 规则
|
||||||
|
|
||||||
|
- 中文祈使句,动词开头(「新增…」「修复…」「补充…」);
|
||||||
|
- ≤ 50 字符;
|
||||||
|
- 结尾不加句号;
|
||||||
|
- 条目 id 不写入 subject(scope 已承载,方便 `git log --grep='iter(b02)'` 检索)。
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
```
|
||||||
|
iter(b02): 新增 toast 消息提示组件
|
||||||
|
feat(ui): dataTable 新增 selected 受控信号
|
||||||
|
fix(cli): 修正 style 检查对 URL 中 // 的误判
|
||||||
|
chore(backlog): 记录 d29 完成
|
||||||
|
docs: 重写 README 组件清单
|
||||||
|
```
|
||||||
|
|
||||||
|
## 反例(会被门禁拒绝)
|
||||||
|
|
||||||
|
```
|
||||||
|
iter: b02 新增 toast 消息提示组件 ← iter 缺 scope(id)
|
||||||
|
iter(b02): 文档站 toast 组件的新增。 ← 句号结尾
|
||||||
|
feat(ui): 增加 dataTable 的受控选中信号并且支持与内核选择双向同步以及已选计数展示 ← 超 50 字符
|
||||||
|
update stuff ← 非 type 枚举、非中文祈使句
|
||||||
|
```
|
||||||
|
|
||||||
|
## 与流水线的关系
|
||||||
|
|
||||||
|
- 自动化每轮迭代的首个提交:`iter(<id>): <subject>`;
|
||||||
|
- 同轮的元数据变更(backlog 置 done):`chore(backlog): 记录 <id> 完成`;
|
||||||
|
- 合并提交(qa/stable 的 ff-only)不产生 message,无需遵守;
|
||||||
|
- 门禁来源:`ua commit-lint <文件|message>`,commit-msg 钩子自动调用;git 自动的 Merge/Revert/fixup 信息跳过校验。
|
||||||
@@ -11,13 +11,13 @@ npm run demo # 打开演示:apps/demo/dist/demo.html
|
|||||||
npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全量文档 llms-full.txt
|
npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全量文档 llms-full.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
## 组件(共 29 个)
|
## 组件(共 30 个)
|
||||||
|
|
||||||
<!-- ua:components -->
|
<!-- ua:components -->
|
||||||
accordion · alert · avatar · badge · breadcrumb · button · card · checkbox · dataTable · drawer · form · inputNumber · menu · modal · pagination · popover · progress · radioGroup · select · skeleton · slider · spinner · switchButton · tabs · tag · textField · textarea · toast · tooltip
|
accordion · alert · avatar · badge · breadcrumb · button · card · checkbox · dataTable · drawer · form · inputNumber · menu · modal · pagination · popover · progress · radioGroup · select · skeleton · slider · spinner · stepper · switchButton · tabs · tag · textField · textarea · toast · tooltip
|
||||||
<!-- /ua:components -->
|
<!-- /ua:components -->
|
||||||
|
|
||||||
分组:表单(button / textField / inputNumber / textarea / checkbox / switchButton / select / radioGroup / slider / form / formItem)、反馈(alert / toast / spinner / progress / skeleton / badge)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion)、数据(dataTable / tag / avatar / card)。
|
分组:表单(button / textField / inputNumber / textarea / checkbox / switchButton / select / radioGroup / slider / form / formItem)、反馈(alert / toast / spinner / progress / skeleton / badge)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper)、数据(dataTable / tag / avatar / card)。
|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ accordion · alert · avatar · badge · breadcrumb · button · card · checkbo
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) |
|
| `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) |
|
||||||
| `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) |
|
| `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) |
|
||||||
| `packages/skin/ui` | @ui-arch/ui:29 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
| `packages/skin/ui` | @ui-arch/ui:30 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
||||||
| `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) |
|
| `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) |
|
||||||
| `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server |
|
| `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server |
|
||||||
| `apps/demo` | 交互演示(esbuild 打包成单文件) |
|
| `apps/demo` | 交互演示(esbuild 打包成单文件) |
|
||||||
|
|||||||
+22
-1
@@ -1,4 +1,4 @@
|
|||||||
import { h, mount, renderList, signal } from '@ui-arch/frame';
|
import { computed, h, mount, renderList, signal } from '@ui-arch/frame';
|
||||||
import {
|
import {
|
||||||
accordion,
|
accordion,
|
||||||
alert,
|
alert,
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
skeleton,
|
skeleton,
|
||||||
slider,
|
slider,
|
||||||
spinner,
|
spinner,
|
||||||
|
stepper,
|
||||||
switchButton,
|
switchButton,
|
||||||
tabs,
|
tabs,
|
||||||
tag,
|
tag,
|
||||||
@@ -115,6 +116,9 @@ const volume = signal(40);
|
|||||||
// —— 数字输入:购买数量演示,与禁用态实例共用同一信号 ——
|
// —— 数字输入:购买数量演示,与禁用态实例共用同一信号 ——
|
||||||
const qty = signal(1);
|
const qty = signal(1);
|
||||||
|
|
||||||
|
// —— 步骤条:下单流程演示,按钮推进、点已完成步骤回跳 ——
|
||||||
|
const stepIdx = signal(0);
|
||||||
|
|
||||||
// —— 受控选中:dataTable 内部勾选实时写回,外部可清空 ——
|
// —— 受控选中:dataTable 内部勾选实时写回,外部可清空 ——
|
||||||
const picked = signal<RowKey[]>([]);
|
const picked = signal<RowKey[]>([]);
|
||||||
|
|
||||||
@@ -342,6 +346,23 @@ const content = h(
|
|||||||
h('div', { class: 'row' }, badge({ label: 'modal', variant: 'success' }), badge({ label: 'footer', variant: 'default' })),
|
h('div', { class: 'row' }, badge({ label: 'modal', variant: 'success' }), badge({ label: 'footer', variant: 'default' })),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
section(
|
||||||
|
'sec-stepper',
|
||||||
|
'步骤条',
|
||||||
|
'stepper:done/current/upcoming 三态随 active 信号流转,clickable 时点已完成步骤回跳。',
|
||||||
|
stepper({
|
||||||
|
steps: [{ label: '确认订单', description: '收货信息与商品清单' }, { label: '支付', description: '选择支付方式' }, { label: '完成', description: '等待发货' }],
|
||||||
|
active: stepIdx,
|
||||||
|
clickable: true,
|
||||||
|
onChange: (i) => toast.info(`回到第 ${i + 1} 步`),
|
||||||
|
}),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{ class: 'row' },
|
||||||
|
button({ label: '上一步', variant: 'ghost', disabled: computed(() => stepIdx() === 0), onClick: () => stepIdx(Math.max(0, stepIdx() - 1)) }),
|
||||||
|
button({ label: '下一步', disabled: computed(() => stepIdx() === 2), onClick: () => stepIdx(Math.min(2, stepIdx() + 1)) }),
|
||||||
|
),
|
||||||
|
),
|
||||||
section(
|
section(
|
||||||
'sec-menu',
|
'sec-menu',
|
||||||
'下拉菜单',
|
'下拉菜单',
|
||||||
|
|||||||
+25
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-23T09:49:54.480Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-23T09:57:15.241Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -628,6 +628,29 @@ const tab = signal('a'); tabs({ tabs: [{ value: 'a', label: '概览' }, { value:
|
|||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-tabs-modal
|
现场演示:apps/docs/demo.html#sec-tabs-modal
|
||||||
|
|
||||||
|
### stepper — 步骤条 Stepper
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- steps(必填):StepperItem[] —— 步骤列表,每项 { label, description? }
|
||||||
|
- active(必填):Signal<number> —— 当前步索引(0 基)信号,双向绑定:回跳写回、外部赋值同步
|
||||||
|
- clickable:boolean —— 已完成步骤是否可点击回跳;缺省 false
|
||||||
|
- onChange(回调):(index: number) => void —— 步骤切换回调(点击回跳触发)
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
const step = signal(1); stepper({ steps: [{ label: '下单' }, { label: '支付' }, { label: '完成' }], active: step, clickable: true })
|
||||||
|
```
|
||||||
|
|
||||||
|
手把手:
|
||||||
|
|
||||||
|
1. 用 signal(0) 创建当前步索引,传入 active;索引之前的步骤显示 ✓ 已完成态
|
||||||
|
2. 流程推进时外部赋值即可:step(step() + 1),组件三态自动跟随
|
||||||
|
3. clickable: true 时已完成步骤可点击回跳并写回信号,配合 onChange 同步业务状态
|
||||||
|
|
||||||
|
现场演示:apps/docs/demo.html#sec-stepper
|
||||||
|
|
||||||
### pagination — 分页器 Pagination
|
### pagination — 分页器 Pagination
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
@@ -803,6 +826,7 @@ API 速览:
|
|||||||
- spinner(props):加载指示器,size 控制直径
|
- spinner(props):加载指示器,size 控制直径
|
||||||
- progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复
|
- progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复
|
||||||
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
||||||
|
- stepper(props):步骤条,done/current/upcoming 三态,active 信号双向绑定,clickable 时已完成步骤可回跳
|
||||||
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
||||||
- drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭
|
- drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭
|
||||||
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
- spinner(props):加载指示器,size 控制直径
|
- spinner(props):加载指示器,size 控制直径
|
||||||
- progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复
|
- progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复
|
||||||
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
||||||
|
- stepper(props):步骤条,done/current/upcoming 三态,active 信号双向绑定,clickable 时已完成步骤可回跳
|
||||||
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
- modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区
|
||||||
- drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭
|
- drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭
|
||||||
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
- form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export { pagination } from './pagination.js';
|
|||||||
export { spinner } from './spinner.js';
|
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 { stepper } from './stepper.js';
|
||||||
export { modal } from './modal.js';
|
export { modal } from './modal.js';
|
||||||
export { drawer } from './drawer.js';
|
export { drawer } from './drawer.js';
|
||||||
export { form, formItem } from './form.js';
|
export { form, formItem } from './form.js';
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { effect, h } from '@ui-arch/frame';
|
||||||
|
import type { StepperProps } from './types.js';
|
||||||
|
|
||||||
|
/*** 步骤条:done/current/upcoming 三态横向流程指示,clickable 时已完成步骤可点回跳并写回 active 信号 ***/
|
||||||
|
export function stepper(props: StepperProps): HTMLElement {
|
||||||
|
const active = props.active;
|
||||||
|
const clickStep = (i: number): void => { if (!props.clickable || i >= active()) return; // 卫语句:不可点或未完成不回跳
|
||||||
|
active(i); if (props.onChange) props.onChange(i); }; // 写回信号并回调
|
||||||
|
const render = (): HTMLElement => {
|
||||||
|
const cur = active();
|
||||||
|
const items = props.steps.map((s, i) => {
|
||||||
|
const state = i < cur ? 'done' : i === cur ? 'current' : 'upcoming'; // 三态:已完成 / 当前 / 未到
|
||||||
|
const mark = state === 'done' ? '✓' : String(i + 1); // 完成打勾,其余显示序号
|
||||||
|
const clickable = !!props.clickable && i < cur; // 仅已完成步骤可回跳
|
||||||
|
const head = clickable
|
||||||
|
? h('button', { class: 'ua-step-dot', type: 'button', onClick: () => clickStep(i) }, mark)
|
||||||
|
: h('span', { class: 'ua-step-dot' }, mark);
|
||||||
|
const body = h('div', { class: 'ua-step-text' }, h('span', { class: 'ua-step-label' }, s.label),
|
||||||
|
s.description ? h('span', { class: 'ua-step-desc' }, s.description) : null);
|
||||||
|
const li = h('li', { class: `ua-step ua-step-${state}`, 'aria-current': state === 'current' ? 'step' : null }, head, body);
|
||||||
|
return li;
|
||||||
|
});
|
||||||
|
return h('ol', { class: 'ua-stepper' }, ...items);
|
||||||
|
};
|
||||||
|
let el!: HTMLElement; // 当前渲染产物(effect 首跑赋值)
|
||||||
|
effect(() => { const next = render(); if (el) el.replaceWith(next); el = next; }); // active 变化整条重渲染(条目少,开销可忽略)
|
||||||
|
return el;
|
||||||
|
}
|
||||||
@@ -175,6 +175,20 @@ export interface InputNumberProps {
|
|||||||
onChange?: (value: number) => void; // 值变化回调(已夹取到合法区间)
|
onChange?: (value: number) => void; // 值变化回调(已夹取到合法区间)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 步骤条条目 ***/
|
||||||
|
export interface StepperItem {
|
||||||
|
label: string; // 步骤标题
|
||||||
|
description?: string; // 步骤补充说明;缺省不渲染
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 步骤条入参 ***/
|
||||||
|
export interface StepperProps {
|
||||||
|
steps: StepperItem[]; // 步骤列表(按顺序渲染)
|
||||||
|
active: Signal<number>; // 当前步索引(0 基),双向绑定(回跳写回、外部赋值同步)
|
||||||
|
clickable?: boolean; // 已完成步骤是否可点击回跳;缺省 false
|
||||||
|
onChange?: (index: number) => void; // 步骤切换回调(点击回跳触发)
|
||||||
|
}
|
||||||
|
|
||||||
/*** 徽标入参 ***/
|
/*** 徽标入参 ***/
|
||||||
export interface BadgeProps {
|
export interface BadgeProps {
|
||||||
label: string; // 徽标文案
|
label: string; // 徽标文案
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
"spinner(props):加载指示器,size 控制直径",
|
"spinner(props):加载指示器,size 控制直径",
|
||||||
"progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复",
|
"progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复",
|
||||||
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
||||||
|
"stepper(props):步骤条,done/current/upcoming 三态,active 信号双向绑定,clickable 时已完成步骤可回跳",
|
||||||
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
||||||
"drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭",
|
"drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭",
|
||||||
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
||||||
@@ -978,6 +979,42 @@
|
|||||||
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "stepper",
|
||||||
|
"title": "步骤条 Stepper",
|
||||||
|
"demo": "sec-stepper",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "steps",
|
||||||
|
"t": "StepperItem[]",
|
||||||
|
"r": 1,
|
||||||
|
"d": "步骤列表,每项 { label, description? }"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "active",
|
||||||
|
"t": "Signal<number>",
|
||||||
|
"r": 1,
|
||||||
|
"d": "当前步索引(0 基)信号,双向绑定:回跳写回、外部赋值同步"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "clickable",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "已完成步骤是否可点击回跳;缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onChange",
|
||||||
|
"t": "(index: number) => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "步骤切换回调(点击回跳触发)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "const step = signal(1); stepper({ steps: [{ label: '下单' }, { label: '支付' }, { label: '完成' }], active: step, clickable: true })",
|
||||||
|
"steps": [
|
||||||
|
"用 signal(0) 创建当前步索引,传入 active;索引之前的步骤显示 ✓ 已完成态",
|
||||||
|
"流程推进时外部赋值即可:step(step() + 1),组件三态自动跟随",
|
||||||
|
"clickable: true 时已完成步骤可点击回跳并写回信号,配合 onChange 同步业务状态"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "pagination",
|
"id": "pagination",
|
||||||
"title": "分页器 Pagination",
|
"title": "分页器 Pagination",
|
||||||
|
|||||||
+82
-1
@@ -703,6 +703,86 @@
|
|||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* —— 步骤条 —— */
|
||||||
|
.ua-stepper {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.ua-step {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--ua-space-2);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.ua-step + .ua-step {
|
||||||
|
padding-left: var(--ua-space-3);
|
||||||
|
}
|
||||||
|
.ua-step + .ua-step::before {
|
||||||
|
content: ''; /* 连接线:与前一节点之间的横向短线 */
|
||||||
|
width: 20px;
|
||||||
|
height: 1px;
|
||||||
|
margin-top: 13px;
|
||||||
|
margin-right: var(--ua-space-2);
|
||||||
|
background: var(--ua-color-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.ua-step-dot {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border: 1px solid var(--ua-color-border);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--ua-color-surface);
|
||||||
|
color: var(--ua-color-text-muted);
|
||||||
|
font: inherit;
|
||||||
|
font-size: var(--ua-font-size-sm);
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: border-color 0.15s, background 0.15s, color 0.15s, transform 0.12s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
button.ua-step-dot {
|
||||||
|
padding: 0; /* 可回跳步骤:重置原生按钮样式 */
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button.ua-step-dot:hover {
|
||||||
|
border-color: var(--ua-color-accent);
|
||||||
|
color: var(--ua-color-accent);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.ua-step-done .ua-step-dot {
|
||||||
|
border-color: color-mix(in srgb, var(--ua-color-accent) 55%, transparent);
|
||||||
|
background: color-mix(in srgb, var(--ua-color-accent) 10%, var(--ua-color-bg));
|
||||||
|
color: var(--ua-color-accent);
|
||||||
|
}
|
||||||
|
.ua-step-current .ua-step-dot {
|
||||||
|
border-color: var(--ua-color-accent);
|
||||||
|
background: var(--ua-color-bg);
|
||||||
|
color: var(--ua-color-accent);
|
||||||
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ua-color-accent) 15%, transparent);
|
||||||
|
}
|
||||||
|
.ua-step-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.ua-step-label {
|
||||||
|
font-size: var(--ua-font-size-md);
|
||||||
|
line-height: 1.4;
|
||||||
|
color: var(--ua-color-text-muted);
|
||||||
|
}
|
||||||
|
.ua-step-current .ua-step-label {
|
||||||
|
color: var(--ua-color-text);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.ua-step-desc {
|
||||||
|
font-size: var(--ua-font-size-sm);
|
||||||
|
color: var(--ua-color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
/* —— 徽标 —— */
|
/* —— 徽标 —— */
|
||||||
.ua-badge {
|
.ua-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -1378,7 +1458,8 @@
|
|||||||
.ua-modal,
|
.ua-modal,
|
||||||
.ua-modal-overlay,
|
.ua-modal-overlay,
|
||||||
.ua-drawer,
|
.ua-drawer,
|
||||||
.ua-drawer-overlay {
|
.ua-drawer-overlay,
|
||||||
|
.ua-step-dot {
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user