iter: b03 新增 skeleton 骨架屏组件(三形状占位 + 扫光 + reduced-motion 兜底)

This commit is contained in:
HIsheR
2026-10-23 15:53:34 +08:00
parent ece38bcac9
commit 28820574e8
11 changed files with 162 additions and 10 deletions
+1
View File
@@ -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';
+12
View File
@@ -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);
}
+6
View File
@@ -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; // 提示文案
+28 -2
View File
@@ -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",
+45
View File
@@ -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 {