Files
UI/.ua/index.json
T
2026-10-23 16:49:45 +08:00

1039 lines
45 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"$schema": "ui-arch-registry-index/0.1",
"generatedAt": "2026-10-23T08:49:44.245Z",
"repoRoot": ".",
"packages": [
{
"name": "@ui-arch/frame",
"type": "headless-core",
"description": "自研微框架:signal / computed / effect 信号系统 + hyperscript(h)DOM 层。函数值属性与函数型子节点自动响应式;renderList 做列表整体替换;零依赖、无虚拟 DOM。",
"tags": [
"framework",
"signal",
"reactive",
"hyperscript"
],
"api": {
"entry": "src/index.ts",
"exports": [
"signal(initial) -> Signal<T>:无参读值并收集依赖,传参写值并触发订阅",
"effect(fn):立即执行并追踪依赖,信号变化自动重跑",
"computed(fn):由信号派生的只读计算值",
"h(tag, props, ...children):hyperscript;on* 绑事件,函数值属性/子节点自动响应式",
"renderList(el, fn):信号变化时整体替换容器子节点",
"mount(target, view):挂载视图"
],
"keyTypes": [
"Signal"
]
},
"usage": "const count = signal(0); mount('#app', () => h('button', { onClick: () => count(count() + 1) }, () => `count = ${count()}`))",
"conventions": [
"组件 = 返回 DOM 节点的普通函数,不做类组件与生命周期抽象",
"跨系统状态(如 table-core)用桥接信号:subscribe 里递增 version 信号"
],
"docs": {
"intro": "零依赖微框架:组件 = 返回 DOM 的普通函数。signal/effect/computed 管状态,h() 管 DOM,无虚拟 DOM 无生命周期抽象。",
"steps": [
"引入:import { signal, computed, effect, h, mount } from '@ui-arch/frame'",
"声明状态:const count = signal(0);无参调用读值 count(),传参写值 count(1)",
"渲染视图:mount('#app', () => h('div', null, () => `count = ${count()}`))——函数型子节点即响应式文本",
"绑定事件:props 里 on 开头且值为函数的键(onClick 等)自动 addEventListener",
"派生数据:const double = computed(() => count() * 2),视图里直接调用 double(),依赖自动追踪",
"列表渲染:renderList(tbody, () => rows.map(r => h('tr', null, r.name))),信号变化整体替换子节点"
]
},
"version": "0.1.0",
"dir": "packages/core/frame"
},
{
"name": "@ui-arch/table-core",
"type": "headless-core",
"description": "Headless table state machine: immutable state, dispatch/reducer, composable derive pipeline (filter -> sort -> paginate) with per-stage memoization. No DOM, no framework dependency.",
"tags": [
"table",
"headless",
"state-machine",
"datagrid"
],
"api": {
"entry": "src/index.ts",
"exports": [
"createTable(options) -> TableInstance: create headless table instance",
"sortPlugin(options?) -> multi-column sorting plugin (asc -> desc -> off cycle)",
"filterPlugin(options?) -> column filter plugin (default substring match, custom predicate supported)",
"paginationPlugin() -> client-side pagination with page clamping, exposes setPage/nextPage/prevPage/getPageInfo",
"selectionPlugin() -> rowKey-based selection, exposes selectAllVisible"
],
"keyTypes": [
"ColumnDef",
"TableState",
"TablePlugin",
"RowModel",
"Action"
]
},
"usage": "const table = createTable({ data, columns, rowKey: 'id', plugins: [filterPlugin(), sortPlugin(), paginationPlugin()], initialState: { pagination: { page: 1, pageSize: 20 } } }); table.dispatch({ type: 'toggleSort', key: 'name' }); table.getVisibleRows()",
"conventions": [
"selection/expanded must store rowKey, never row index",
"derive pipeline order is fixed: filter -> sort -> paginate",
"actions are the only way to mutate state; state is immutable"
],
"docs": {
"intro": "Headless 表格内核:不渲染任何 DOM,只管理状态与派生。任何渲染层通过 getVisibleRows() 拿行模型,配 subscribe() 刷新。",
"steps": [
"创建实例:const table = createTable({ data, columns, rowKey: 'id', plugins: [filterPlugin(), sortPlugin(), paginationPlugin()] })",
"取派生数据:table.getVisibleRows() 返回 { rows, total };total 语义固定 = 筛选后、分页前的行数",
"变更状态:只能 dispatch action,如 table.dispatch({ type: 'toggleSort', key: 'name' });状态不可变",
"订阅刷新:table.subscribe(fn),fn 里重新 getVisibleRows() 并交给渲染层",
"分页信息:api.getPageInfo() 返回 { page, pageSize, totalPages };越界页码自动钳制",
"扩展能力:实现 TablePlugin(derive / patchState / api 三钩子)传入 plugins 即插入管线"
]
},
"version": "0.1.0",
"dir": "packages/core/table-core"
},
{
"name": "@ui-arch/tokens",
"type": "design-tokens",
"description": "Single source of design tokens. Skin packages consume CSS variables only - hardcoded colors inside skins are forbidden. Dark theme overrides values under [data-theme='dark'], never names.",
"tags": [
"tokens",
"theme",
"css-variables"
],
"api": {
"entry": "tokens.css",
"exports": [
"tokens.css: import '@ui-arch/tokens/tokens.css' once at app root",
"tokens.json: programmatic access for tools and agents"
],
"keyTypes": [
"--ua-color-*",
"--ua-space-*",
"--ua-radius-*"
]
},
"usage": "import '@ui-arch/tokens/tokens.css'; then use var(--ua-color-accent) etc. in any skin package",
"docs": {
"intro": "设计令牌唯一来源:CSS 变量 --ua-* + 同名 JSON。皮肤只允许消费变量,禁止硬编码颜色;暗色只换值不换名。",
"steps": [
"引入:在应用入口 import '@ui-arch/tokens/tokens.css'",
"消费:组件样式一律 var(--ua-color-accent) 这类变量,不写死色值",
"暗色:根元素设 data-theme=dark,变量值自动切换",
"扩展:新令牌先同时加进 tokens.css 与 tokens.json,再重新生成文档站"
]
},
"version": "0.1.0",
"dir": "packages/design/tokens"
},
{
"name": "@ui-arch/ui",
"type": "skin-dom",
"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",
"skin",
"table",
"form",
"feedback"
],
"api": {
"entry": "src/index.ts",
"exports": [
"button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用",
"textField(props):文本输入,value 信号双向绑定,disabled 支持布尔或信号禁用",
"textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号禁用",
"checkbox(props):复选框,checked 信号双向绑定,disabled 支持布尔或信号禁用",
"switchButton(props):开关,checked 信号双向绑定,disabled 支持布尔或信号禁用",
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用",
"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",
"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 多行,扫光微动画",
"pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达",
"spinner(props):加载指示器,size 控制直径",
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭",
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
"formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄",
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core"
],
"keyTypes": [
"ButtonProps",
"TextFieldProps",
"TextareaProps",
"CheckboxProps",
"SwitchProps",
"SelectProps",
"RadioGroupProps",
"MenuItem",
"MenuProps",
"AccordionItem",
"AccordionProps",
"BreadcrumbItem",
"BreadcrumbProps",
"OptionItem",
"CardProps",
"BadgeProps",
"AlertProps",
"ToastProps",
"TooltipProps",
"SkeletonProps",
"PaginationProps",
"SpinnerProps",
"ProgressProps",
"TabsProps",
"ModalProps",
"FormRule",
"FormItemProps",
"FormItemHandle",
"FormProps",
"DataTableProps"
]
},
"usage": "const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')",
"conventions": [
"皮肤不持有业务状态,一律委托 headless 内核或调用方信号",
"表单类组件统一约定:*Props.value/checked/active 为 Signal 双向绑定,onChange/onInput 为回调",
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
],
"docs": {
"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'",
"创建状态:import { signal } from '@ui-arch/frame',如 const name = signal('')",
"调用组件:const el = textField({ value: name }),把 el 挂到你的容器或配合 h() 组合",
"读写状态:name() 读、name('张三') 写,界面自动同步,无需手动操作 DOM"
],
"components": [
{
"id": "button",
"title": "按钮 Button",
"demo": "sec-counter",
"props": [
{
"n": "label",
"t": "string",
"r": 1,
"d": "按钮文案"
},
{
"n": "variant",
"t": "'primary' | 'ghost' | 'danger'",
"d": "视觉强度;缺省 primary",
"df": "primary"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "禁用态:布尔直接置位,传信号则动态同步"
},
{
"n": "onClick",
"t": "() => void",
"k": 1,
"d": "点击回调"
}
],
"example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })",
"steps": [
"确定语义强度:主操作用 primary(默认),次要操作用 ghost,危险操作用 danger",
"绑定 onClick 回调处理点击逻辑",
"需要禁用态时自行在回调首行卫语句判断,或外层包条件渲染"
]
},
{
"id": "textField",
"title": "文本输入 TextField",
"demo": "sec-bind",
"props": [
{
"n": "value",
"t": "Signal<string>",
"d": "双向绑定信号:输入写回信号,信号变化写回输入框"
},
{
"n": "placeholder",
"t": "string",
"d": "占位文案"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "禁用态:布尔直接置位,传信号则动态同步"
},
{
"n": "onInput",
"t": "(value: string) => void",
"k": 1,
"d": "输入回调,每次按键触发"
}
],
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
"steps": [
"先用 signal('') 创建字符串信号",
"传入 value 完成双向绑定:用户输入自动写回,外部赋值自动回显",
"在任意响应式位置用 name() 读取最新值,如 () => `你好,${name()}`"
]
},
{
"id": "textarea",
"title": "多行输入 Textarea",
"demo": "sec-form",
"props": [
{
"n": "value",
"t": "Signal<string>",
"d": "双向绑定信号"
},
{
"n": "rows",
"t": "number",
"d": "初始行数;缺省 3",
"df": "3"
},
{
"n": "placeholder",
"t": "string",
"d": "占位文案"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "禁用态:布尔直接置位,传信号则动态同步"
},
{
"n": "onInput",
"t": "(value: string) => void",
"k": 1,
"d": "输入回调"
}
],
"example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })",
"steps": [
"创建信号并传入 value",
"用 rows 控制初始高度,用户可纵向拉伸",
"字数统计等派生逻辑直接在响应式文本里读 bio().length"
]
},
{
"id": "checkbox",
"title": "复选框 Checkbox",
"demo": "sec-form",
"props": [
{
"n": "checked",
"t": "Signal<boolean>",
"d": "勾选状态信号,双向绑定"
},
{
"n": "label",
"t": "string",
"d": "右侧文案;缺省只渲染勾选框"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "禁用态:布尔直接置位,传信号则动态同步"
},
{
"n": "onChange",
"t": "(checked: boolean) => void",
"k": 1,
"d": "变更回调"
}
],
"example": "const agreed = signal(false); checkbox({ label: '同意条款', checked: agreed, onChange: (v) => console.log(v) })",
"steps": [
"创建布尔信号 agreed = signal(false)",
"传入 checked 与 label,勾选即写回信号",
"表单提交前用 agreed() 判断是否勾选"
]
},
{
"id": "switchButton",
"title": "开关 Switch",
"demo": "sec-form",
"props": [
{
"n": "checked",
"t": "Signal<boolean>",
"d": "开关状态信号,双向绑定"
},
{
"n": "label",
"t": "string",
"d": "左侧文案"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "禁用态:布尔直接置位,传信号则动态同步"
},
{
"n": "onChange",
"t": "(checked: boolean) => void",
"k": 1,
"d": "变更回调"
}
],
"example": "const night = signal(true); switchButton({ label: '夜间模式', checked: night })",
"steps": [
"创建布尔信号并传入 checked",
"轨道与滑块的动画由 CSS 完成,无需干预",
"状态变化在 onChange 里做副作用(如切换主题)"
]
},
{
"id": "select",
"title": "下拉选择 Select(自绘弹层)",
"demo": "sec-form",
"props": [
{
"n": "options",
"t": "OptionItem[]",
"r": 1,
"d": "选项列表,每项 { value, label }"
},
{
"n": "value",
"t": "Signal<string>",
"d": "选中值信号,双向绑定"
},
{
"n": "placeholder",
"t": "string",
"d": "未选中时触发器显示的占位文案"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "禁用态:布尔直接置位,传信号则动态同步;禁用时触发器置灰且不展开弹层"
},
{
"n": "onChange",
"t": "(value: string) => void",
"k": 1,
"d": "选中回调"
}
],
"example": "const city = signal(''); select({ options: [{ value: 'bj', label: '北京' }], value: city, placeholder: '选择城市…' })",
"steps": [
"准备 options 数组,value 是提交用值、label 是展示文案",
"创建信号并传入 value;未传时组件内部自持状态",
"点击触发器展开弹层,点选项写回信号并收起;点外部或按 Esc 也会收起",
"选中项在弹层中用淡色弱化标记,不抢视觉焦点"
]
},
{
"id": "menu",
"title": "下拉菜单 Menu",
"demo": "sec-menu",
"props": [
{
"n": "label",
"t": "string",
"r": 1,
"d": "触发器文案"
},
{
"n": "items",
"t": "MenuItem[]",
"r": 1,
"d": "菜单条目:{ value, label, disabled?, danger?, divider? }"
},
{
"n": "variant",
"t": "'primary' | 'ghost'",
"d": "触发器视觉强度;缺省 ghost",
"df": "ghost"
},
{
"n": "align",
"t": "'start' | 'end'",
"d": "弹层对齐方向;缺省 start(与触发器左对齐)",
"df": "start"
},
{
"n": "onSelect",
"t": "(value: string) => void",
"k": 1,
"d": "选中回调,禁用项不触发"
}
],
"example": "menu({ label: '操作', items: [{ value: 'edit', label: '编辑' }, { value: 'del', label: '删除', danger: true }], onSelect: (v) => console.log(v) })",
"steps": [
"准备 items 数组:value 是回调标识、label 是展示文案,插一条 { divider: true } 即可分出条目组",
"传 label 决定触发器文案;默认 ghost 轻量样式,主操作用 variant: 'primary'",
"点触发器展开弹层,点条目收起并触发 onSelect;点外部或按 Esc 也会收起(Esc 后焦点回到触发器)",
"键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过",
"靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true"
]
},
{
"id": "radioGroup",
"title": "单选组 RadioGroup",
"demo": "sec-form",
"props": [
{
"n": "options",
"t": "OptionItem[]",
"r": 1,
"d": "选项列表"
},
{
"n": "value",
"t": "Signal<string>",
"d": "选中值信号,双向绑定"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
"d": "整组禁用:全部选项与文案置灰且不可选"
},
{
"n": "onChange",
"t": "(value: string) => void",
"k": 1,
"d": "变更回调"
}
],
"example": "const size = signal('m'); radioGroup({ options: [{ value: 's', label: '小' }, { value: 'm', label: '中' }], value: size })",
"steps": [
"创建字符串信号,初值设为默认选项的 value",
"传入 options 与 value,组内自动互斥",
"读取 size() 获取当前选中值",
"需要整组禁用时传 disabled:布尔值静态禁用,也可传信号(如 locked)实现动态锁定"
]
},
{
"id": "accordion",
"title": "折叠面板 Accordion",
"demo": "sec-accordion",
"props": [
{
"n": "items",
"t": "AccordionItem[]",
"r": 1,
"d": "面板列表:{ value, title, content, disabled? }"
},
{
"n": "active",
"t": "Signal<string>",
"d": "受控展开面板信号(单开模式);缺省由组件内部持有状态"
},
{
"n": "multiple",
"t": "boolean",
"d": "允许多个面板同时展开;缺省 false(手风琴,单开)",
"df": "false"
},
{
"n": "onChange",
"t": "(active: string) => void",
"k": 1,
"d": "展开变化回调:收起时传空串"
}
],
"example": "accordion({ items: [{ value: 'a', title: '账户设置', content: '内容…' }, { value: 'b', title: '通知', content: h('p', null, '…') }] })",
"steps": [
"准备 items:value 作标识、title 作标题、content 可为字符串 / DOM 节点 / 响应式函数",
"默认手风琴模式:展开一个自动收起其他;需要同时展开多个时传 multiple: true",
"需要外部控制或读取展开项时传 active 信号(受控单开),否则组件内部自持状态",
"标题是原生 button:Tab 聚焦、Enter / Space 切换,展开态带 aria-expanded + region 关联",
"内容区用 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(渲染 <a>);单页应用想自行接管时不给 href,改用 onNavigate 回调",
"改分隔符传 separator,如 '›';分隔符对读屏隐藏",
"语义结构为 nav + ol + li,可直接放进页头或卡片标题栏"
]
},
{
"id": "card",
"title": "卡片 Card",
"demo": "sec-card",
"props": [
{
"n": "title",
"t": "string",
"d": "标题;缺省不渲染标题行"
},
{
"n": "...children",
"t": "unknown[]",
"d": "任意子节点:字符串 / DOM / 响应式函数"
}
],
"example": "card({ title: '用户表' }, dataTable({ data, columns }))",
"steps": [
"card 是容器组件:第一个参数是 props,其余参数是任意子节点",
"传 title 显示标题行,否则只有内容区",
"子节点可以混合字符串、DOM 节点与响应式函数"
]
},
{
"id": "badge",
"title": "徽标 Badge",
"demo": "sec-feedback",
"props": [
{
"n": "label",
"t": "string",
"r": 1,
"d": "徽标文案"
},
{
"n": "variant",
"t": "'default' | 'success' | 'warning' | 'danger'",
"d": "语义色;缺省 default",
"df": "default"
}
],
"example": "badge({ label: '成功', variant: 'success' })",
"steps": [
"选择语义色:default 中性、success 成功、warning 警告、danger 危险",
"传入 label 即可,纯展示组件无状态"
]
},
{
"id": "alert",
"title": "提示条 Alert",
"demo": "sec-feedback",
"props": [
{
"n": "message",
"t": "string",
"r": 1,
"d": "提示内容"
},
{
"n": "variant",
"t": "'info' | 'success' | 'warning' | 'danger'",
"d": "语义色;缺省 info",
"df": "info"
}
],
"example": "alert({ message: '操作可能存在风险', variant: 'warning' })",
"steps": [
"按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误",
"传入 message 即可,入场自带下滑动画"
]
},
{
"id": "skeleton",
"title": "骨架屏 Skeleton",
"demo": "sec-skeleton",
"props": [
{
"n": "variant",
"t": "'text' | 'rect' | 'circle'",
"d": "占位形状;缺省 rect(整块矩形)",
"df": "rect"
},
{
"n": "rows",
"t": "number",
"d": "text 形状的行数;缺省 3,末行自动缩短模拟段落",
"df": "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",
"demo": "sec-toast",
"props": [
{
"n": "message",
"t": "string",
"r": 1,
"d": "提示文案"
},
{
"n": "duration",
"t": "number",
"d": "自动消失毫秒;缺省 3000",
"df": "3000"
},
{
"n": "variant",
"t": "'info' | 'success' | 'warning' | 'danger'",
"d": "语义色;缺省 info(toast.error 对应 danger)",
"df": "info"
}
],
"example": "toast.success('保存成功'); toast.error('网络请求失败', 5000)",
"steps": [
"直接调用语义工厂:toast.info / success / warning / error(message, duration?),无需先建容器",
"不传 duration 时默认停留 3 秒后自动淡出并移除节点",
"多条消息在右上角自动纵向堆叠,互不遮挡",
"需要完整入参(含 variant)时用 showToast(props);组件挂载容器由内部懒创建,样式随 ui.css 一起引入"
]
},
{
"id": "tooltip",
"title": "悬浮提示 Tooltip",
"demo": "sec-tooltip",
"props": [
{
"n": "content",
"t": "string",
"r": 1,
"d": "提示文案(纯文本)"
},
{
"n": "placement",
"t": "'top' | 'bottom'",
"d": "气泡方位;缺省 top(触发器上方)",
"df": "top"
},
{
"n": "delay",
"t": "number",
"d": "出场延迟毫秒;缺省 150,传 0 立即显示",
"df": "150"
},
{
"n": "trigger",
"t": "HTMLElement",
"r": 1,
"d": "第二个参数:被包裹的触发器元素(按钮 / 输入框等任意 DOM)"
}
],
"example": "tooltip({ content: '保存后立即生效' }, button({ label: '保存', onClick: save }))",
"steps": [
"先创建触发器元素,如 const btn = button({ label: '保存' })",
"用 tooltip(props, trigger) 包一层:返回带气泡的包裹元素,原触发器仍是独立节点",
"鼠标移入或键盘聚焦约 150ms 后出场,移出 / 失焦立即收起;按 Esc 可立刻关闭",
"气泡默认在上方,空间不足或需要避让时传 placement: 'bottom'",
"气泡 pointer-events: none 不抢指针;判读依赖 aria-describedby + role='tooltip',读屏可读",
"只做展示:不要在气泡里放可点击元素,需要交互请用 modal 或自绘弹层"
]
},
{
"id": "spinner",
"title": "加载指示器 Spinner",
"demo": "sec-feedback",
"props": [
{
"n": "size",
"t": "number",
"d": "直径像素;缺省 18",
"df": "18"
}
],
"example": "spinner(); spinner({ size: 26 })",
"steps": [
"数据加载时插入 spinner(),加载完成移除",
"用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)"
]
},
{
"id": "progress",
"title": "进度条 Progress",
"demo": "sec-feedback",
"props": [
{
"n": "value",
"t": "Signal<number>",
"r": 1,
"d": "进度 0-100 信号,变化自动平滑过渡"
},
{
"n": "showText",
"t": "boolean",
"d": "是否显示百分比文字;缺省 true",
"df": "true"
}
],
"example": "const pct = signal(0); progress({ value: pct }); setInterval(() => pct((pct() + 5) % 105), 400)",
"steps": [
"创建数值信号 pct = signal(0),范围 0-100(越界自动钳制)",
"传入 value,填充宽度与百分比文字随信号平滑过渡",
"进行中填充带流动条纹,到 100% 自动变 success 色并停止条纹",
"不需要百分比文字时传 showText: false"
]
},
{
"id": "tabs",
"title": "标签页 Tabs",
"demo": "sec-tabs-modal",
"props": [
{
"n": "tabs",
"t": "OptionItem[]",
"r": 1,
"d": "页签列表,每项 { value, label }"
},
{
"n": "active",
"t": "Signal<string>",
"d": "当前激活页签 value 信号,双向绑定"
},
{
"n": "onChange",
"t": "(value: string) => void",
"k": 1,
"d": "切换回调"
}
],
"example": "const tab = signal('a'); tabs({ tabs: [{ value: 'a', label: '概览' }, { value: 'b', label: '明细', disabled: true }], active: tab })",
"steps": [
"创建信号 tab = signal('a'),初值为默认页签",
"tabs 只渲染页签头;页签内容用响应式函数按信号切换,如 () => tab() === 'a' ? panelA : panelB",
"点击或方向键(←→ 环形)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点",
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
]
},
{
"id": "pagination",
"title": "分页器 Pagination",
"demo": "sec-table",
"props": [
{
"n": "page",
"t": "Signal<number>",
"r": 1,
"d": "当前页码信号,双向绑定:外部赋值自动翻页,点击页码写回信号"
},
{
"n": "pageSize",
"t": "number",
"r": 1,
"d": "每页条数"
},
{
"n": "total",
"t": "number",
"r": 1,
"d": "总条数"
},
{
"n": "onChange",
"t": "(page: number) => void",
"k": 1,
"d": "翻页回调"
}
],
"example": "const page = signal(1); pagination({ page, pageSize: 3, total: 24, onChange: (p) => console.log(p) })",
"steps": [
"创建页码信号 page = signal(1),与 pageSize / total 一起传入",
"页码 ≤7 全部展开;更多时只保留首尾与当前页 ±1,缺口折叠为省略号",
"首末页自动禁用 ‹ › 翻页按钮,当前页禁点并标记 aria-current",
"与 dataTable 联动:把同一个 page 信号传给 dataTable 的 page 参数,两处翻页自动同步",
"键盘可达:全部是原生 button,Tab 聚焦后 Enter / Space 触发"
]
},
{
"id": "modal",
"title": "弹窗 Modal",
"demo": "sec-tabs-modal",
"props": [
{
"n": "open",
"t": "Signal<boolean>",
"r": 1,
"d": "开关信号:true 显示 / false 隐藏"
},
{
"n": "title",
"t": "string",
"r": 1,
"d": "标题"
},
{
"n": "onClose",
"t": "() => void",
"k": 1,
"d": "关闭回调(点遮罩、× 或 Esc 触发)"
},
{
"n": "...children",
"t": "unknown[]",
"d": "弹窗内容子节点"
}
],
"example": "const open = signal(false); modal({ title: '确认操作', open, onClose: () => open(false) }, h('p', null, '内容'))",
"steps": [
"创建布尔信号 open = signal(false),弹窗初始隐藏",
"把 open 传给 modal,其余参数为弹窗内容子节点",
"打开:open(true);关闭:open(false),点遮罩或 × 会自动写回 false",
"入场自带遮罩淡入 + 弹窗上浮动画"
]
},
{
"id": "form",
"title": "表单 Form / 表单字段 FormItem",
"demo": "sec-formlab",
"props": [
{
"n": "layout",
"t": "'vertical' | 'horizontal'",
"d": "布局方向;缺省 vertical(horizontal 为标签左置)",
"df": "vertical"
},
{
"n": "submitText",
"t": "string",
"d": "提交按钮文案;缺省「提交」"
},
{
"n": "resetText",
"t": "string",
"d": "重置按钮文案;缺省不渲染"
},
{
"n": "onSubmit",
"t": "() => void",
"k": 1,
"d": "全部字段校验通过后的提交回调"
},
{
"n": "(formItem)label",
"t": "string",
"r": 1,
"d": "字段标签"
},
{
"n": "(formItem)value",
"t": "Signal<string>",
"r": 1,
"d": "字段值信号,校验数据源"
},
{
"n": "(formItem)required",
"t": "boolean",
"d": "必填:显示红星并自动追加非空规则"
},
{
"n": "(formItem)rules",
"t": "FormRule[]",
"d": "校验规则:required / min / max / pattern / validator,按序执行取首个失败"
},
{
"n": "(formItem)validateOn",
"t": "'input' | 'blur' | 'submit'",
"d": "校验时机;缺省 blur(输入时自动清已报错误)",
"df": "blur"
}
],
"example": "const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label: '用户名', required: true, value: name, rules: [{ min: 2, max: 12 }] }, textField({ value: name }))])",
"steps": [
"为每个字段创建 signal 与控件,如 const name = signal(''); textField({ value: name })",
"用 formItem 包住控件:formItem({ label, required, value, rules }, control),返回 { el, validate } 句柄",
"把句柄数组交给 form:form({ onSubmit }, items),form 渲染全部字段 + 提交/重置按钮",
"提交时逐项校验:全过触发 onSubmit;有错则标红控件、显示错误文案并聚焦首个出错项",
"规则语义:required 非空(trim)、min/max 长度、pattern 正则、validator 自定义(true 通过 / 字符串为错误)",
"非必填字段为空时跳过其余规则;Enter 键触发表单原生 submit(已拦截走校验流程)"
]
},
{
"id": "dataTable",
"title": "数据表格 DataTable",
"demo": "sec-table",
"props": [
{
"n": "data",
"t": "T[]",
"r": 1,
"d": "全量行数据,每行须含 id 字段作 rowKey"
},
{
"n": "columns",
"t": "ColumnDef<T>[]",
"r": 1,
"d": "列定义:{ key, header?, sortable? }"
},
{
"n": "filterKey",
"t": "string",
"d": "工具栏筛选的列;缺省取第一列"
},
{
"n": "pageSize",
"t": "number",
"d": "每页行数;缺省 5",
"df": "5"
},
{
"n": "selectable",
"t": "boolean",
"d": "启用行选择列:全选三态(含半选)、行 checkbox、已选计数;缺省 false",
"df": "false"
},
{
"n": "page",
"t": "Signal<number>",
"d": "受控页码信号:与内核分页双向同步,传同一个信号给独立 pagination 组件即可联动"
},
{
"n": "loading",
"t": "boolean | Signal<boolean>",
"d": "加载态:为真时数据行换成骨架行(行数随 pageSize),筛选输入与翻页按钮临时禁用,容器带 aria-busy"
}
],
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
"steps": [
"准备行数据,确保每行有唯一 id(跨页选择与刷新不丢行靠它)",
"定义 columns:sortable: true 的列表头可点击排序(升 → 降 → 取消循环),标记箭头 ↑↓↕",
"selectable: true 开启行选择:表头全选框带 indeterminate 半选态,工具栏显示已选计数",
"选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失",
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供",
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用"
]
}
]
},
"version": "0.1.0",
"dir": "packages/skin/ui"
}
]
}