From 28820574e8f8aa1e9577495c0909cf544e798973 Mon Sep 17 00:00:00 2001 From: HIsheR Date: Fri, 23 Oct 2026 15:53:34 +0800 Subject: [PATCH] =?UTF-8?q?iter:=20b03=20=E6=96=B0=E5=A2=9E=20skeleton=20?= =?UTF-8?q?=E9=AA=A8=E6=9E=B6=E5=B1=8F=E7=BB=84=E4=BB=B6=EF=BC=88=E4=B8=89?= =?UTF-8?q?=E5=BD=A2=E7=8A=B6=E5=8D=A0=E4=BD=8D=20+=20=E6=89=AB=E5=85=89?= =?UTF-8?q?=20+=20reduced-motion=20=E5=85=9C=E5=BA=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/backlog.json | 4 +-- .ua/index.json | 32 +++++++++++++++++++--- .ua/iterations.log | 1 + apps/demo/main.ts | 11 ++++++++ llms-full.txt | 27 +++++++++++++++++-- llms.txt | 3 ++- packages/skin/ui/src/index.ts | 1 + packages/skin/ui/src/skeleton.ts | 12 +++++++++ packages/skin/ui/src/types.ts | 6 +++++ packages/skin/ui/ua.manifest.json | 30 +++++++++++++++++++-- packages/skin/ui/ui.css | 45 +++++++++++++++++++++++++++++++ 11 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 packages/skin/ui/src/skeleton.ts diff --git a/.ua/backlog.json b/.ua/backlog.json index 71c841c..dd7f14e 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -22,8 +22,8 @@ "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": "open", - "notes": "" + "status": "done", + "notes": "skeleton({variant, rows}):text(多行,末行缩短)/ rect / circle 三形状,扫光动画(::after 位移 + 渐变),reduced-motion 下静态灰块(动画列表已补 .ua-skeleton 与 ::after);demo 新增 sec-skeleton 卡片组合演示" }, { "id": "b04", diff --git a/.ua/index.json b/.ua/index.json index 95ac11b..0b168e6 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-23T07:49:42.422Z", + "generatedAt": "2026-10-23T07:52:40.603Z", "repoRoot": ".", "packages": [ { @@ -130,7 +130,7 @@ { "name": "@ui-arch/ui", "type": "skin-dom", - "description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", + "description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", "tags": [ "components", "dom", @@ -154,6 +154,7 @@ "alert(props):提示条,variant = info | success | warning | danger", "toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本", "tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起", + "skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画", "spinner(props):加载指示器,size 控制直径", "progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色", "tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签", @@ -176,6 +177,7 @@ "AlertProps", "ToastProps", "TooltipProps", + "SkeletonProps", "SpinnerProps", "ProgressProps", "TabsProps", @@ -194,7 +196,7 @@ "语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色" ], "docs": { - "intro": "18 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。", + "intro": "19 个 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'", @@ -489,6 +491,30 @@ "传入 message 即可,入场自带下滑动画" ] }, + { + "id": "skeleton", + "title": "骨架屏 Skeleton", + "demo": "sec-skeleton", + "props": [ + { + "n": "variant", + "t": "'text' | 'rect' | 'circle'", + "d": "占位形状;缺省 rect(整块矩形)" + }, + { + "n": "rows", + "t": "number", + "d": "text 形状的行数;缺省 3,末行自动缩短模拟段落" + } + ], + "example": "skeleton({ variant: 'text', rows: 3 }); skeleton({ variant: 'circle' }); skeleton({ variant: 'rect' })", + "steps": [ + "数据加载期间用 skeleton 顶替真实内容,加载完成后整体替换", + "文本段落用 variant: 'text' + rows 控制行数,头像用 'circle',卡片/图片区域用 'rect'", + "占位块自带扫光动画,reduced-motion 用户自动静态化为纯灰块", + "组合布局:skeleton 是普通 DOM,可放进 card / row 等任意容器" + ] + }, { "id": "toast", "title": "消息提示 Toast", diff --git a/.ua/iterations.log b/.ua/iterations.log index 48c58bf..3be08c0 100644 --- a/.ua/iterations.log +++ b/.ua/iterations.log @@ -1,2 +1,3 @@ {"time":"2026-10-23T15:47:00+08:00","id":"b01","result":"done","summary":"新增 tooltip 悬浮提示组件(包裹式 API / 150ms 延迟 / Esc 收起 / reduced-motion 兜底),同步 manifest + llms-full.txt + demo 区块,并修复 ua style 对 URL 中 // 的误判;npm run check 全绿"} {"time":"2026-10-23T15:50:03+08:00","id":"b02","result":"done","summary":"新增 toast 消息提示组件:四语义工厂 + showToast,右上角堆叠,默认 3s 自动消失,微动画 + reduced-motion 兜底;manifest/docs/llms 同步,check 全绿"} +{"time":"2026-10-23T15:53:33+08:00","id":"b03","result":"done","summary":"新增 skeleton 骨架屏组件:text/rect/circle 三形状 + 扫光微动画 + reduced-motion 静态化;manifest/docs/llms/demo 同步,check 全绿"} diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 0bd291b..c93d567 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -12,6 +12,7 @@ import { progress, radioGroup, select, + skeleton, spinner, switchButton, tabs, @@ -139,6 +140,16 @@ const app = h( button({ label: '信息(1.5s)', onClick: () => toast.info('已同步云端', 1500) }), ), ), + section( + 'sec-skeleton', + '骨架屏', + 'skeleton 占位:text 多行段落 / rect 块 / circle 圆,扫光微动画,reduced-motion 自动静态化。', + card( + { title: '加载中的用户资料' }, + h('div', { class: 'row' }, skeleton({ variant: 'circle' }), skeleton({ variant: 'text', rows: 2 })), + skeleton({ variant: 'rect' }), + ), + ), section( 'sec-formlab', '表单校验', diff --git a/llms-full.txt b/llms-full.txt index b8c3cf6..9f2c57f 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-23T07:49:42.422Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-23T07:52:40.603Z,数据源:各包 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 -18 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。 +19 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。 ### button — 按钮 Button @@ -260,6 +260,28 @@ alert({ message: '操作可能存在风险', variant: 'warning' }) 现场演示:apps/docs/demo.html#sec-feedback +### skeleton — 骨架屏 Skeleton + +参数: + +- variant:'text' | 'rect' | 'circle' —— 占位形状;缺省 rect(整块矩形) +- rows:number —— text 形状的行数;缺省 3,末行自动缩短模拟段落 + +示例: + +``` +skeleton({ variant: 'text', rows: 3 }); skeleton({ variant: 'circle' }); skeleton({ variant: 'rect' }) +``` + +手把手: + +1. 数据加载期间用 skeleton 顶替真实内容,加载完成后整体替换 +2. 文本段落用 variant: 'text' + rows 控制行数,头像用 'circle',卡片/图片区域用 'rect' +3. 占位块自带扫光动画,reduced-motion 用户自动静态化为纯灰块 +4. 组合布局:skeleton 是普通 DOM,可放进 card / row 等任意容器 + +现场演示:apps/docs/demo.html#sec-skeleton + ### toast — 消息提示 Toast 参数: @@ -469,6 +491,7 @@ API 速览: - alert(props):提示条,variant = info | success | warning | danger - toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本 - tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起 +- skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画 - spinner(props):加载指示器,size 控制直径 - progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色 - tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签 diff --git a/llms.txt b/llms.txt index 24c870b..45f8b49 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 / radioGroup / card / badge / alert / toast / tooltip / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。 +- 描述: DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。 - API: - button(props):按钮,variant = primary | ghost | danger - textField(props):文本输入,value 信号双向绑定 @@ -59,6 +59,7 @@ - alert(props):提示条,variant = info | success | warning | danger - toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本 - tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起 + - skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画 - spinner(props):加载指示器,size 控制直径 - progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色 - tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签 diff --git a/packages/skin/ui/src/index.ts b/packages/skin/ui/src/index.ts index 8a9243a..fe0eddb 100644 --- a/packages/skin/ui/src/index.ts +++ b/packages/skin/ui/src/index.ts @@ -11,6 +11,7 @@ export { badge } from './badge.js'; export { alert } from './alert.js'; export { toast, showToast } from './toast.js'; export { tooltip } from './tooltip.js'; +export { skeleton } from './skeleton.js'; export { spinner } from './spinner.js'; export { progress } from './progress.js'; export { tabs } from './tabs.js'; diff --git a/packages/skin/ui/src/skeleton.ts b/packages/skin/ui/src/skeleton.ts new file mode 100644 index 0000000..4148c57 --- /dev/null +++ b/packages/skin/ui/src/skeleton.ts @@ -0,0 +1,12 @@ +import { h } from '@ui-arch/frame'; +import type { SkeletonProps } from './types.js'; + +/*** 骨架屏:variant 决定占位形状(text 多行 / rect 块 / circle 圆),纯展示无状态 ***/ +export function skeleton(props: SkeletonProps): HTMLElement { + const variant = props.variant ?? 'rect'; // 缺省整块矩形 + if (variant !== 'text') return h('div', { class: `ua-skeleton ua-skeleton-${variant}` }); // 单形状直接返回 + const rows = props.rows ?? 3; // text 行数,末行缩短模拟段落收尾 + const lines: HTMLElement[] = []; + for (let i = 0; i < rows; i++) lines.push(h('div', { class: `ua-skeleton ua-skeleton-text${i === rows - 1 ? ' ua-last' : ''}` })); + return h('div', { class: 'ua-skeleton-group' }, ...lines); +} diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 9e8ad53..9e77800 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -92,6 +92,12 @@ export interface AlertProps { variant?: 'info' | 'success' | 'warning' | 'danger'; // 语义色;缺省 info } +/*** 骨架屏入参 ***/ +export interface SkeletonProps { + variant?: 'text' | 'rect' | 'circle'; // 占位形状;缺省 rect + rows?: number; // text 形状的行数;缺省 3 +} + /*** toast 消息提示入参 ***/ export interface ToastProps { message: string; // 提示文案 diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 7b3a2e1..153b357 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 / radioGroup / card / badge / alert / toast / tooltip / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", + "description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。", "tags": [ "components", "dom", @@ -25,6 +25,7 @@ "alert(props):提示条,variant = info | success | warning | danger", "toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本", "tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起", + "skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画", "spinner(props):加载指示器,size 控制直径", "progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色", "tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签", @@ -47,6 +48,7 @@ "AlertProps", "ToastProps", "TooltipProps", + "SkeletonProps", "SpinnerProps", "ProgressProps", "TabsProps", @@ -65,7 +67,7 @@ "语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色" ], "docs": { - "intro": "18 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。", + "intro": "19 个 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'", @@ -360,6 +362,30 @@ "传入 message 即可,入场自带下滑动画" ] }, + { + "id": "skeleton", + "title": "骨架屏 Skeleton", + "demo": "sec-skeleton", + "props": [ + { + "n": "variant", + "t": "'text' | 'rect' | 'circle'", + "d": "占位形状;缺省 rect(整块矩形)" + }, + { + "n": "rows", + "t": "number", + "d": "text 形状的行数;缺省 3,末行自动缩短模拟段落" + } + ], + "example": "skeleton({ variant: 'text', rows: 3 }); skeleton({ variant: 'circle' }); skeleton({ variant: 'rect' })", + "steps": [ + "数据加载期间用 skeleton 顶替真实内容,加载完成后整体替换", + "文本段落用 variant: 'text' + rows 控制行数,头像用 'circle',卡片/图片区域用 'rect'", + "占位块自带扫光动画,reduced-motion 用户自动静态化为纯灰块", + "组合布局:skeleton 是普通 DOM,可放进 card / row 等任意容器" + ] + }, { "id": "toast", "title": "消息提示 Toast", diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index 9eb7d96..ba15b7d 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -379,6 +379,49 @@ background: color-mix(in srgb, var(--ua-color-danger) 12%, transparent); } +/* —— 骨架屏:占位灰块 + 扫光 —— */ +.ua-skeleton { + position: relative; + overflow: hidden; + border-radius: var(--ua-radius-sm); + background: color-mix(in srgb, var(--ua-color-border) 45%, transparent); +} +.ua-skeleton::after { + content: ''; + position: absolute; + inset: 0; + transform: translateX(-100%); + background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ua-color-bg) 65%, transparent), transparent); + animation: ua-shimmer 1.4s ease-in-out infinite; +} +@keyframes ua-shimmer { + to { + transform: translateX(100%); + } +} +.ua-skeleton-text { + height: 12px; + margin-bottom: var(--ua-space-2); +} +.ua-skeleton-text.ua-last { + width: 60%; + margin-bottom: 0; +} +.ua-skeleton-rect { + width: 100%; + height: 80px; + border-radius: var(--ua-radius-md); +} +.ua-skeleton-circle { + width: 40px; + height: 40px; + border-radius: 50%; + flex-shrink: 0; +} +.ua-skeleton-group { + width: 100%; +} + /* —— toast 消息提示:右上角堆叠,转瞬即逝 —— */ .ua-toast-host { position: fixed; @@ -707,6 +750,8 @@ .ua-alert, .ua-tip, .ua-toast, + .ua-skeleton, + .ua-skeleton::after, .ua-form-error, .ua-modal, .ua-modal-overlay {