From 594051975b59dea6e5bc16ae64c91090192b5257 Mon Sep 17 00:00:00 2001 From: HIsheR Date: Mon, 26 Oct 2026 21:40:43 +0800 Subject: [PATCH] =?UTF-8?q?iter(g240):=20=E6=8C=89=E9=92=AE=20loading=20?= =?UTF-8?q?=E6=80=81=EF=BC=8C=E8=BD=AC=E5=9C=88=E7=A6=81=E7=82=B9=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=B8=83=E5=B0=94=E4=B8=8E=E4=BF=A1=E5=8F=B7=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/index.json | 12 +++++++++--- apps/demo/main.ts | 2 ++ llms-full.txt | 7 ++++--- llms.txt | 2 +- packages/skin/ui/src/button.ts | 19 +++++++++++++++---- packages/skin/ui/src/types.ts | 1 + packages/skin/ui/ua.manifest.json | 10 ++++++++-- packages/skin/ui/ui.css | 21 +++++++++++++++++++++ 8 files changed, 61 insertions(+), 13 deletions(-) diff --git a/.ua/index.json b/.ua/index.json index 301b293..9fed55f 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T13:32:36.756Z", + "generatedAt": "2026-10-26T13:40:30.157Z", "repoRoot": ".", "packages": [ { @@ -142,7 +142,7 @@ "api": { "entry": "src/index.ts", "exports": [ - "button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)", + "button(props):按钮,variant 三档视觉强度,disabled / loading 支持布尔或信号动态同步(loading 转圈并禁点),onClick 点击回调", "textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号,onInput 输入回调;prefix/suffix 可选渲染为输入框内侧附加块,clearable 开启后有值即显一键清空按钮,maxCount 设原生上限并实时显示字数", "inputNumber(props):数字输入,−/+ 按钮与键盘 ↑↓ 调节(长按连发步进),min/max/step 夹取,precision 小数位归一;prefix/suffix 可选渲染为整组前后附加块", "textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号,rows 控行数;prefix/suffix 可选渲染为上下附加块,maxCount 显示实时字数并设原生上限", @@ -355,13 +355,19 @@ "n": "size", "t": "'sm' | 'md' | 'lg'", "d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md" + }, + { + "n": "loading", + "t": "boolean | Signal", + "df": "false", + "d": "加载态:转圈并禁点(aria-busy 播报忙碌);布尔直接置位,传信号则动态同步" } ], "example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })", "steps": [ "确定语义强度:主操作用 primary(默认),次要操作用 ghost,危险操作用 danger", "绑定 onClick 回调处理点击逻辑", - "需要禁用态时自行在回调首行卫语句判断,或外层包条件渲染", + "需要禁用态时传 disabled(布尔或信号);异步提交用 loading:开始时置 true,完成后置 false,按钮转圈并自动禁点", "放进
里需要提交时显式传 type: \"submit\";缺省 button 不会触发原生提交" ] }, diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 90c4aee..6e51491 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -159,6 +159,7 @@ const calDay = signal(dayKey(0)); // —— 响应式状态演示 —— const count = signal(0); +const saving = signal(false); // 按钮 loading 态演示(模拟异步保存) // —— 双向绑定演示 —— const name = signal(''); @@ -326,6 +327,7 @@ const content = h( 'signal 驱动:按钮修改信号,文本自动更新,无虚拟 DOM。', button({ label: 'count +1', onClick: () => count(count() + 1) }), button({ label: '重置', variant: 'ghost', onClick: () => count(0) }), + button({ label: '保存(模拟请求)', loading: saving, onClick: () => { saving(true); setTimeout(() => saving(false), 1500); } }), h('span', { class: 'out' }, () => `count = ${count()}`), ), section( diff --git a/llms-full.txt b/llms-full.txt index 8e5c0b9..3a70046 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T13:32:36.756Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T13:40:30.157Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -90,6 +90,7 @@ image({ src: '/a.png', alt: '架构图', width: 160 }) - disabled:boolean | Signal —— 禁用态:布尔直接置位,传信号则动态同步 - onClick(回调):() => void —— 点击回调 - size:'sm' | 'md' | 'lg' —— 高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md +- loading(默认 false):boolean | Signal —— 加载态:转圈并禁点(aria-busy 播报忙碌);布尔直接置位,传信号则动态同步 示例: @@ -101,7 +102,7 @@ button({ label: '提交', variant: 'primary', onClick: () => console.log('clicke 1. 确定语义强度:主操作用 primary(默认),次要操作用 ghost,危险操作用 danger 2. 绑定 onClick 回调处理点击逻辑 -3. 需要禁用态时自行在回调首行卫语句判断,或外层包条件渲染 +3. 需要禁用态时传 disabled(布尔或信号);异步提交用 loading:开始时置 true,完成后置 false,按钮转圈并自动禁点 4. 放进 里需要提交时显式传 type: "submit";缺省 button 不会触发原生提交 现场演示:apps/docs/demo.html#sec-counter @@ -2140,7 +2141,7 @@ highlight({ text: 'Alpha Beta', keyword: 'beta' }) API 速览: -- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交) +- button(props):按钮,variant 三档视觉强度,disabled / loading 支持布尔或信号动态同步(loading 转圈并禁点),onClick 点击回调 - textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号,onInput 输入回调;prefix/suffix 可选渲染为输入框内侧附加块,clearable 开启后有值即显一键清空按钮,maxCount 设原生上限并实时显示字数 - inputNumber(props):数字输入,−/+ 按钮与键盘 ↑↓ 调节(长按连发步进),min/max/step 夹取,precision 小数位归一;prefix/suffix 可选渲染为整组前后附加块 - textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号,rows 控行数;prefix/suffix 可选渲染为上下附加块,maxCount 显示实时字数并设原生上限 diff --git a/llms.txt b/llms.txt index c17112a..97166e3 100644 --- a/llms.txt +++ b/llms.txt @@ -47,7 +47,7 @@ - 类型: skin-dom · 目录: packages/skin/ui - 描述: DOM 皮肤组件库:表单 / 反馈 / 浮层 / 导航 / 数据展示,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌(组件全量见 api.exports 或刚体清单) - API: - - button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交) + - button(props):按钮,variant 三档视觉强度,disabled / loading 支持布尔或信号动态同步(loading 转圈并禁点),onClick 点击回调 - textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号,onInput 输入回调;prefix/suffix 可选渲染为输入框内侧附加块,clearable 开启后有值即显一键清空按钮,maxCount 设原生上限并实时显示字数 - inputNumber(props):数字输入,−/+ 按钮与键盘 ↑↓ 调节(长按连发步进),min/max/step 夹取,precision 小数位归一;prefix/suffix 可选渲染为整组前后附加块 - textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号,rows 控行数;prefix/suffix 可选渲染为上下附加块,maxCount 显示实时字数并设原生上限 diff --git a/packages/skin/ui/src/button.ts b/packages/skin/ui/src/button.ts index 8f60320..0c0e490 100644 --- a/packages/skin/ui/src/button.ts +++ b/packages/skin/ui/src/button.ts @@ -1,11 +1,22 @@ -import { h } from '@ui-arch/frame'; +import { effect, h, signal } from '@ui-arch/frame'; import type { ButtonProps } from './types.js'; -/*** 按钮:variant 控制视觉强度,disabled 支持布尔或信号禁用,onClick 为点击回调 ***/ +/*** 按钮:variant 控制视觉强度,disabled / loading 支持布尔或信号动态同步(loading 转圈并禁点),onClick 为点击回调 ***/ export function button(props: ButtonProps): HTMLElement { - return h( + const loading = typeof props.loading === 'function' ? props.loading : signal(!!props.loading); // 归一成信号:布尔也走同一响应式路径 + const spin = h('span', { class: 'ua-spinner ua-btn-spin', 'aria-hidden': 'true' }); // 转圈指示:CSS 默认 display:none,加载态才显形 + const el = h( 'button', - { class: `ua-btn ua-btn-${props.variant ?? 'primary'}${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, type: props.type ?? 'button', disabled: props.disabled, onClick: props.onClick }, // 缺省 button:表单内不隐式提交 + { + class: `ua-btn ua-btn-${props.variant ?? 'primary'}${props.size && props.size !== 'md' ? ` is-${props.size}` : ''}`, + type: props.type ?? 'button', + disabled: () => (typeof props.disabled === 'function' ? props.disabled() : !!props.disabled) || loading(), // 禁点口径:显式禁用或加载中 + 'aria-busy': () => (loading() ? 'true' : null), // 读屏播报忙碌态(null 移除属性) + onClick: props.onClick, + }, // 缺省 button:表单内不隐式提交 + spin, props.label, ); + effect(() => el.classList.toggle('is-loading', loading())); // 加载态只切类名,不整棵重渲染(保住焦点与子节点) + return el; } diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index f04d665..fdfaf3f 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -26,6 +26,7 @@ export interface ButtonProps { variant?: 'primary' | 'ghost' | 'danger'; // 视觉强度;缺省 primary type?: 'button' | 'submit' | 'reset'; // 原生类型;缺省 button(表单内不会意外触发提交) disabled?: boolean | Signal; // 禁用态:布尔直接置位,传信号则动态同步 + loading?: boolean | Signal; // 加载态:转圈并禁点(aria-busy);布尔直接置位,传信号则动态同步 onClick?: () => void; // 点击回调 } diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index f904eb7..ed2487e 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -13,7 +13,7 @@ "api": { "entry": "src/index.ts", "exports": [ - "button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)", + "button(props):按钮,variant 三档视觉强度,disabled / loading 支持布尔或信号动态同步(loading 转圈并禁点),onClick 点击回调", "textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号,onInput 输入回调;prefix/suffix 可选渲染为输入框内侧附加块,clearable 开启后有值即显一键清空按钮,maxCount 设原生上限并实时显示字数", "inputNumber(props):数字输入,−/+ 按钮与键盘 ↑↓ 调节(长按连发步进),min/max/step 夹取,precision 小数位归一;prefix/suffix 可选渲染为整组前后附加块", "textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号,rows 控行数;prefix/suffix 可选渲染为上下附加块,maxCount 显示实时字数并设原生上限", @@ -226,13 +226,19 @@ "n": "size", "t": "'sm' | 'md' | 'lg'", "d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md" + }, + { + "n": "loading", + "t": "boolean | Signal", + "df": "false", + "d": "加载态:转圈并禁点(aria-busy 播报忙碌);布尔直接置位,传信号则动态同步" } ], "example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })", "steps": [ "确定语义强度:主操作用 primary(默认),次要操作用 ghost,危险操作用 danger", "绑定 onClick 回调处理点击逻辑", - "需要禁用态时自行在回调首行卫语句判断,或外层包条件渲染", + "需要禁用态时传 disabled(布尔或信号);异步提交用 loading:开始时置 true,完成后置 false,按钮转圈并自动禁点", "放进 里需要提交时显式传 type: \"submit\";缺省 button 不会触发原生提交" ] }, diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index 8bbda77..32583fb 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -5238,3 +5238,24 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); } color: inherit; font-weight: var(--ua-weight-medium); } + +/* ---- 按钮 loading 态:转圈 + 禁点 ---- */ +.ua-btn-spin { + display: none; /* 非加载态不占位:文案位置不漂移 */ + border-width: 2px; +} +.ua-btn.is-loading { + cursor: progress; /* 光标改忙碌:点了也不会有下一步 */ +} +.ua-btn.is-loading .ua-btn-spin { + display: inline-block; + width: 1em; + height: 1em; + margin-right: var(--ua-space-05); + vertical-align: -0.125em; /* 随文字基线微沉:转圈与文案居中对齐 */ +} +@media (prefers-reduced-motion: reduce) { + .ua-btn.is-loading .ua-btn-spin { + animation: none; /* 降动效:转圈静态化为实色环 */ + } +}