3636 lines
160 KiB
JSON
3636 lines
160 KiB
JSON
{
|
||
"name": "@ui-arch/ui",
|
||
"type": "skin-dom",
|
||
"description": "DOM 皮肤组件库:表单 / 反馈 / 浮层 / 导航 / 数据展示,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌(组件全量见 api.exports 或刚体清单)",
|
||
"tags": [
|
||
"components",
|
||
"dom",
|
||
"skin",
|
||
"table",
|
||
"form",
|
||
"feedback"
|
||
],
|
||
"api": {
|
||
"entry": "src/index.ts",
|
||
"exports": [
|
||
"button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)",
|
||
"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 显示实时字数并设原生上限",
|
||
"slider(props):滑块,value 信号双向绑定,min/max/step 可配,原生 range 承载键盘交互,右侧可显示当前值",
|
||
"checkbox(props):复选框,checked 信号双向绑定,indeterminate 半选态(读屏播报 mixed),disabled 支持布尔或信号禁用",
|
||
"switchButton(props):开关,checked 信号双向绑定,loading 加载锁(滑块转圈 + aria-busy),disabled 支持布尔或信号禁用",
|
||
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用,空间不足自动翻转,键入字母快跳选项;searchable 开启弹层搜索框,clearable 有值时给清空按钮",
|
||
"menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭,空间不足自动翻转,键入字母快跳菜单项",
|
||
"flip(trigger, panel, preferred):浮层上下翻转判定,按触发器与面板的视口几何决定 preferred 方位放不下时是否翻转,返回实际生效方位 'top' | 'bottom'",
|
||
"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",
|
||
"tag(props):可关闭标签,variant 五档语义色,closable + onClose 支持从数据源移除(纯展示请用 badge)",
|
||
"avatar(props):头像,有 src 渲染图片(alt 用名称)、无 src 用首字占位,size 可调 + 四档语义底色",
|
||
"alert(props):提示条,variant 四档语义色 + 可选 title 与 closable 关闭按钮,role=alert 读屏可播报",
|
||
"toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失(悬停暂停、移出续时),出入场微动画;showToast(props) 为完整入参版本(closable 显示关闭按钮,同屏最多 4 条)",
|
||
"tooltip(props, trigger):包裹式悬浮提示,hover / 键盘聚焦延迟出场,Esc 收起;触摸屏按下即出、抬手延迟 1.2s 收起,气泡 pointer-events: none 不挡点击",
|
||
"popover(props, trigger):可交互气泡卡片,包裹触发器,默认点击开合 + 点外部 / Esc 收起,content 可放按钮等交互元素;视口一侧空间不足时自动翻转",
|
||
"skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画",
|
||
"pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,jumpable 时末尾可输入页码直接跳转,键盘可达;pageSizes 可切每页条数",
|
||
"spinner(props):加载指示器,size 控制直径",
|
||
"progress(props):进度条,value 信号驱动 0-100(满格切 success 色);不传 value 退化为不确定态无限往复",
|
||
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签;页签项 closable 时带关闭按钮(聚焦后 Delete 同样可关),关闭只回调 onClose 由调用方摘数据",
|
||
"stepper(props):步骤条,done/current/upcoming 三态,active 信号双向绑定,clickable 时已完成步骤可回跳",
|
||
"rate(props):评分星标,value 信号双向绑定,hover 预览移出还原,键盘方向键调节,max 可配,allowHalf 开启半星(左半区 0.5 分)",
|
||
"empty(props):空状态占位,title/description 可选,action 放操作区",
|
||
"divider(props):分隔线,水平/垂直两种方向,可带居中文案标签,dashed 切虚线",
|
||
"space(props):间距容器,flex gap 统一子元素间距,direction / size / align / justify / wrap / block 可调",
|
||
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭,size 三档宽度,footer 提供底部操作区",
|
||
"drawer(props, ...children):抽屉,open 信号驱动侧滑面板,placement 左右可配、width 可调、footer 操作区,遮罩 / × / Esc 关闭",
|
||
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
||
"formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄",
|
||
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core",
|
||
"image(props):图片,缩略图点击 / 回车开遮罩大图预览(Esc 或点击遮罩关闭),width 控制缩略宽,加载失败自动落占位(fallback 自定义文案)",
|
||
"timeline(props):时间线,items 按序渲染纵向事件流(标题 / 时间 / 正文 + 语义色节点),mode 控制轴线左右,pending 追加进行中节点",
|
||
"segmented(props):分段控制器,胶囊分段 + 滑动滑块,value 信号双向绑定,←→ 环形导航,整组或单项可禁用",
|
||
"descriptions(props):描述列表,items(label/value/span) 铺进 column 列网格,bordered 切表格形态,窄屏自动单列",
|
||
"statistic(props):统计数值,标题 + 大号数字 + 前后缀 + 环比趋势,value 变化时 rAF 滚动过渡(400ms easeOutCubic),减弱动效偏好下直落终值",
|
||
"list(props):列表,header/footer 外壳 + 条目 avatar/title/description/extra,onItemClick 让条目可点击,items 为空回落空状态",
|
||
"carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控",
|
||
"tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应",
|
||
"upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求)",
|
||
"notification(props):右下角通知卡片(标题 + 描述 + 操作区),返回 { el, close } 句柄,缺省常驻需手动关闭",
|
||
"result(props):结果页,status 四档语义色 + 标题 + 说明 + 操作区,用于提交完成/校验失败等终态",
|
||
"transfer(props):穿梭框,左右两栏勾选后整体搬运,targetKeys 信号双向绑定,两栏各自搜索与全选(含半选),禁用条目不参与搬运也不可勾选",
|
||
"cascader(props):级联选择,多列联动面板(悬停下钻 / 点击提交),value 为路径数组信号,changeOnSelect 允许停在中间层,全键盘可操作",
|
||
"splitter(props):可拖拽分栏容器,分隔条支持指针拖拽 / 方向键微调 / 双击复位,sizes 为百分比数组信号",
|
||
"autoComplete(props):输入联想,输入即过滤候选、↑↓ 环形高亮 / Enter 选中 / Esc 收起,options 可传函数做远程联想",
|
||
"datePicker(props):日期选择,单月网格 + 翻月 / 今天快捷键,value 为 YYYY-MM-DD 信号,min/max 闭区间限制,方向键逐日逐周可导航",
|
||
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
||
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
||
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose",
|
||
"watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互",
|
||
"checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项",
|
||
"affix(props, ...children):固钉,滚过 offset 后内容转 fixed 贴顶、外层保留占位高度,固定后宽度随容器自适应",
|
||
"multiSelect(props):多选下拉,标签式回显 + 可搜索弹层,点选不收起便于连续多选,max 到限后未选项禁用",
|
||
"anchor(props):锚点导航,竖排区块链接,滚动时按目标位置高亮当前区块(aria-current 同步),点击按 offset 偏移量平滑定位并回写 hash;container 可指定内滚容器",
|
||
"popconfirm(props, trigger):二次确认气泡,挂在任意触发器上,就地确认危险操作;确定后先收起再回调,取消 / 点外部 / Esc 走 onCancel,展开即聚焦「取消」",
|
||
"calendar(props):月历,整月网格内嵌页面(排期 / 值班 / 日程),每日可带 marker 标记文案,方向键漫游 + Enter 选中,翻月走 onMonthChange",
|
||
"typography(props):排版文本,variant 决定语义(标题出 h1-h5 / 段落出 p / 行内出 span),支持语义色、装饰位、对齐、多行截断与复制按钮",
|
||
"grid(props, ...children):栅格容器,columns 固定列数或 min 走 auto-fit 自适应列宽,gutter 统一列间距,窄屏自动降为单列",
|
||
"floatButton(props):悬浮操作按钮,固定停靠左下 / 右下角,label 缺省为纯图标圆形,badge 走角标,tooltip 兼作 aria-label",
|
||
"mentions(props):@提及输入,textarea 内键入 @ 弹候选(名字 / 关键词过滤),↑↓ 环形移动、Enter/Tab 选中、Esc 只收本层,选中按光标替换并写回 value",
|
||
"treeSelect(props):树形选择,按钮触发弹层并复用 tree(搜索 / 多选 / 级联),单选选中即收起、多选保持展开,Esc 与点外收起",
|
||
"link(props):链接,站内跳转与站外跳转统一入口,external 自动补 ↗ 与 rel=noopener noreferrer 并给读屏播报",
|
||
"avatarGroup(props):头像组,横向叠放并在超出 max 时折叠成 +N,整组 title 列出全部姓名",
|
||
"virtualList(props):虚拟列表,固定行高长列表只渲染可视窗口 + 上下缓冲条,万级数据滚动不随总量掉帧;返回 { el, scrollTo } 句柄",
|
||
"commandPalette(props):命令面板,⌘K / Ctrl+K 唤起,输入即过滤(主文案/说明/关键词三层),↑↓ 环形选择 + 回车执行 + Esc 收起,选中与关闭都归还焦点;返回 { el, open, close } 句柄"
|
||
],
|
||
"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",
|
||
"PopoverProps",
|
||
"SliderProps",
|
||
"TagProps",
|
||
"DrawerProps",
|
||
"AvatarProps",
|
||
"TimelineItem",
|
||
"TimelineProps",
|
||
"SegmentedProps",
|
||
"DescriptionItem",
|
||
"DescriptionsProps",
|
||
"StatisticProps"
|
||
]
|
||
},
|
||
"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,一律来自令牌,禁止硬编码颜色",
|
||
"无障碍基线:装饰性占位(skeleton)aria-hidden、加载指示(spinner)role=status、关闭按钮必须 aria-label、弹层弹窗 aria-haspopup/expanded、读屏可见的状态一律同步 aria-*"
|
||
],
|
||
"docs": {
|
||
"intro": "28 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。\n\n阴影对照表:下拉弹层=shadow-2 / 浮层与悬浮按钮=shadow-3 / 模态弹窗与抽屉及通知=shadow-4 / 卡片与输入组轻浮起=shadow-1;主按钮与强调件的主题色投影=shadow-accent / shadow-accent-strong,组件内不得再手写 box-shadow 字面值。",
|
||
"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": "image",
|
||
"title": "图片 Image",
|
||
"demo": "sec-image",
|
||
"props": [
|
||
{
|
||
"n": "src",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "图片地址(缩略图与大图共用)"
|
||
},
|
||
{
|
||
"n": "alt",
|
||
"t": "string",
|
||
"d": "替代文本;缺省空串",
|
||
"df": "''"
|
||
},
|
||
{
|
||
"n": "width",
|
||
"t": "number",
|
||
"d": "缩略图宽度像素,高度自适应;缺省走样式默认"
|
||
},
|
||
{
|
||
"n": "fallback",
|
||
"t": "string",
|
||
"df": "'图片加载失败'",
|
||
"d": "加载失败时缩略图内显示的占位文案;缺省「图片加载失败」"
|
||
}
|
||
],
|
||
"example": "image({ src: '/a.png', alt: '架构图', width: 160 })",
|
||
"steps": [
|
||
"基础用法只传 src:渲染缩略图,hover 上浮提亮,光标变放大镜",
|
||
"点击缩略图(或聚焦后回车)打开全屏遮罩大图,Esc 或点击遮罩任意处关闭,焦点归还缩略图",
|
||
"宽度用 width(像素)控制,高度按比例自适应,容器内不撑破 max-width",
|
||
"alt 尽量传有意义的描述,读屏与图片加载失败时都依赖它"
|
||
]
|
||
},
|
||
{
|
||
"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": "type",
|
||
"t": "'button' | 'submit' | 'reset'",
|
||
"d": "原生类型;缺省 button,表单内不会意外提交"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用态:布尔直接置位,传信号则动态同步"
|
||
},
|
||
{
|
||
"n": "onClick",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "点击回调"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "'sm' | 'md' | 'lg'",
|
||
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||
}
|
||
],
|
||
"example": "button({ label: '提交', variant: 'primary', onClick: () => console.log('clicked') })",
|
||
"steps": [
|
||
"确定语义强度:主操作用 primary(默认),次要操作用 ghost,危险操作用 danger",
|
||
"绑定 onClick 回调处理点击逻辑",
|
||
"需要禁用态时自行在回调首行卫语句判断,或外层包条件渲染",
|
||
"放进 <form> 里需要提交时显式传 type: \"submit\";缺省 button 不会触发原生提交"
|
||
]
|
||
},
|
||
{
|
||
"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": "输入回调,每次按键触发"
|
||
},
|
||
{
|
||
"n": "prefix",
|
||
"t": "string | HTMLElement",
|
||
"d": "前置附加块(textField/inputNumber 在输入框内侧前、textarea 在上方);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "suffix",
|
||
"t": "string | HTMLElement",
|
||
"d": "后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "clearable",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "开启后有值即显示一键清空按钮;缺省 false"
|
||
},
|
||
{
|
||
"n": "maxCount",
|
||
"t": "number",
|
||
"d": "字数上限:同时设原生 maxlength,并在输入框内侧显示实时计数;缺省不限"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "'sm' | 'md' | 'lg'",
|
||
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||
}
|
||
],
|
||
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
|
||
"steps": [
|
||
"先用 signal('') 创建字符串信号",
|
||
"传入 value 完成双向绑定:用户输入自动写回,外部赋值自动回显",
|
||
"在任意响应式位置用 name() 读取最新值,如 () => `你好,${name()}`",
|
||
"需要图标、货币符、单位等前后缀时传 prefix/suffix(字符串或 DOM),控件会包进带边框的附加组,聚焦光环由组统一承载",
|
||
"需要限制长度就给 maxCount:超限输入被原生拦截,输入框内侧实时显示 n/上限"
|
||
]
|
||
},
|
||
{
|
||
"id": "inputNumber",
|
||
"title": "数字输入 InputNumber",
|
||
"demo": "sec-inputnumber",
|
||
"props": [
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<number>",
|
||
"r": 1,
|
||
"d": "当前值信号,双向绑定(按钮/键盘写回、外部赋值同步)"
|
||
},
|
||
{
|
||
"n": "min",
|
||
"t": "number",
|
||
"d": "最小值;缺省无下限"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "number",
|
||
"d": "最大值;缺省无上限"
|
||
},
|
||
{
|
||
"n": "step",
|
||
"t": "number",
|
||
"d": "步长;缺省 1",
|
||
"df": "1"
|
||
},
|
||
{
|
||
"n": "precision",
|
||
"t": "number",
|
||
"d": "小数位数:写回与显示都按 toFixed(p) 归一(消除步进浮点尾差,step 0.1 也干净);缺省不处理"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用态:布尔直接置位,传信号则动态同步"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(value: number) => void",
|
||
"k": 1,
|
||
"d": "值变化回调(已夹取到合法区间)"
|
||
},
|
||
{
|
||
"n": "prefix",
|
||
"t": "string | HTMLElement",
|
||
"d": "前置附加块(textField/inputNumber 在输入框内侧前、textarea 在上方);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "suffix",
|
||
"t": "string | HTMLElement",
|
||
"d": "后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "'sm' | 'md' | 'lg'",
|
||
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||
}
|
||
],
|
||
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
|
||
"steps": [
|
||
"用 signal(1) 创建数字信号并传入 value 完成双向绑定",
|
||
"点击 −/+ 按钮或在输入框聚焦时按 ↑↓ 以 step 调节;直接输入在失焦或回车时解析",
|
||
"非法输入自动回滚为当前值;min/max 超界自动夹取,disabled 支持布尔或信号禁用",
|
||
"金额、比例这类小数场景传 precision:值与显示都固定小数位,步进不再出现 0.30000000000000004",
|
||
"需要图标、货币符、单位等前后缀时传 prefix/suffix(字符串或 DOM),控件会包进带边框的附加组,聚焦光环由组统一承载"
|
||
]
|
||
},
|
||
{
|
||
"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": "输入回调"
|
||
},
|
||
{
|
||
"n": "prefix",
|
||
"t": "string | HTMLElement",
|
||
"d": "前置附加块(textField/inputNumber 在输入框内侧前、textarea 在上方);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "suffix",
|
||
"t": "string | HTMLElement",
|
||
"d": "后置附加块(textField/inputNumber 在输入框内侧后、textarea 在下方);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "maxCount",
|
||
"t": "number",
|
||
"d": "字数上限:同时设原生 maxlength,并在下方附加块显示实时计数;缺省不限"
|
||
},
|
||
{
|
||
"n": "autosize",
|
||
"t": "boolean | { minRows?, maxRows? }",
|
||
"d": "自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数"
|
||
}
|
||
],
|
||
"example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })",
|
||
"steps": [
|
||
"创建信号并传入 value",
|
||
"用 rows 控制初始高度,用户可纵向拉伸",
|
||
"字数统计等派生逻辑直接在响应式文本里读 bio().length",
|
||
"需要图标、货币符、单位等前后缀时传 prefix/suffix(字符串或 DOM),控件会包进带边框的附加组,聚焦光环由组统一承载"
|
||
]
|
||
},
|
||
{
|
||
"id": "slider",
|
||
"title": "滑块 Slider",
|
||
"demo": "sec-slider",
|
||
"props": [
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<number>",
|
||
"r": 1,
|
||
"d": "当前值信号,双向绑定:拖动写回,外部赋值同步位置"
|
||
},
|
||
{
|
||
"n": "min",
|
||
"t": "number",
|
||
"d": "最小值;缺省 0",
|
||
"df": "0"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "number",
|
||
"d": "最大值;缺省 100",
|
||
"df": "100"
|
||
},
|
||
{
|
||
"n": "step",
|
||
"t": "number",
|
||
"d": "步长;缺省 1",
|
||
"df": "1"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用态:布尔直接置位,传信号则动态同步"
|
||
},
|
||
{
|
||
"n": "showValue",
|
||
"t": "boolean",
|
||
"d": "是否右侧显示当前值;缺省 true",
|
||
"df": "true"
|
||
},
|
||
{
|
||
"n": "marks",
|
||
"t": "{ value: number; label?: string }[]",
|
||
"d": "刻度标注:按值定位在轨道下方,label 缺省显示数值;纯装饰不进读屏(数值由原生 input 播报)"
|
||
},
|
||
{
|
||
"n": "range",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "双滑块区间模式:value 当下限、end 当上限,两拇指之间用强调色连成已选段"
|
||
},
|
||
{
|
||
"n": "end",
|
||
"t": "Signal<number>",
|
||
"d": "区间上限信号;仅 range 模式使用,不传则组件内部维护"
|
||
},
|
||
{
|
||
"n": "onRangeChange",
|
||
"t": "(low: number, high: number) => void",
|
||
"k": 1,
|
||
"d": "区间两端变更回调,参数已保证 low ≤ high(越界那一端会被压平)"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(value: number) => void",
|
||
"k": 1,
|
||
"d": "拖动回调"
|
||
}
|
||
],
|
||
"example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })",
|
||
"steps": [
|
||
"创建数值信号(初值在 min/max 区间内),传入 value 完成双向绑定",
|
||
"用 min / max / step 限定范围与粒度,如 step: 5 只取 5 的倍数",
|
||
"拖动或按 ←→ 键改变值:原生 range 自带键盘与触屏交互,无需额外处理",
|
||
"只想看拖动条不显示数字时传 showValue: false;需要自定义展示可自行读 signal",
|
||
"禁用场景传 disabled(布尔或信号),置灰且不可拖",
|
||
"需要在轨道下方标刻度(如 0 / 50 / 100 的档位含义)传 marks:[{ value, label }],按 min-max 换算百分比定位,容器宽度变化时自动跟随",
|
||
"要选数值区间就传 range: true 并给 end 信号:两条原生 range 叠在同一轨道,只有拇指可拖;拖下限越过上限会自动压平,回调走 onRangeChange(low, high)"
|
||
]
|
||
},
|
||
{
|
||
"id": "checkbox",
|
||
"title": "复选框 Checkbox",
|
||
"demo": "sec-form",
|
||
"props": [
|
||
{
|
||
"n": "checked",
|
||
"t": "Signal<boolean>",
|
||
"d": "勾选状态信号,双向绑定"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "右侧文案;缺省只渲染勾选框"
|
||
},
|
||
{
|
||
"n": "indeterminate",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "半选态(批量选择时部分子项选中):仅影响视觉与读屏 mixed 播报,不改变 checked 值;点一下即回到全选"
|
||
},
|
||
{
|
||
"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() 判断是否勾选",
|
||
"做全选/子项联动时把「部分选中」折算成 indeterminate(布尔或信号):视觉显示横杠,读屏播报 mixed,点一下即全选"
|
||
]
|
||
},
|
||
{
|
||
"id": "switchButton",
|
||
"title": "开关 Switch",
|
||
"propsType": "SwitchProps",
|
||
"demo": "sec-form",
|
||
"props": [
|
||
{
|
||
"n": "checked",
|
||
"t": "Signal<boolean>",
|
||
"d": "开关状态信号,双向绑定"
|
||
},
|
||
{
|
||
"n": "ariaLabel",
|
||
"t": "string",
|
||
"d": "无可见文案时的可访问名(如纯图标开关);缺省取 label"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "左侧文案"
|
||
},
|
||
{
|
||
"n": "loading",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "加载中:锁定交互、滑块换成转圈并挂 aria-busy(异步提交防误点);与 disabled 同时生效"
|
||
},
|
||
{
|
||
"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 里做副作用(如切换主题)",
|
||
"切换要等异步请求就传 loading(布尔或信号):请求期间滑块转圈、不可点,完成后自动恢复"
|
||
]
|
||
},
|
||
{
|
||
"id": "select",
|
||
"title": "下拉选择 Select(自绘弹层)",
|
||
"demo": "sec-form",
|
||
"props": [
|
||
{
|
||
"n": "options",
|
||
"t": "OptionItem[]",
|
||
"r": 1,
|
||
"d": "选项列表,每项 { value, label };每项可加 group 指定分组小标题"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"d": "选中值信号,双向绑定"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "未选中时触发器显示的占位文案"
|
||
},
|
||
{
|
||
"n": "searchable",
|
||
"t": "boolean",
|
||
"d": "弹层顶部带搜索框,按标签包含匹配过滤;缺省 false"
|
||
},
|
||
{
|
||
"n": "clearable",
|
||
"t": "boolean",
|
||
"d": "有值时触发器右缘出现清空按钮(收起态按 Delete / Backspace 也能清);缺省 false,用于「可选筛选项」"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用态:布尔直接置位,传信号则动态同步;禁用时触发器置灰且不展开弹层"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(value: string) => void",
|
||
"k": 1,
|
||
"d": "选中回调"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "'sm' | 'md' | 'lg'",
|
||
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||
}
|
||
],
|
||
"example": "const city = signal(''); select({ options: [{ value: 'bj', label: '北京' }], value: city, placeholder: '选择城市…' })",
|
||
"steps": [
|
||
"准备 options 数组,value 是提交用值、label 是展示文案",
|
||
"创建信号并传入 value;未传时组件内部自持状态",
|
||
"点击触发器展开弹层,点选项写回信号并收起;点外部或按 Esc 也会收起",
|
||
"选中项在弹层中用淡色弱化标记,不抢视觉焦点",
|
||
"弹层展开时按 Esc 只收起下拉并回焦触发器;若下拉在弹窗/抽屉内,再按一次 Esc 才关外层(逐层关闭)",
|
||
"searchable: true 时打开即聚焦搜索框,输入实时过滤;↑↓/Home/End 只在可见项间移动,无命中显示「无匹配选项」",
|
||
"需要允许「不选」的筛选项就开 clearable:有值时右缘出现 ×,点它把值写成空串并回调 onChange(''),键盘在收起态按 Delete / Backspace 等价",
|
||
"候选多时给选项加 group:相邻同组合成一段、组首插灰色小标题(段间有分隔线),过滤后整组无命中时标题一起隐藏——搜索路径与键盘 ↑↓ 仍只走真实选项"
|
||
]
|
||
},
|
||
{
|
||
"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?, shortcut? }"
|
||
},
|
||
{
|
||
"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 后焦点回到触发器)",
|
||
"条目要给快捷键提示时传 shortcut(如 '⌘K'):右侧灰字展示,组件不绑定按键,实际快捷键由调用方在全局监听",
|
||
"键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过",
|
||
"靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true",
|
||
"展开时按 Esc 只收起菜单并回焦触发器,不会连带关闭所在弹窗(逐层关闭)"
|
||
]
|
||
},
|
||
{
|
||
"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 条目的点击回调"
|
||
},
|
||
{
|
||
"n": "maxItems",
|
||
"t": "number",
|
||
"d": "最多展示几项:超出时首项之后折叠成「…」按钮(aria-label 报出折叠条数),点开即完整展开;缺省不折叠"
|
||
}
|
||
],
|
||
"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,可直接放进页头或卡片标题栏",
|
||
"路径过深时传 maxItems(如 4):首项 + 「…」+ 末几项,点「…」原地展开全部,末项始终是当前页"
|
||
]
|
||
},
|
||
{
|
||
"id": "card",
|
||
"title": "卡片 Card",
|
||
"demo": "sec-card",
|
||
"props": [
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"d": "标题;缺省不渲染标题行"
|
||
},
|
||
{
|
||
"n": "...children",
|
||
"t": "unknown[]",
|
||
"d": "任意子节点:字符串 / DOM / 响应式函数"
|
||
},
|
||
{
|
||
"n": "extra",
|
||
"t": "unknown",
|
||
"d": "标题行右侧操作区(按钮 / 统计文案);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "footer",
|
||
"t": "unknown",
|
||
"d": "底部操作区;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "bordered",
|
||
"t": "boolean",
|
||
"d": "是否显示边框;缺省 true"
|
||
},
|
||
{
|
||
"n": "hoverable",
|
||
"t": "boolean",
|
||
"d": "悬停浮起(可点击卡片);缺省 false"
|
||
}
|
||
],
|
||
"example": "card({ title: '用户表' }, dataTable({ data, columns }))",
|
||
"steps": [
|
||
"card 是容器组件:第一个参数是 props,其余参数是任意子节点",
|
||
"传 title 显示标题行,否则只有内容区",
|
||
"子节点可以混合字符串、DOM 节点与响应式函数",
|
||
"title 与 extra 同处标题行:有 extra 时右侧对齐,没标题也能只放操作区",
|
||
"footer 渲染在内容之后的底部区,右对齐并带上分隔线,适合放主次按钮",
|
||
"bordered:false 去边框(嵌在已有边界的容器里),hoverable 让整卡悬停浮起(自带 reduced-motion 兜底)"
|
||
]
|
||
},
|
||
{
|
||
"id": "badge",
|
||
"title": "徽标 Badge",
|
||
"demo": "sec-feedback",
|
||
"props": [
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "徽标文案;与 count / dot 三选一,dot 时作可访问名"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'default' | 'success' | 'warning' | 'danger'",
|
||
"d": "语义色;缺省 default"
|
||
},
|
||
{
|
||
"n": "count",
|
||
"t": "number",
|
||
"d": "数字角标:超过 max 显示 N+,负数归零"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "number",
|
||
"d": "数字上限;缺省 99"
|
||
},
|
||
{
|
||
"n": "dot",
|
||
"t": "boolean",
|
||
"d": "只显示小红点(无文字),role=img + label 作名称"
|
||
}
|
||
],
|
||
"example": "badge({ label: '成功', variant: 'success' })",
|
||
"steps": [
|
||
"纯状态文案直接传 label,配 variant 定语义色",
|
||
"要显示数量传 count(如未读数),超过 max(缺省 99)自动渲染成 99+",
|
||
"只提示「有更新」用 dot: true,配合 label 给出读屏名(如「有未读消息」)"
|
||
]
|
||
},
|
||
{
|
||
"id": "tag",
|
||
"title": "标签 Tag",
|
||
"demo": "sec-tag",
|
||
"props": [
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "标签文案"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'default' | 'success' | 'warning' | 'danger' | 'info'",
|
||
"d": "语义色;缺省 default",
|
||
"df": "default"
|
||
},
|
||
{
|
||
"n": "closable",
|
||
"t": "boolean",
|
||
"d": "显示 × 关闭按钮;缺省 false(纯展示)",
|
||
"df": "false"
|
||
},
|
||
{
|
||
"n": "onClose",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "关闭回调:调用方从数据源移除该项后重新渲染"
|
||
},
|
||
{
|
||
"n": "checkable",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "可选中:渲染为筛选片(button + aria-pressed);与 closable 互斥,同时传以 checkable 为准"
|
||
},
|
||
{
|
||
"n": "checked",
|
||
"t": "Signal<boolean>",
|
||
"d": "选中态信号,双向绑定(点击写回、外部赋值同步);不传则组件内部自持"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(checked: boolean) => void",
|
||
"d": "选中态变化回调,参数是点击后的新值"
|
||
}
|
||
],
|
||
"example": "tag({ label: '前端', variant: 'info', closable: true, onClose: () => remove('前端') })",
|
||
"steps": [
|
||
"纯展示用 badge,需要可移除的标签(筛选项 / 已选条件)用 tag",
|
||
"按语义选 variant:info 中性标记、success 通过、warning 待处理、danger 已失效",
|
||
"closable: true 时必须配 onClose:把数据源里的该项删掉再重渲染(组件无状态,不自行摘除节点)",
|
||
"组合用法:容器里用 renderList 按信号数组渲染 tag 列表,删除即写回信号",
|
||
"标签是内联元素,可直接排进 .row 等容器,自动换行由容器决定",
|
||
"需要可点的筛选片时传 checkable + checked:点击写回信号,视觉与 aria-pressed 同步,不做整块重渲染(焦点不丢)"
|
||
]
|
||
},
|
||
{
|
||
"id": "avatar",
|
||
"title": "头像 Avatar",
|
||
"demo": "sec-avatar",
|
||
"props": [
|
||
{
|
||
"n": "name",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "名称:生成占位首字,并作为图片 alt 与悬停标题"
|
||
},
|
||
{
|
||
"n": "src",
|
||
"t": "string",
|
||
"d": "图片地址;缺省用首字占位"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "number",
|
||
"d": "直径像素;缺省 32",
|
||
"df": "32"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'default' | 'success' | 'warning' | 'danger'",
|
||
"d": "占位底色语义档;缺省 default",
|
||
"df": "default"
|
||
}
|
||
],
|
||
"example": "avatar({ name: '张三' }); avatar({ name: 'Alice', size: 40, variant: 'success' })",
|
||
"steps": [
|
||
"只传 name:用名称首字做占位(中文取首字、英文大写),底色走 accent 淡化,暗色自动适配",
|
||
"有图片就传 src:渲染 img 并用 name 作 alt,读屏语义完整;图片缺失时由浏览器 alt 兜底",
|
||
"尺寸用 size(像素)控制,头像不参与布局拉伸(flex:none),可安全排进 .row 与表格单元格",
|
||
"需要区分类别(在线/告警)时用 variant 换底色档,颜色一律来自令牌"
|
||
]
|
||
},
|
||
{
|
||
"id": "alert",
|
||
"title": "提示条 Alert",
|
||
"demo": "sec-feedback",
|
||
"props": [
|
||
{
|
||
"n": "message",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "提示内容"
|
||
},
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"d": "加粗标题行;缺省只渲染正文"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'info' | 'success' | 'warning' | 'danger'",
|
||
"d": "语义色;缺省 info",
|
||
"df": "info"
|
||
},
|
||
{
|
||
"n": "closable",
|
||
"t": "boolean",
|
||
"d": "显示 × 关闭按钮,退场后从 DOM 移除;缺省 false",
|
||
"df": "false"
|
||
},
|
||
{
|
||
"n": "onClose",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "关闭回调,供调用方同步业务状态"
|
||
},
|
||
{
|
||
"n": "banner",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "通栏形态:去圆角、只保留下边框、左右撑满,适合页面顶部通知"
|
||
},
|
||
{
|
||
"n": "showIcon",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "显示语义图标(info ℹ / success ✓ / warning ! / danger ✕),纯装饰不进读屏"
|
||
},
|
||
{
|
||
"n": "icon",
|
||
"t": "string | HTMLElement",
|
||
"d": "自定义图标节点,传了即覆盖语义图标"
|
||
}
|
||
],
|
||
"example": "alert({ message: '操作可能存在风险', variant: 'warning' })",
|
||
"steps": [
|
||
"按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误",
|
||
"传入 message 即可,入场自带下滑动画",
|
||
"需要可关闭或带操作按钮时,外层用 h() 组合 button 自行拼装,alert 只负责语义底色",
|
||
"需要用户可自行消除的提示传 closable: true:点 × 淡出并从 DOM 移除,配合 onClose 同步业务状态",
|
||
"有标题与正文两层信息时传 title(加粗首行),正文仍走 message",
|
||
"banner: true 出通栏形态(圆角与左右边框归零),页面顶部通知直接铺满容器即可,不需要额外包裹层"
|
||
]
|
||
},
|
||
{
|
||
"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": "closable",
|
||
"t": "boolean",
|
||
"d": "显示关闭按钮,点 × 立即收起;缺省 false(仅可关闭的 toast 接收指针事件,其余保持穿透)",
|
||
"df": "false"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'info' | 'success' | 'warning' | 'danger'",
|
||
"d": "语义色;缺省 info(toast.error 对应 danger)",
|
||
"df": "info"
|
||
},
|
||
{
|
||
"n": "actionLabel",
|
||
"t": "string",
|
||
"d": "操作按钮文案(如「撤销」);传了才渲染,点后 toast 立即收起"
|
||
},
|
||
{
|
||
"n": "onAction",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "操作按钮回调,在 toast 收起之后触发(撤销删除之类)"
|
||
}
|
||
],
|
||
"example": "toast.success('保存成功'); toast.error('网络请求失败', 5000)",
|
||
"steps": [
|
||
"直接调用语义工厂:toast.info / success / warning / error(message, duration?),无需先建容器",
|
||
"不传 duration 时默认停留 3 秒后自动淡出并移除节点",
|
||
"多条消息在右上角自动纵向堆叠,互不遮挡",
|
||
"需要完整入参(含 variant)时用 showToast(props);组件挂载容器由内部懒创建,样式随 ui.css 一起引入",
|
||
"同屏最多 4 条,超出自动挤掉最早一条;需要用户主动确认的消息用 showToast({ closable: true }) 给个 ×",
|
||
"要「撤销」这类操作时给 showToast 传 actionLabel 与 onAction:按钮出现在文案右侧,点完先收起再回调,duration 建议放到 5000 让用户来得及点"
|
||
]
|
||
},
|
||
{
|
||
"id": "tooltip",
|
||
"title": "悬浮提示 Tooltip",
|
||
"demo": "sec-tooltip",
|
||
"props": [
|
||
{
|
||
"n": "content",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "提示文案(纯文本)"
|
||
},
|
||
{
|
||
"n": "placement",
|
||
"t": "'top' | 'bottom' | 'left' | 'right'",
|
||
"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": [
|
||
"把提示要挂在的元素作为第二个参数传入,返回的包裹节点替换原位置(气泡绝对定位,不占布局)",
|
||
"鼠标悬停或键盘聚焦后延迟 delay 毫秒出场,移出 / 失焦 / Esc 收起;空间不足时自动翻到另一侧",
|
||
"触摸屏没有 hover:手指按下即出场,抬手后停留 1.2 秒再收起,免得手指正挡着就消失",
|
||
"气泡设了 pointer-events: none,不会挡住下面的点击;aria-describedby 已关联,读屏能念出提示文案",
|
||
"方位支持上下左右四向:贴边或工具栏竖排时用 left / right,出场时按该方向剩余空间自动翻到对侧"
|
||
]
|
||
},
|
||
{
|
||
"id": "popover",
|
||
"title": "气泡卡片 Popover",
|
||
"demo": "sec-popover",
|
||
"props": [
|
||
{
|
||
"n": "content",
|
||
"t": "unknown",
|
||
"r": 1,
|
||
"d": "弹层内容:字符串 / DOM 节点 / 响应式函数,可放按钮等交互元素"
|
||
},
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"d": "弹层标题;缺省不渲染标题行"
|
||
},
|
||
{
|
||
"n": "placement",
|
||
"t": "'top' | 'bottom'",
|
||
"d": "弹层方位;缺省 bottom(触发器下方)",
|
||
"df": "bottom"
|
||
},
|
||
{
|
||
"n": "trigger",
|
||
"t": "'click' | 'hover'",
|
||
"d": "触发方式;缺省 click",
|
||
"df": "click"
|
||
},
|
||
{
|
||
"n": "delay",
|
||
"t": "number",
|
||
"d": "hover 模式出场延迟毫秒;缺省 150",
|
||
"df": "150"
|
||
},
|
||
{
|
||
"n": "onOpenChange",
|
||
"t": "(open: boolean) => void",
|
||
"k": 1,
|
||
"d": "开合回调"
|
||
},
|
||
{
|
||
"n": "triggerEl(第二个参数)",
|
||
"t": "HTMLElement",
|
||
"r": 1,
|
||
"d": "第二个参数:被包裹的触发器元素"
|
||
},
|
||
{
|
||
"n": "open",
|
||
"t": "Signal<boolean>",
|
||
"d": "受控开合信号;不传则内部维护,传了外部可编程开合(popconfirm 即基于它)"
|
||
}
|
||
],
|
||
"example": "popover({ title: '筛选条件', content: h('div', null, '任意可交互内容') }, button({ label: '筛选' }))",
|
||
"steps": [
|
||
"创建触发器元素,再用 popover(props, trigger) 包一层:返回带弹层的包裹元素",
|
||
"点击触发器开合;点弹层外部或按 Esc 收起(Esc 后焦点回触发器)",
|
||
"content 里可以放 button / checkbox 等交互元素(tooltip 只能纯文本),点击弹层内部不会误关",
|
||
"hover 场景传 trigger: 'hover' + delay 控制多快出场;placement: 'top' 让弹层往上翻",
|
||
"需要感知开合做副作用(如懒加载数据)时用 onOpenChange",
|
||
"展开时按 Esc 只收起气泡并回焦触发器,不会连带关闭所在弹窗(逐层关闭)"
|
||
]
|
||
},
|
||
{
|
||
"id": "spinner",
|
||
"title": "加载指示器 Spinner",
|
||
"demo": "sec-feedback",
|
||
"props": [
|
||
{
|
||
"n": "size",
|
||
"t": "number",
|
||
"d": "直径像素;缺省 18",
|
||
"df": "18"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "读屏播报文案,如「正在加载订单」;缺省「加载中」"
|
||
}
|
||
],
|
||
"example": "spinner(); spinner({ size: 26 })",
|
||
"steps": [
|
||
"数据加载时插入 spinner(),加载完成移除",
|
||
"用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)",
|
||
"与 skeleton 分工:已知内容结构用 skeleton 占位,只有『在跑』的短等待用 spinner",
|
||
"区块级加载给 spinner 传 label 说明在加载什么,读屏才不会只念一句「加载中」"
|
||
]
|
||
},
|
||
{
|
||
"id": "progress",
|
||
"title": "进度条 Progress",
|
||
"demo": "sec-feedback",
|
||
"props": [
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<number>",
|
||
"d": "当前进度 0-100 的信号;不传即不确定态(未知时长的加载)"
|
||
},
|
||
{
|
||
"n": "type",
|
||
"t": "'line' | 'circle'",
|
||
"df": "line",
|
||
"d": "形态:线性条或环形;环形需同时传 value(不确定态仍走线性条)"
|
||
},
|
||
{
|
||
"n": "showText",
|
||
"t": "boolean",
|
||
"d": "是否右侧显示百分比;缺省 true(不确定态忽略)"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "可访问名,如「上传进度」;缺省不确定态念「进行中」"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'default' | 'success' | 'warning' | 'danger'",
|
||
"d": "填充语义色;缺省 default(满格自动切 success)"
|
||
}
|
||
],
|
||
"example": "const pct = signal(0); progress({ value: pct }); setInterval(() => pct((pct() + 5) % 105), 400)",
|
||
"steps": [
|
||
"传 value 信号给定量进度,不传则退化为往复的不确定态",
|
||
"label 写清这是什么进度,读屏会把名称与百分比一起念出来",
|
||
"失败或告警用 variant: 'danger' / 'warning',该颜色在满格时也不被 success 覆盖",
|
||
"看板 / 上传卡片要环形进度就传 type: 'circle'(同时必须传 value):SVG 双圆按 dashoffset 出弧,中间自动显示百分比,语义色同 variant"
|
||
]
|
||
},
|
||
{
|
||
"id": "tabs",
|
||
"title": "标签页 Tabs",
|
||
"demo": "sec-tabs-modal",
|
||
"props": [
|
||
{
|
||
"n": "tabs",
|
||
"t": "TabItem[]",
|
||
"r": 1,
|
||
"d": "页签列表,每项 { value, label, disabled?, closable? }"
|
||
},
|
||
{
|
||
"n": "active",
|
||
"t": "Signal<string>",
|
||
"d": "当前激活页签 value 信号,双向绑定"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(value: string) => void",
|
||
"k": 1,
|
||
"d": "切换回调"
|
||
},
|
||
{
|
||
"n": "onClose",
|
||
"t": "(value: string) => void",
|
||
"k": 1,
|
||
"d": "关闭页签回调:组件不自行移除,凭 value 从数据源里摘掉后重绘"
|
||
},
|
||
{
|
||
"n": "extra",
|
||
"t": "unknown",
|
||
"d": "页签栏右侧操作区内容(按钮 / 链接):与页签同一行、共用一条分隔线;缺省不渲染且页签栏结构不变"
|
||
},
|
||
{
|
||
"n": "closable",
|
||
"t": "boolean",
|
||
"d": "页签项上的开关:true 时渲染关闭按钮(×),关闭按钮不进 Tab 序但可用鼠标点;缺省不可关"
|
||
}
|
||
],
|
||
"example": "const tab = signal('a'); tabs({ tabs: [{ value: 'a', label: '概览', closable: true }, { value: 'b', label: '明细', disabled: true }], active: tab, onClose: (v) => toast.info(`关闭 ${v}`) })",
|
||
"steps": [
|
||
"创建信号 tab = signal('a'),初值为默认页签",
|
||
"tabs 只渲染页签头;页签内容用响应式函数按信号切换,如 () => tab() === 'a' ? panelA : panelB",
|
||
"点击或方向键(←→ 环形)、Home/End(直达首/末页签)切换,滑动指示条自动跟随;disabled: true 的页签置灰不可点",
|
||
"需要可关闭页签时给该项加 closable: true,× 或聚焦后按 Delete 触发 onClose(value);组件不改数据源,调用方从数组里摘掉该项再重绘",
|
||
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动,Home/End 跳首个/末个启用页签",
|
||
"页签栏右侧要放「新建 / 设置」这类操作就传 extra(按钮或链接):它与页签同一行共用一条分隔线,不传时 DOM 结构保持不变"
|
||
]
|
||
},
|
||
{
|
||
"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": "direction",
|
||
"t": "'horizontal' | 'vertical'",
|
||
"df": "'horizontal'",
|
||
"d": "排列方向:horizontal 横排连接线在节点之间;vertical 竖排、连接线落在圆点下方,已走完的一段用主色"
|
||
},
|
||
{
|
||
"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 同步业务状态",
|
||
"侧栏或移动端传 direction: 'vertical':圆点成一列、说明文字紧贴右侧,步与步之间由圆点下方的竖线相连(末步不画线,已完成的段用主色标进度)"
|
||
]
|
||
},
|
||
{
|
||
"id": "rate",
|
||
"title": "评分 Rate",
|
||
"demo": "sec-rate",
|
||
"props": [
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<number>",
|
||
"r": 1,
|
||
"d": "当前评分(1 基,allowHalf 时 0.5 步进)信号,双向绑定:点选/键盘写回、外部赋值同步"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "number",
|
||
"d": "星标总数;缺省 5",
|
||
"df": "5"
|
||
},
|
||
{
|
||
"n": "allowHalf",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "允许半星:点击星标左半区记 0.5 分,键盘步进同为 0.5;缺省整数"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用态:布尔直接置位,传信号则动态同步"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(value: number) => void",
|
||
"k": 1,
|
||
"d": "评分变化回调"
|
||
}
|
||
],
|
||
"example": "const score = signal(0); rate({ value: score, max: 5, onChange: (v) => console.log(v) })",
|
||
"steps": [
|
||
"用 signal(0) 创建评分信号并传入 value;0 表示未评分",
|
||
"点击星标或聚焦后按 →/↑ 加一分、←/↓ 减一分;悬停高亮预览、移出还原",
|
||
"max 可调整星标总数;disabled 支持布尔或信号禁用",
|
||
"要支持半星就传 allowHalf:点星标左半区记 0.5 分,悬停位置实时预览半格填充"
|
||
]
|
||
},
|
||
{
|
||
"id": "empty",
|
||
"title": "空状态 Empty",
|
||
"demo": "sec-empty",
|
||
"props": [
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"d": "主标题;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "description",
|
||
"t": "string",
|
||
"d": "补充说明;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "icon",
|
||
"t": "unknown",
|
||
"d": "图标区内容(字符串或图标 DOM);缺省渲染 ⌀ 占位符号,装饰性元素不进无障碍树"
|
||
},
|
||
{
|
||
"n": "action",
|
||
"t": "unknown",
|
||
"d": "操作区内容(常见为按钮);缺省不渲染"
|
||
}
|
||
],
|
||
"example": "empty({ title: '暂无数据', description: '调整筛选条件后再试', action: button({ label: '重置筛选', variant: 'ghost' }) })",
|
||
"steps": [
|
||
"在列表 / 表格 / 筛选结果为空时渲染 empty 占位",
|
||
"title 一句话说明状态,description 补充原因或建议",
|
||
"action 传入 DOM(如 ghost 按钮)引导用户下一步操作",
|
||
"要换成自家插图就传 icon(字符串或 SVG 节点):它固定 aria-hidden,文案仍由 title / description 承担"
|
||
]
|
||
},
|
||
{
|
||
"id": "divider",
|
||
"title": "分隔线 Divider",
|
||
"demo": "sec-divider",
|
||
"props": [
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "居中文案标签;缺省纯线条"
|
||
},
|
||
{
|
||
"n": "vertical",
|
||
"t": "boolean",
|
||
"d": "是否垂直分隔线(用于行内工具栏);缺省水平"
|
||
},
|
||
{
|
||
"n": "dashed",
|
||
"t": "boolean",
|
||
"d": "是否虚线;缺省实线"
|
||
}
|
||
],
|
||
"example": "divider({ label: '更多信息' }) / divider({ vertical: true })",
|
||
"steps": [
|
||
"在区块之间插入 divider 划分内容层次",
|
||
"传 label 在水平线上显示居中文案,如「更多信息」",
|
||
"工具栏内用 vertical: true 的行内竖线,dashed: true 切虚线弱化分隔感"
|
||
]
|
||
},
|
||
{
|
||
"id": "space",
|
||
"title": "间距容器 Space",
|
||
"demo": "sec-space",
|
||
"props": [
|
||
{
|
||
"n": "direction",
|
||
"t": "'horizontal' | 'vertical'",
|
||
"d": "主轴方向;缺省 horizontal 横排"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "'sm' | 'md' | 'lg' | number",
|
||
"d": "子元素间距:三档走 --ua-space-* 令牌,数字按像素;缺省 md"
|
||
},
|
||
{
|
||
"n": "align",
|
||
"t": "'start' | 'center' | 'end' | 'baseline'",
|
||
"d": "交叉轴对齐;缺省 center,按钮与文字混排不错位"
|
||
},
|
||
{
|
||
"n": "justify",
|
||
"t": "'start' | 'center' | 'end' | 'between'",
|
||
"d": "主轴分布;缺省 start,between 两端撑开"
|
||
},
|
||
{
|
||
"n": "wrap",
|
||
"t": "boolean",
|
||
"d": "空间不足是否换行;缺省 false"
|
||
},
|
||
{
|
||
"n": "block",
|
||
"t": "boolean",
|
||
"d": "是否占满父容器宽度;缺省按内容收缩"
|
||
},
|
||
{
|
||
"n": "split",
|
||
"t": "string",
|
||
"d": "子元素之间的分隔符(如 · / |):纯装饰、aria-hidden,仅 2 个以上子元素时插入"
|
||
}
|
||
],
|
||
"example": "space({ size: 'lg' }, button({ label: '保存' }), button({ label: '取消', variant: 'ghost' })) / space({ direction: 'vertical', align: 'start' }, ...)",
|
||
"steps": [
|
||
"把一排需要统一间距的元素交给 space 托管,不再各自写 margin",
|
||
"调间距:size 传 sm/md/lg 走令牌,需要精确像素时直接给数字",
|
||
"竖排用 direction: 'vertical',配 align: 'start' 让子元素左对齐",
|
||
"工具栏两端撑开用 justify: 'between' + block: true;窄屏放不下时加 wrap",
|
||
"一行里要塞分隔符(面包屑式「A · B · C」)传 split: '·':分隔符由容器插入且读屏跳过,不要手写 span 再逐个调 margin"
|
||
]
|
||
},
|
||
{
|
||
"id": "pagination",
|
||
"title": "分页器 Pagination",
|
||
"demo": "sec-pagination",
|
||
"props": [
|
||
{
|
||
"n": "page",
|
||
"t": "Signal<number>",
|
||
"r": 1,
|
||
"d": "当前页码信号,双向绑定:外部赋值自动翻页,点击页码写回信号"
|
||
},
|
||
{
|
||
"n": "pageSize",
|
||
"t": "number",
|
||
"r": 1,
|
||
"d": "每页条数"
|
||
},
|
||
{
|
||
"n": "total",
|
||
"t": "number",
|
||
"r": 1,
|
||
"d": "总条数"
|
||
},
|
||
{
|
||
"n": "simple",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "极简模式:只留上一页 / 下一页与「当前 / 总页」文字,适合表格工具栏等窄位"
|
||
},
|
||
{
|
||
"n": "hideOnSinglePage",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "只有一页时整个分页器隐藏(不占布局位)"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(page: number) => void",
|
||
"k": 1,
|
||
"d": "翻页回调"
|
||
},
|
||
{
|
||
"n": "jumpable",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "末尾显示跳页输入框(总页数 > 1 才出现);缺省 false"
|
||
},
|
||
{
|
||
"n": "pageSizes",
|
||
"t": "number[]",
|
||
"d": "每页条数候选(如 [10, 20, 50]);传了才渲染条数切换器"
|
||
},
|
||
{
|
||
"n": "onPageSizeChange",
|
||
"t": "(size: number) => void",
|
||
"k": 1,
|
||
"d": "切换每页条数回调,同时自动回到第 1 页"
|
||
}
|
||
],
|
||
"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 触发",
|
||
"pageSizes 传候选数组即出现「N 条/页」切换器,切换后自动回第 1 页;onPageSizeChange 拿新条数重算数据",
|
||
"空间紧的地方传 simple:页码区换成「3 / 5」文字 + 两个箭头(文字带 aria-live,翻页会被读屏播报);数据少到只有一页时再传 hideOnSinglePage,整块自动消失而不是留个灰掉的 1"
|
||
]
|
||
},
|
||
{
|
||
"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": "size",
|
||
"t": "'sm' | 'md' | 'lg'",
|
||
"d": "宽度档位:sm 340px / md 420px / lg 640px;缺省 md",
|
||
"df": "md"
|
||
},
|
||
{
|
||
"n": "footer",
|
||
"t": "unknown",
|
||
"d": "底部操作区内容(常见为按钮组,右对齐);缺省不渲染;给了它就用自定义页脚,内置按钮组不再渲染"
|
||
},
|
||
{
|
||
"n": "confirmText",
|
||
"t": "string",
|
||
"d": "内置页脚:确认按钮文案;缺省「确定」(不传 footer 且有 onConfirm 时也渲染内置页脚)"
|
||
},
|
||
{
|
||
"n": "cancelText",
|
||
"t": "string",
|
||
"d": "内置页脚:取消按钮文案;缺省「取消」,点击走 close 流程"
|
||
},
|
||
{
|
||
"n": "onConfirm",
|
||
"t": "() => void | Promise<void>",
|
||
"k": 1,
|
||
"d": "内置页脚:确认回调;返回 Promise 时按钮变「处理中…」并禁用,成功自动收窗、失败留在窗口重试"
|
||
},
|
||
{
|
||
"n": "closeOnOverlay",
|
||
"t": "boolean",
|
||
"d": "点遮罩是否关闭弹窗;缺省 true,表单类可传 false 防误关(× 与 Esc 仍可用)"
|
||
},
|
||
{
|
||
"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),点遮罩、× 或 Esc 会自动写回 false;多弹层叠加时 Esc 只关最上层(后开先关)",
|
||
"入场自带遮罩淡入 + 弹窗上浮动画",
|
||
"底部操作按钮放进 footer(如取消/确定),需要更宽内容用 size: 'lg',窄确认框用 'sm'",
|
||
"打开后 Tab 焦点锁在弹窗内,关闭自动把焦点还给打开前的元素",
|
||
"普通确认框不必手拼 footer:传 confirmText / onConfirm 即得「取消 + 确认」按钮组;onConfirm 写成 async,组件会在等待期间把按钮置灰改文案,resolve 后自己收窗、reject 时保持打开供重试"
|
||
]
|
||
},
|
||
{
|
||
"id": "drawer",
|
||
"title": "抽屉 Drawer",
|
||
"demo": "sec-drawer",
|
||
"props": [
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "标题"
|
||
},
|
||
{
|
||
"n": "open",
|
||
"t": "Signal<boolean>",
|
||
"r": 1,
|
||
"d": "开关信号:true 滑出 / false 收起"
|
||
},
|
||
{
|
||
"n": "placement",
|
||
"t": "'left' | 'right' | 'top' | 'bottom'",
|
||
"d": "滑出方向;缺省 right",
|
||
"df": "right"
|
||
},
|
||
{
|
||
"n": "width",
|
||
"t": "number",
|
||
"d": "面板尺寸(像素):左右滑出为宽度,上下滑出为高度;缺省 320",
|
||
"df": "320"
|
||
},
|
||
{
|
||
"n": "footer",
|
||
"t": "unknown",
|
||
"d": "底部操作区内容;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "closeOnOverlay",
|
||
"t": "boolean",
|
||
"d": "点遮罩是否关闭抽屉;缺省 true(× 与 Esc 始终可用)"
|
||
},
|
||
{
|
||
"n": "modal",
|
||
"t": "boolean",
|
||
"d": "是否模态:false 时无遮罩、不锁背景滚动、不困焦点,页面仍可操作(检查器 / 详情面板);缺省 true"
|
||
},
|
||
{
|
||
"n": "onClose",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "关闭回调(点遮罩 / × / Esc 触发)"
|
||
},
|
||
{
|
||
"n": "...children",
|
||
"t": "unknown[]",
|
||
"d": "面板内容子节点"
|
||
}
|
||
],
|
||
"example": "const open = signal(false); drawer({ title: '详情', open, width: 360, onClose: () => open(false) }, h('p', null, '内容'))",
|
||
"steps": [
|
||
"创建布尔信号 open = signal(false),传给 drawer;其余参数为面板内容子节点",
|
||
"打开后从侧边滑出:placement: 'right'(默认)或 'left',width 控制宽度(窄屏自动收到 92vw)",
|
||
"点遮罩、× 或按 Esc 关闭并写回 false;打开时焦点落在面板首个可交互元素,Esc 只关最上层弹层",
|
||
"底部操作按钮放进 footer;与 modal 的分工:modal 是强打断的确认,drawer 保留主视图上下文做详情/编辑",
|
||
"面板内容与 modal 一样接受任意 DOM 与响应式函数",
|
||
"要「一边看一边改」就把 modal 设成 false:遮罩透明且不吃点击、背景滚动不锁、Tab 不被困在面板里,× 与 Esc 仍能关"
|
||
]
|
||
},
|
||
{
|
||
"id": "form",
|
||
"title": "表单 Form / 表单字段 FormItem",
|
||
"demo": "sec-formlab",
|
||
"props": [
|
||
{
|
||
"n": "layout",
|
||
"t": "'vertical' | 'horizontal'",
|
||
"d": "布局方向;缺省 vertical(horizontal 为标签左置)",
|
||
"df": "vertical"
|
||
},
|
||
{
|
||
"n": "columns",
|
||
"t": "1 | 2 | 3",
|
||
"d": "字段列数:>1 时字段走栅格铺列(沿用 grid 的窄屏单列),按钮区始终整行;缺省 1",
|
||
"df": "1"
|
||
},
|
||
{
|
||
"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"
|
||
},
|
||
{
|
||
"n": "(formItem)hint",
|
||
"t": "string",
|
||
"d": "控件下方辅助说明(格式提示等);报错时让位给错误文案"
|
||
},
|
||
{
|
||
"n": "(formItem)span",
|
||
"t": "number",
|
||
"d": "多列表单里本字段占几列(如整宽的多行备注);单列布局或窄屏下自动退回整行"
|
||
},
|
||
{
|
||
"n": "onReset",
|
||
"t": "() => void",
|
||
"d": "重置按钮点击回调;组件不自行清空字段,由调用方重置信号;不传 resetText 不渲染按钮"
|
||
},
|
||
{
|
||
"n": "onError",
|
||
"t": "(count: number) => void",
|
||
"k": 1,
|
||
"d": "校验不通过时的回调,参数为不通字段数(适合埋点或顶部提示)"
|
||
},
|
||
{
|
||
"n": "errorSummary",
|
||
"t": "boolean",
|
||
"d": "是否在表单顶部显示「有 N 项需要修改」汇总条;缺省 true,自带错误 UI 时传 false"
|
||
}
|
||
],
|
||
"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(已拦截走校验流程)",
|
||
"错误态覆盖包裹式控件:标红作用于控件根与内部原生控件(含自绘 select 的触发器),并同步 aria-invalid,读屏与视觉一致",
|
||
"触发时机用 input / change / focusout(均会冒泡),因此 checkbox / select / 单选组这类包裹式控件同样能失焦校验",
|
||
"单行输入框内按 Enter 等同点击提交(textarea 内仍是换行);提交按钮为 type=\"submit\",不会与点击回调重复触发",
|
||
"rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖",
|
||
"formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交",
|
||
"需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行",
|
||
"字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)",
|
||
"多列表单里想整宽就传 span:formItem({ span: 2 }, textarea(...)) 会占满两列,窄屏单列时自动忽略跨列(否则会撑出横向滚动)"
|
||
]
|
||
},
|
||
{
|
||
"id": "dataTable",
|
||
"title": "数据表格 DataTable",
|
||
"propsType": "DataTableProps",
|
||
"demo": "sec-table",
|
||
"props": [
|
||
{
|
||
"n": "data",
|
||
"t": "T[]",
|
||
"r": 1,
|
||
"d": "全量行数据,每行须含 id 字段作 rowKey"
|
||
},
|
||
{
|
||
"n": "columns",
|
||
"t": "ColumnDef<T>[]",
|
||
"r": 1,
|
||
"d": "列定义:内核字段 { key, header?, sortable?, width? } + 皮肤字段 { align?, format? };width 数字为固定像素,'flex' 自适应"
|
||
},
|
||
{
|
||
"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"
|
||
},
|
||
{
|
||
"n": "selected",
|
||
"t": "Signal<RowKey[]>",
|
||
"d": "受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写"
|
||
},
|
||
{
|
||
"n": "exportable",
|
||
"t": "boolean",
|
||
"d": "工具栏显示「导出 CSV」:导出筛选 + 排序后的全量行(不分页);缺省 false"
|
||
},
|
||
{
|
||
"n": "exportName",
|
||
"t": "string",
|
||
"d": "导出文件名;缺省 table.csv"
|
||
},
|
||
{
|
||
"n": "onRowClick",
|
||
"t": "(row: T) => void",
|
||
"k": 1,
|
||
"d": "整行点击 / Enter / 空格的回调;传了行才可聚焦可点(未传时行保持纯展示,不抢 Tab 焦点)"
|
||
}
|
||
],
|
||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||
"steps": [
|
||
"准备行数据,确保每行有唯一 id(跨页选择与刷新不丢行靠它)",
|
||
"定义 columns:sortable: true 的列表头可点击排序(升 → 降 → 取消循环),标记箭头 ↑↓↕",
|
||
"列展示细节用皮肤字段:align 控制表头与单元格对齐,format(value, row) 返回纯文本(走 textContent,不需要转义)",
|
||
"列宽用内核字段 width:数字(像素)固定该列,'flex' 或不设则按内容自适应",
|
||
"selectable: true 开启行选择:表头全选框带 indeterminate 半选态,工具栏显示已选计数",
|
||
"选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失",
|
||
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
|
||
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供",
|
||
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
|
||
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
||
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
||
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",
|
||
"需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出",
|
||
"整行可点(进详情、选中一行做后续操作)传 onRowClick:行会变成可聚焦项,Enter / 空格与点击等价;不传时行不进 Tab 序列,纯展示表不会被读屏当成按钮"
|
||
]
|
||
},
|
||
{
|
||
"id": "timeline",
|
||
"title": "时间线 Timeline",
|
||
"demo": "sec-timeline",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "TimelineItem[]",
|
||
"r": 1,
|
||
"d": "条目列表,按时间顺序自上而下;每项含 title(必填)、content、time、variant"
|
||
},
|
||
{
|
||
"n": "mode",
|
||
"t": "'left' | 'right' | 'alternate'",
|
||
"d": "轴线位置:left / right 单侧排布;alternate 双侧交替(轴线居中、奇偶左右分列,窄屏自动退回左侧)",
|
||
"df": "left"
|
||
},
|
||
{
|
||
"n": "pending",
|
||
"t": "boolean",
|
||
"d": "末尾追加虚线「进行中…」节点,表示流程尚未结束;缺省 false",
|
||
"df": "false"
|
||
}
|
||
],
|
||
"example": "timeline({ items: [{ title: '提交审批', time: '09:12', variant: 'success' }, { title: '财务复核', time: '10:30', content: '等待张工处理' }], pending: true })",
|
||
"steps": [
|
||
"事件流按时间顺序排(组件不做排序):较早的条目放前面,最新的放最后",
|
||
"variant 只承载语义:success 完成、warning 待处理、danger 失败、default 普通记录(节点色走 --ua-* 令牌,随暗色主题变化)",
|
||
"time 是纯展示文案,格式自由('09:12' / '10-23 18:00'),组件不做日期解析",
|
||
"流程未结束时传 pending: true,末尾自动补一个虚线脉冲节点,完成后置回 false 即消失",
|
||
"需要轴线居右(如右侧栏时间流)时传 mode: 'right',条目与节点整体镜像",
|
||
"content 为纯文本;需要按钮等交互内容时改用 accordion / card 组合",
|
||
"要做里程碑式双侧时间线就传 mode: 'alternate':轴线居中、条目按奇偶分列左右并在窄屏退回单侧,pending 虚节点同样参与交替计数"
|
||
]
|
||
},
|
||
{
|
||
"id": "segmented",
|
||
"title": "分段控制器 Segmented",
|
||
"demo": "sec-segmented",
|
||
"props": [
|
||
{
|
||
"n": "options",
|
||
"t": "OptionItem[]",
|
||
"r": 1,
|
||
"d": "分段选项列表(互斥),每项含 value / label / disabled"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"r": 1,
|
||
"d": "当前选中值信号,双向绑定(点击写回,外部赋值即同步滑块)"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "'sm' | 'md'",
|
||
"d": "尺寸档位;缺省 md",
|
||
"df": "md"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "整组禁用:布尔直接置位,传信号则动态同步;单项禁用走 options[].disabled"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(value: string) => void",
|
||
"k": 1,
|
||
"d": "切换回调(禁用项与重复点击当前项都不触发)"
|
||
}
|
||
],
|
||
"example": "segmented({ options: [{ value: 'day', label: '日' }, { value: 'week', label: '周' }], value: range })",
|
||
"steps": [
|
||
"和 tabs 的分工:tabs 是页签(下划线指示条 + 面板语义),segmented 是紧凑互斥切换(视图 / 单位 / 时间粒度)",
|
||
"value 必须传信号:点击写回信号,外部改信号滑块自动平移,不要在回调里另存一份状态",
|
||
"options[].disabled 置灰单项;disabled 传布尔或信号则整组失效(连滑块一起置灰)",
|
||
"键盘:←→ 在可用项间环形移动并选中,Tab 只落在当前项上(漫游 tabindex)",
|
||
"滑块几何由组件测量按钮位置得到,不要在外部改按钮宽度 / 内边距,否则需自行重渲染",
|
||
"需要带图标或更复杂布局的分段,改用 radioGroup + 自定义样式"
|
||
]
|
||
},
|
||
{
|
||
"id": "descriptions",
|
||
"title": "描述列表 Descriptions",
|
||
"demo": "sec-descriptions",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "DescriptionItem[]",
|
||
"r": 1,
|
||
"d": "描述项列表:每项含 label(字段名)、value(字符串 / DOM / 响应式函数)、span(跨列数)"
|
||
},
|
||
{
|
||
"n": "column",
|
||
"t": "1 | 2 | 3 | 4",
|
||
"d": "桌面列数;缺省 3,窄屏(≤640px)由样式表降为单列",
|
||
"df": "3"
|
||
},
|
||
{
|
||
"n": "bordered",
|
||
"t": "boolean",
|
||
"d": "表格形态(带分隔线与字段名底纹);缺省 false 纯网格",
|
||
"df": "false"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "'sm' | 'md'",
|
||
"d": "尺寸档位;缺省 md",
|
||
"df": "md"
|
||
},
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"d": "分组标题;缺省不渲染标题行"
|
||
},
|
||
{
|
||
"n": "layout",
|
||
"t": "'horizontal' | 'vertical'",
|
||
"df": "'horizontal'",
|
||
"d": "字段名位置:horizontal 在值左侧,vertical 在值上方(窄栏 / 长文本更耐读,配 bordered 时去掉标签区的分隔线)"
|
||
},
|
||
{
|
||
"n": "extra",
|
||
"t": "unknown",
|
||
"k": 1,
|
||
"d": "标题行右侧内容(如更新时间、刷新按钮);仅在传了 title 时渲染"
|
||
}
|
||
],
|
||
"example": "descriptions({ title: '订单信息', column: 2, bordered: true, items: [{ label: '订单号', value: 'RB-2048' }, { label: '金额', value: '¥1,280.00' }, { label: '备注', value: '加急', span: 2 }] })",
|
||
"steps": [
|
||
"详情页 / 抽屉里展示只读字段用它,不要用 form(form 承载输入与校验)",
|
||
"column 按信息密度定:字段多且短用 3-4 列,字段长(地址、备注)用 2 列并配 span 占整行",
|
||
"value 支持任意 DOM:放 tag、badge、button 都可以;要给值做响应式更新就传函数 () => 内容",
|
||
"bordered: true 是表格形态(适合打印与密集信息),纯网格形态更轻,适合卡片内嵌",
|
||
"窄屏自动降为单列,无需在调用方写媒体查询",
|
||
"纯展示组件,无状态无回调:值变了由调用方重新渲染或传函数式 value",
|
||
"窄栏或长文本场景把 layout 设成 'vertical':字段名落到值上方,bordered 形态会自动去掉标签右侧的分隔线与底纹;标题行要放操作就传 extra(数组可放多个节点)"
|
||
]
|
||
},
|
||
{
|
||
"id": "statistic",
|
||
"title": "统计数值 Statistic",
|
||
"demo": "sec-statistic",
|
||
"props": [
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"d": "指标名(小字灰标题);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<number>",
|
||
"r": 1,
|
||
"d": "数值信号:变化时滚动过渡到新值;倒计时模式可不传"
|
||
},
|
||
{
|
||
"n": "countdown",
|
||
"t": "number",
|
||
"d": "目标时间戳(毫秒):传了即倒计时模式,每秒显示 DD 天 HH:MM:SS,忽略 value"
|
||
},
|
||
{
|
||
"n": "precision",
|
||
"t": "number",
|
||
"d": "小数位,千分位分隔自动带;缺省 0",
|
||
"df": "0"
|
||
},
|
||
{
|
||
"n": "prefix",
|
||
"t": "string",
|
||
"d": "数字前缀(如 ¥);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "suffix",
|
||
"t": "string",
|
||
"d": "数字后缀(如 元 / %);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "trend",
|
||
"t": "'up' | 'down'",
|
||
"d": "环比方向:up 红 / down 绿(A 股涨红跌绿口径);缺省不渲染"
|
||
}
|
||
],
|
||
"example": "const gmv = signal(128000); statistic({ title: '本月 GMV', value: gmv, prefix: '¥', precision: 2, trend: 'up' })",
|
||
"steps": [
|
||
"value 必须传信号:组件用 effect 监听,值变了自动滚动过渡,不要在外部手动改文本",
|
||
"首跑也会从 0 滚到目标值(入场动画);不想要就初始化信号为终值后在挂载后再改",
|
||
"precision 控制小数位,千分位由 toLocaleString 自动加,不要自己拼格式化",
|
||
"trend 只表达方向与颜色(涨红跌绿),数值本身与环比幅度仍由调用方组织",
|
||
"多条指标并排时用 descriptions 或 flex 容器排列,本组件不自带网格",
|
||
"用户开启「减弱动效」时组件直接落终值,无需调用方判断",
|
||
"做倒计时(活动剩余时间等)传 countdown: 目标时间戳,组件每秒刷新并在到点/卸载时自动停表"
|
||
]
|
||
},
|
||
{
|
||
"id": "list",
|
||
"title": "列表 List",
|
||
"demo": "sec-list",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "ListItem[]",
|
||
"r": 1,
|
||
"d": "条目列表,每项 { title, description?, avatar?, extra? }"
|
||
},
|
||
{
|
||
"n": "header",
|
||
"t": "string",
|
||
"d": "列表头标题;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "footer",
|
||
"t": "string",
|
||
"d": "列表尾说明;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "bordered",
|
||
"t": "boolean",
|
||
"d": "是否带外边框与分隔线;缺省 false"
|
||
},
|
||
{
|
||
"n": "emptyText",
|
||
"t": "string",
|
||
"d": "无数据占位文案;缺省「暂无数据」"
|
||
},
|
||
{
|
||
"n": "onItemClick",
|
||
"t": "(item: ListItem, index: number) => void",
|
||
"k": 1,
|
||
"d": "条目点击回调;提供后条目可点击并带 hover 反馈"
|
||
}
|
||
],
|
||
"example": "list({ header: '待办', bordered: true, items: [{ title: '报销单 RB-2048', description: '等待复核', avatar: avatar({ name: '王' }) }], onItemClick: (item) => console.log(item.title) })",
|
||
"steps": [
|
||
"准备 items 数组,每项至少给 title,description 作次要说明",
|
||
"avatar 传头像组件(如 avatar({ name }))、extra 传右侧附加(如 tag(...)),不传该侧不留位",
|
||
"需要点击时传 onItemClick(item, index),条目自动加 hover 底色;bordered: true 给外边框与分隔线",
|
||
"items 为空数组时自动渲染 empty 占位,文案可用 emptyText 覆盖"
|
||
]
|
||
},
|
||
{
|
||
"id": "carousel",
|
||
"title": "轮播 Carousel",
|
||
"demo": "sec-carousel",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "unknown[]",
|
||
"r": 1,
|
||
"d": "每张幻灯片内容,顺序即播放顺序"
|
||
},
|
||
{
|
||
"n": "index",
|
||
"t": "Signal<number>",
|
||
"d": "当前下标信号;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "autoplay",
|
||
"t": "number",
|
||
"d": "自动前进间隔(毫秒);缺省不自动播放"
|
||
},
|
||
{
|
||
"n": "height",
|
||
"t": "number",
|
||
"d": "固定高度像素;缺省由内容撑开"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "区域无障碍名称;缺省「轮播」"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(index: number) => void",
|
||
"k": 1,
|
||
"d": "切换回调:点击指示点/箭头/键盘/自动前进都会触发"
|
||
}
|
||
],
|
||
"example": "const i = signal(0); carousel({ items: [h('div', null, 'A'), h('div', null, 'B')], index: i, height: 150, autoplay: 4000, onChange: (k) => console.log(k) })",
|
||
"steps": [
|
||
"items 每项是任意节点(图、卡片、文字均可),顺序即播放顺序",
|
||
"index 传自己的信号即可外部受控(外部改信号轮播会跟),不传则由组件内部维护",
|
||
"height 控固定高度(缺省随内容撑开);autoplay 传毫秒数自动前进,鼠标悬停自动暂停",
|
||
"键盘:聚焦轮播后 ←→ 切换,事件不冒泡避免连带切换外层 tabs/menu",
|
||
"指示点与箭头都可点击,末张下一张环形回到首张"
|
||
]
|
||
},
|
||
{
|
||
"id": "tree",
|
||
"title": "树形 Tree",
|
||
"demo": "sec-tree",
|
||
"props": [
|
||
{
|
||
"n": "nodes",
|
||
"t": "TreeNode[]",
|
||
"r": 1,
|
||
"d": "根节点列表,每项 { key, label, children?, disabled? }"
|
||
},
|
||
{
|
||
"n": "expanded",
|
||
"t": "Signal<string[]>",
|
||
"d": "展开的 key 集合;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "selected",
|
||
"t": "Signal<string>",
|
||
"d": "选中 key 信号;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "defaultExpandAll",
|
||
"t": "boolean",
|
||
"d": "首屏展开全部分支;缺省 false"
|
||
},
|
||
{
|
||
"n": "searchable",
|
||
"t": "boolean",
|
||
"d": "顶部搜索框:按标签过滤,命中节点保留父链并自动展开;缺省 false"
|
||
},
|
||
{
|
||
"n": "onSelect",
|
||
"t": "(node: TreeNode) => void",
|
||
"k": 1,
|
||
"d": "选中回调:点击标签或 Enter/空格触发"
|
||
},
|
||
{
|
||
"n": "multi",
|
||
"t": "boolean",
|
||
"d": "是否显示勾选框并开启多选;缺省 false"
|
||
},
|
||
{
|
||
"n": "checked",
|
||
"t": "Signal<string[]>",
|
||
"d": "勾选 key 集合(multi 时生效);不传则内部维护"
|
||
},
|
||
{
|
||
"n": "onCheck",
|
||
"t": "(keys: string[]) => void",
|
||
"d": "勾选变更回调(父子已级联,传副本)"
|
||
}
|
||
],
|
||
"example": "const sel = signal(''); tree({ nodes: [{ key: 'src', label: 'src', children: [{ key: 'ui', label: 'ui' }] }], selected: sel, onSelect: (n) => console.log(n.key) })",
|
||
"steps": [
|
||
"节点树用 { key, label, children } 描述,key 唯一即可(选中与展开都按 key 记)",
|
||
"传 selected 信号即可外部受控;不传则组件内部维护选中态",
|
||
"点箭头只展开/收起,点标签才选中;叶子节点箭头位置留空以对齐",
|
||
"键盘:↑↓ 在可见节点间移动、Home/End 到首末行(只移动焦点),Enter/空格 选中、→ 展开、← 收起;disabled 节点点击与键盘都不响应",
|
||
"defaultExpandAll 首屏展开全部分支(外部已给 expanded 初值时以外部为准)",
|
||
"multi: true 时每行前置勾选框:勾父级下钻全选子孙(跳过禁用项),子孙全选后父级自动上勾、部分勾选显示半选",
|
||
"勾选框点击不触发选中(stopPropagation),键盘在多选模式下 Enter/空格改为切换勾选",
|
||
"节点多时开 searchable:输入即过滤,命中节点连同父链一起显示,隐藏的枝不参与方向键遍历"
|
||
]
|
||
},
|
||
{
|
||
"id": "upload",
|
||
"title": "文件选择 Upload",
|
||
"demo": "sec-upload",
|
||
"props": [
|
||
{
|
||
"n": "accept",
|
||
"t": "string",
|
||
"d": "接受的类型(同原生 accept),如 'image/*,.pdf'"
|
||
},
|
||
{
|
||
"n": "multiple",
|
||
"t": "boolean",
|
||
"d": "允许多选;缺省 false(单选时新选择覆盖旧的)"
|
||
},
|
||
{
|
||
"n": "hint",
|
||
"t": "string",
|
||
"d": "提示文案;缺省「点击或拖拽文件到此处」"
|
||
},
|
||
{
|
||
"n": "maxSize",
|
||
"t": "number",
|
||
"d": "单文件体积上限(字节);超限丢弃并在提示行说明,缺省不限"
|
||
},
|
||
{
|
||
"n": "maxCount",
|
||
"t": "number",
|
||
"d": "multiple 时的文件数上限;超出部分丢弃并提示,缺省不限"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean",
|
||
"d": "禁用:不可点选、不接受拖拽"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(files: File[]) => void",
|
||
"k": 1,
|
||
"d": "文件变化回调:选择/拖拽/移除后都触发,回传副本"
|
||
}
|
||
],
|
||
"example": "upload({ multiple: true, accept: 'image/*', hint: '拖拽图片到此处', onChange: (files) => console.log(files.map((f) => f.name)) })",
|
||
"steps": [
|
||
"把 upload 放进表单,accept 限定类型、multiple 允许多选",
|
||
"需要拦体积就给 maxSize(字节),限制数量给 maxCount",
|
||
"被拒的文件不会进清单,原因显示在下方提示行(拖拽与点选走同一套校验)",
|
||
"onChange 拿到的 File[] 是副本,自行发请求上传(组件不内置网络层)",
|
||
"选中的图片会带 28px 缩略图(对象 URL,随文件离场即 revoke,不会泄漏);非图片只显示文件名与体积,点 × 移除"
|
||
]
|
||
},
|
||
{
|
||
"id": "notification",
|
||
"title": "通知卡片 Notification",
|
||
"demo": "sec-notification",
|
||
"props": [
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "通知标题"
|
||
},
|
||
{
|
||
"n": "description",
|
||
"t": "string",
|
||
"d": "补充说明;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'info' | 'success' | 'warning' | 'danger'",
|
||
"d": "语义色(左侧条与圆点);缺省 info"
|
||
},
|
||
{
|
||
"n": "duration",
|
||
"t": "number",
|
||
"d": "自动关闭毫秒;缺省 0 = 常驻不自动关,非 0 时悬停 / 聚焦暂停计时"
|
||
},
|
||
{
|
||
"n": "closable",
|
||
"t": "boolean",
|
||
"d": "是否显示关闭按钮;缺省 true"
|
||
},
|
||
{
|
||
"n": "action",
|
||
"t": "unknown",
|
||
"d": "操作区内容(如按钮);缺省不渲染"
|
||
},
|
||
{
|
||
"n": "placement",
|
||
"t": "'top-right' | 'bottom-right' | 'top-left' | 'bottom-left'",
|
||
"df": "bottom-right",
|
||
"d": "停靠角落;每个角落独立堆叠(同角最多 3 条),右上与 toast 同侧,建议次要信息放左侧"
|
||
},
|
||
{
|
||
"n": "key",
|
||
"t": "string",
|
||
"d": "同 key 复用同一张卡:再次调用只原地改标题 / 描述 / 语义色,不再堆新条(返回的句柄带 update 方法可主动改)"
|
||
}
|
||
],
|
||
"example": "const { close } = notification({ title: '导出完成', description: 'report.csv 已开始下载', variant: 'success', duration: 4000 }); // close() 手动关闭",
|
||
"steps": [
|
||
"调用即入列:右下角堆叠,同屏最多 3 条,超出自动挤掉最早一条",
|
||
"缺省常驻(duration 0),需要自动消失就传毫秒数,如 duration: 4000",
|
||
"返回 { el, close } 句柄:程序化关闭用 close(),需要自己挂交互就用 el",
|
||
"action 放操作区(常见为按钮);variant 决定左侧条与圆点语义色",
|
||
"与 toast 的分工:toast 右上角轻量瞬时,notification 右下角可长读、可操作",
|
||
"停靠角落用 placement 调:四个角各自独立堆叠与节流,顶部角落新通知从屏幕边缘长出、旧的往下让",
|
||
"进度类通知传 key: 'sync-job':第一次返回句柄,后续 job.update({ title, description, variant }) 就地把同一条卡改成 60% / 完成,不会像以前那样堆出三条;job.close() 收掉时同时释放 key 登记"
|
||
]
|
||
},
|
||
{
|
||
"id": "result",
|
||
"title": "结果页 Result",
|
||
"demo": "sec-result",
|
||
"props": [
|
||
{
|
||
"n": "status",
|
||
"t": "'success' | 'warning' | 'danger' | 'info'",
|
||
"d": "终态语义色(图标底色);缺省 info"
|
||
},
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "主标题"
|
||
},
|
||
{
|
||
"n": "description",
|
||
"t": "string",
|
||
"d": "补充说明;缺省不渲染"
|
||
},
|
||
{
|
||
"n": "extra",
|
||
"t": "unknown",
|
||
"d": "操作区内容(常见为主次按钮);缺省不渲染"
|
||
}
|
||
],
|
||
"example": "result({ status: 'success', title: '提交成功', description: '工单 #2049 已受理', extra: button({ label: '查看工单', variant: 'primary', onClick: () => go() }) })",
|
||
"steps": [
|
||
"status 决定图标底色(success 绿 / warning 黄 / danger 红 / info 蓝),整体居中排布",
|
||
"title 必填;description 用于说明原因或下一步,超过一行自动居中换行",
|
||
"extra 放操作区,常见为主按钮 + 幽灵按钮;不传则不渲染",
|
||
"组件自带 role=\"status\",插入页面后读屏会播报终态文案"
|
||
]
|
||
},
|
||
{
|
||
"id": "transfer",
|
||
"title": "穿梭框 Transfer",
|
||
"demo": "sec-transfer",
|
||
"props": [
|
||
{
|
||
"n": "dataSource",
|
||
"t": "TransferItem[]",
|
||
"r": 1,
|
||
"d": "全量条目,左右两栏都按是否在 targetKeys 里派生"
|
||
},
|
||
{
|
||
"n": "targetKeys",
|
||
"t": "Signal<string[]>",
|
||
"d": "右侧集合信号;不传则组件内部维护,传了即外部受控"
|
||
},
|
||
{
|
||
"n": "titles",
|
||
"t": "[string, string]",
|
||
"d": "左右栏标题;缺省「待选列表 / 已选列表」"
|
||
},
|
||
{
|
||
"n": "searchable",
|
||
"t": "boolean",
|
||
"d": "是否显示搜索框;缺省 true,逐栏独立过滤"
|
||
},
|
||
{
|
||
"n": "height",
|
||
"t": "number",
|
||
"d": "列表区固定高度像素;缺省走样式表 220,超出滚动"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(keys: string[]) => void",
|
||
"d": "右侧集合变化回调(搬运后触发,传副本)"
|
||
}
|
||
],
|
||
"example": "transfer({ targetKeys: enabled, titles: ['待选', '已启用'], onChange: (keys) => toast.info(`已启用 ${keys.length} 项`), dataSource: [{ key: 'a11y', label: '无障碍语义' }, { key: 'ssr', label: 'SSR(暂不可用)', disabled: true }] })",
|
||
"steps": [
|
||
"dataSource 是唯一数据源,条目归属由 targetKeys 决定;搬运只改 targetKeys,不删条目",
|
||
"两栏独立勾选,勾选后点中间 → / ← 整体搬运,搬完两栏勾选自动清空;无勾选时按钮自动禁用",
|
||
"每栏均有搜索框与全选:全选只覆盖当前搜索结果里未禁用的条目,部分勾选时全选框显示半选",
|
||
"disabled 条目不可勾选、不计入全选基数、也不会被搬运",
|
||
"传入 targetKeys 信号即为受控用法,外部写入即可程序化搬运(如「一键全选」)"
|
||
]
|
||
},
|
||
{
|
||
"id": "cascader",
|
||
"title": "级联选择 Cascader",
|
||
"demo": "sec-cascader",
|
||
"props": [
|
||
{
|
||
"n": "options",
|
||
"t": "CascaderOption[]",
|
||
"r": 1,
|
||
"d": "根级选项,children 递归出列,value 逐级拼成路径"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string[]>",
|
||
"d": "选中路径信号;不传则内部维护,传了即外部受控"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "未选时占位文案;缺省「请选择」"
|
||
},
|
||
{
|
||
"n": "separator",
|
||
"t": "string",
|
||
"d": "路径分隔符;缺省「 / 」"
|
||
},
|
||
{
|
||
"n": "changeOnSelect",
|
||
"t": "boolean",
|
||
"d": "允许停在中间层;缺省 false 必须选到叶子才提交"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用触发器;支持信号响应式禁用"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(path: string[]) => void",
|
||
"d": "选中回调(叶子或中间层,传副本)"
|
||
}
|
||
],
|
||
"example": "cascader({ value: areaPath, changeOnSelect: true, options: [{ value: 'east', label: '华东', children: [{ value: 'sh', label: '上海' }] }] })",
|
||
"steps": [
|
||
"传入嵌套 options,第一列是根级选项,悬停带 children 的项即展开下一列",
|
||
"点击叶子提交;changeOnSelect 为 true 时点中间层也提交(适合「省/市」这类粗粒度就够用的场景)",
|
||
"value 是路径数组(逐级 value),传信号即受控;触发器文案按 separator 拼接已提交路径",
|
||
"键盘:↑↓ 同列移动、→ 下钻、← 回退、Enter/空格 提交、Esc 收起(与弹窗共用一个 Esc 栈)",
|
||
"面板空间不足时自动向上翻转;disabled 选项不可点也不会展开下一列"
|
||
]
|
||
},
|
||
{
|
||
"id": "splitter",
|
||
"title": "可拖拽分栏 Splitter",
|
||
"demo": "sec-splitter",
|
||
"props": [
|
||
{
|
||
"n": "panes",
|
||
"t": "unknown[]",
|
||
"r": 1,
|
||
"d": "各栏内容(节点或纯文本),顺序即排列顺序"
|
||
},
|
||
{
|
||
"n": "direction",
|
||
"t": "'horizontal' | 'vertical'",
|
||
"d": "分割方向;缺省 horizontal(左右)"
|
||
},
|
||
{
|
||
"n": "sizes",
|
||
"t": "Signal<number[]>",
|
||
"d": "各栏百分比数组(和为 100);不传则内部均分"
|
||
},
|
||
{
|
||
"n": "min",
|
||
"t": "number",
|
||
"d": "每栏最小百分比;缺省 10,拖到边界即停"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(sizes: number[]) => void",
|
||
"d": "拖拽 / 键盘调整后回调(传副本)"
|
||
}
|
||
],
|
||
"example": "splitter({ sizes: ratio, min: 15, panes: [leftPanel, rightPanel], onChange: (s) => ratio([...s]) })",
|
||
"steps": [
|
||
"panes 长度决定栏数与分隔条数(n 栏对应 n-1 条),栏宽由 sizes 百分比驱动",
|
||
"指针按住分隔条拖动即可改比例,拖动过程两栏之和恒定、各自不低于 min",
|
||
"分隔条可 Tab 聚焦,← → / ↑ ↓ 每次调 2%,按住 Shift 调 10%;双击复位为均分",
|
||
"direction 传 vertical 变为上下分栏,键盘方向键与光标同步切换",
|
||
"传入 sizes 信号即受控,外部写入可做「预设布局」切换(如 1:1 / 2:1)"
|
||
]
|
||
},
|
||
{
|
||
"id": "autoComplete",
|
||
"title": "自动补全 AutoComplete",
|
||
"demo": "sec-autocomplete",
|
||
"props": [
|
||
{
|
||
"n": "options",
|
||
"t": "AutoCompleteOption[] | ((q: string) => AutoCompleteOption[])",
|
||
"r": 1,
|
||
"d": "候选项:静态列表或按关键字计算的函数(可远程联想)"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"d": "输入值信号;不传则内部维护,传了即外部受控"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "占位文案,同时作为无障碍名称"
|
||
},
|
||
{
|
||
"n": "emptyText",
|
||
"t": "string",
|
||
"d": "无匹配时的提示文案;缺省「无匹配项」"
|
||
},
|
||
{
|
||
"n": "maxItems",
|
||
"t": "number",
|
||
"d": "最多展示条数;缺省 8"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用输入与弹层"
|
||
},
|
||
{
|
||
"n": "onInput",
|
||
"t": "(query: string) => void",
|
||
"d": "每次输入回调,适合触发远程请求"
|
||
},
|
||
{
|
||
"n": "onSelect",
|
||
"t": "(option: AutoCompleteOption) => void",
|
||
"d": "选中回调(点击或 Enter)"
|
||
}
|
||
],
|
||
"example": "autoComplete({ value: acValue, placeholder: '输入组件名', options: [{ value: 'dataTable', hint: '表格' }], onSelect: (o) => toast.success(o.value) })",
|
||
"steps": [
|
||
"输入任何字符即按包含匹配(忽略大小写)过滤候选,空串时弹层不展开",
|
||
"↑↓ 在候选间环形移动并高亮,Enter 选中,Esc 收起,Tab 失焦也会收起",
|
||
"候选行支持右侧 hint(分类 / 拼音),选中后值写回输入框并触发 onSelect",
|
||
"options 传函数时每次输入都会用当前关键字调用,可在此发起远程请求并返回新数组",
|
||
"value 传信号即受控:外部写入会同步回填输入框内容"
|
||
]
|
||
},
|
||
{
|
||
"id": "datePicker",
|
||
"title": "日期选择 DatePicker",
|
||
"demo": "sec-datepicker",
|
||
"props": [
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"d": "选中日期(YYYY-MM-DD)信号;区间模式下是起始日,不传则内部维护"
|
||
},
|
||
{
|
||
"n": "range",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "区间模式:首次点击定起点(弹层不收起),再次点击定终点;点早了自动交换先后"
|
||
},
|
||
{
|
||
"n": "end",
|
||
"t": "Signal<string>",
|
||
"d": "区间模式的结束日信号;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "onRangeChange",
|
||
"t": "(start: string, end: string) => void",
|
||
"k": 1,
|
||
"d": "区间两端都选定后的回调,参数已按先后排序(单日模式不触发)"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "未选时占位文案;缺省「请选择日期」(区间模式缺省「请选择日期范围」)"
|
||
},
|
||
{
|
||
"n": "min",
|
||
"t": "string",
|
||
"d": "最早可选日期(含,YYYY-MM-DD);更早的日置灰不可点"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "string",
|
||
"d": "最晚可选日期(含,YYYY-MM-DD);更晚的日置灰不可点"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用触发器"
|
||
},
|
||
{
|
||
"n": "presets",
|
||
"t": "DatePreset[]",
|
||
"d": "快捷区间按钮组(仅区间模式渲染):每项给 label + days(相对今天含今天),或 label + from/to 绝对日期;越界结果按 min/max 钳制,点击即写入 value/end 并收起"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(date: string) => void",
|
||
"d": "选中回调(点击日期或 Enter)"
|
||
}
|
||
],
|
||
"example": "datePicker({ value: dateVal, min: '2026-10-20', max: '2026-10-31', onChange: (d) => toast.success(d) })",
|
||
"steps": [
|
||
"点触发器展开当月网格,打开时视图自动对准已选日期(无选中则今天)",
|
||
"‹ › 翻月,底部「今天」一键跳今天并提交;今天用描边标记、选中用实心底色",
|
||
"键盘:←→ 逐日、↑↓ 逐周、PageUp/PageDown 逐月、Enter 提交,跨月会自动翻页",
|
||
"min / max 为闭区间,越界日期置灰不可点也不可键盘选中",
|
||
"value 传信号即受控;Esc 收起(与弹窗/抽屉共用逐层关闭栈)",
|
||
"选日期区间传 range: true:第一次点击只定起点(触发器显示「起 ~ …」),第二次点击补终点并收起,两端之间用低比重底色连成一条",
|
||
"区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号",
|
||
"传 presets 在弹层顶部加一排快捷区间按钮:{ label: '最近 7 天', days: 7 } 走相对今天,{ label: '本月', from: '2026-10-01', to: '2026-10-31' } 走绝对日期(两者都给时 days 优先),点一下即写入并收起,回调照旧走 onRangeChange"
|
||
]
|
||
},
|
||
{
|
||
"id": "backTop",
|
||
"title": "返回顶部 BackTop",
|
||
"demo": "sec-backtop",
|
||
"props": [
|
||
{
|
||
"n": "target",
|
||
"t": "HTMLElement | Window",
|
||
"d": "监听的滚动容器;缺省 window(整页)"
|
||
},
|
||
{
|
||
"n": "threshold",
|
||
"t": "number",
|
||
"d": "滚动超过多少像素才浮现;缺省 300"
|
||
},
|
||
{
|
||
"n": "progress",
|
||
"t": "boolean",
|
||
"d": "是否显示滚动进度环;缺省 true"
|
||
},
|
||
{
|
||
"n": "onClick",
|
||
"t": "() => void",
|
||
"d": "回顶之后的回调(适合埋点)"
|
||
}
|
||
],
|
||
"example": "backTop({ threshold: 300, onClick: () => toast.info('已回到顶部') })",
|
||
"steps": [
|
||
"把返回的元素挂到页面任意位置即可,组件自身 position: fixed 定位在右下角",
|
||
"滚动越过 threshold 才浮现(淡入 + 上移微动画),回到阈值内自动隐藏",
|
||
"progress 为 true 时外环弧长等于已滚比例;内滚容器用 target 传入该元素",
|
||
"点击平滑回顶;系统开启「减少动效」时改为瞬间跳转"
|
||
]
|
||
},
|
||
{
|
||
"id": "colorPicker",
|
||
"title": "颜色选择 ColorPicker",
|
||
"demo": "sec-colorpicker",
|
||
"props": [
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"d": "颜色值信号(CSS 颜色串,支持 var(--ua-*));不传则内部维护"
|
||
},
|
||
{
|
||
"n": "presets",
|
||
"t": "string[]",
|
||
"d": "预设色板;缺省取语义令牌色(accent/success/warning/danger/text/border)"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "未选时占位文案;缺省「请选择颜色」"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用触发器"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(color: string) => void",
|
||
"d": "选定回调(点色板、取色器或手输回车)"
|
||
}
|
||
],
|
||
"example": "colorPicker({ value: colorVal, onChange: (c) => toast.success(c) })",
|
||
"steps": [
|
||
"点触发器展开弹层:上方是预设色板,下方是原生取色器 + 手输框",
|
||
"预设缺省直接用 --ua-* 令牌色,所以暗色主题切换后色块自动跟着变,无需维护两套色值",
|
||
"点色块即提交;手输框支持任意 CSS 颜色串(含 var(--ua-color-accent)),回车提交",
|
||
"触发器左侧色块始终预览当前值,点击外部或 Esc 收起"
|
||
]
|
||
},
|
||
{
|
||
"id": "tour",
|
||
"title": "引导漫游 Tour",
|
||
"demo": "sec-tour",
|
||
"props": [
|
||
{
|
||
"n": "steps",
|
||
"t": "TourStep[]",
|
||
"r": 1,
|
||
"d": "步骤序列:每步含 target 选择器、title、可选 description"
|
||
},
|
||
{
|
||
"n": "padding",
|
||
"t": "number",
|
||
"d": "聚光区相对目标的外扩像素;缺省 6"
|
||
},
|
||
{
|
||
"n": "onFinish",
|
||
"t": "() => void",
|
||
"d": "走完最后一步的回调"
|
||
},
|
||
{
|
||
"n": "onClose",
|
||
"t": "() => void",
|
||
"d": "中途跳过(× 或 Esc)的回调"
|
||
}
|
||
],
|
||
"example": "const t = tour({ steps: [{ target: '#sec-tour', title: '引导漫游', description: '按选择器定位' }], onFinish: () => toast.success('完成') }); document.body.append(t.el); t.start();",
|
||
"steps": [
|
||
"tour() 返回 { el, start, close } 句柄:el 必须挂到 body(fixed 定位,不占布局)",
|
||
"start() 从第一步播放,可重复调用重播;close() 立即退出并按跳过处理",
|
||
"聚光框按 target 选择器定位并外扩 padding,其余区域由超大 box-shadow 压暗(省掉独立遮罩层)",
|
||
"卡片优先落在目标下方,空间不足自动翻到上方,左右夹在视口内;滚动 / 缩放会实时重算",
|
||
"末步按钮文案变「完成」并触发 onFinish;× 或 Esc 触发 onClose,焦点归还开启前的元素"
|
||
]
|
||
},
|
||
{
|
||
"id": "watermark",
|
||
"title": "水印 Watermark",
|
||
"demo": "sec-watermark",
|
||
"props": [
|
||
{
|
||
"n": "text",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "水印文字,支持 \\n 多行"
|
||
},
|
||
{
|
||
"n": "opacity",
|
||
"t": "number",
|
||
"d": "透明度;缺省 0.12"
|
||
},
|
||
{
|
||
"n": "rotate",
|
||
"t": "number",
|
||
"d": "旋转角度(度);缺省 -20"
|
||
},
|
||
{
|
||
"n": "gap",
|
||
"t": "number",
|
||
"d": "平铺间距像素;缺省 140"
|
||
},
|
||
{
|
||
"n": "fontSize",
|
||
"t": "number",
|
||
"d": "字号像素;缺省 14"
|
||
},
|
||
{
|
||
"n": "color",
|
||
"t": "string",
|
||
"d": "颜色(CSS 串或 var(--ua-*) 令牌);缺省取 --ua-color-text-muted 的实色"
|
||
},
|
||
{
|
||
"n": "children",
|
||
"t": "...unknown[]",
|
||
"d": "被水印覆盖的内容,作为第二个及之后的实参传入"
|
||
}
|
||
],
|
||
"example": "watermark({ text: '内部资料\\n仅限内网', gap: 160 }, card({ title: '受保护内容' }, h('p', null, '正文')))",
|
||
"steps": [
|
||
"第一个参数是配置,其后所有实参都被当作内容渲染在水印层下方",
|
||
"组件用一块 gap 见方的 canvas 画单个旋转单元,再 background-repeat 铺满,因此不需要监听尺寸变化",
|
||
"color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随",
|
||
"覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪"
|
||
]
|
||
},
|
||
{
|
||
"id": "checkboxGroup",
|
||
"title": "多选组 CheckboxGroup",
|
||
"demo": "sec-checkgroup",
|
||
"props": [
|
||
{
|
||
"n": "options",
|
||
"t": "CheckOption[]",
|
||
"r": 1,
|
||
"d": "选项集合:value / label / 可选 disabled"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string[]>",
|
||
"d": "选中值数组信号;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "min",
|
||
"t": "number",
|
||
"d": "最少选几项:到下限后已选项禁用,防止取消"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "number",
|
||
"d": "最多选几项:到上限后未选项禁用"
|
||
},
|
||
{
|
||
"n": "checkAll",
|
||
"t": "boolean",
|
||
"d": "是否显示全选行(含半选);缺省 false"
|
||
},
|
||
{
|
||
"n": "direction",
|
||
"t": "'horizontal' | 'vertical'",
|
||
"d": "排列方向;缺省 horizontal"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "整组禁用"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(values: string[]) => void",
|
||
"d": "变更回调(传副本)"
|
||
}
|
||
],
|
||
"example": "checkboxGroup({ value: permPick, min: 1, max: 3, checkAll: true, options: [{ value: 'read', label: '读取' }, { value: 'write', label: '写入' }] })",
|
||
"steps": [
|
||
"options 顺序即渲染顺序;勾选后把整份数组写回 value 信号并触发 onChange",
|
||
"max 生效时未选项自动禁用,min 生效时已选项自动禁用——用禁用表达边界,不抛错也不静默回滚",
|
||
"checkAll 行的选中/半选只统计未禁用项,禁用项不计入基数",
|
||
"direction 传 vertical 改为纵向排列;disabled 支持信号,整组响应式禁用"
|
||
]
|
||
},
|
||
{
|
||
"id": "affix",
|
||
"title": "固钉 Affix",
|
||
"demo": "sec-affix",
|
||
"props": [
|
||
{
|
||
"n": "offset",
|
||
"t": "number",
|
||
"d": "距视口顶部多少像素后转为固定;缺省 0"
|
||
},
|
||
{
|
||
"n": "position",
|
||
"t": "'top' | 'bottom'",
|
||
"d": "固定边:贴顶放头部工具栏,贴底放底部操作条;缺省 top"
|
||
},
|
||
{
|
||
"n": "target",
|
||
"t": "HTMLElement | Window",
|
||
"d": "监听的滚动容器;缺省 window(整页)"
|
||
},
|
||
{
|
||
"n": "children",
|
||
"t": "...unknown[]",
|
||
"d": "被固定的内容,作为第二个及之后的实参传入"
|
||
}
|
||
],
|
||
"example": "affix({ offset: 8 }, card({ title: '滚动时我会贴顶' }, h('p', null, '正文')))",
|
||
"steps": [
|
||
"把要贴住的内容作为实参传入;组件内部包两层:外层占位、内层负责 fixed",
|
||
"滚动到 offset 以内(默认贴视口顶)时内层转 fixed,外层同步撑出等高占位,页面不会塌跳",
|
||
"固定期间宽度与横向位置跟随占位容器,窗口缩放或父容器变宽都自动贴合",
|
||
"内滚容器场景把该元素传给 target,判定改为基于它的滚动位置",
|
||
"底部操作条传 position: 'bottom':占位容器底边越过「视口底 - offset」时才固定,对应的内联边距写 bottom"
|
||
]
|
||
},
|
||
{
|
||
"id": "multiSelect",
|
||
"title": "多选下拉 MultiSelect",
|
||
"demo": "sec-multiselect",
|
||
"props": [
|
||
{
|
||
"n": "options",
|
||
"t": "CheckOption[]",
|
||
"r": 1,
|
||
"d": "选项集合:value / label / 可选 disabled;每项可加 group 指定分组小标题"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string[]>",
|
||
"d": "选中值数组信号;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "未选时占位文案;已选时让位给标签"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "number",
|
||
"d": "最多选几项:到上限后未选项自动禁用"
|
||
},
|
||
{
|
||
"n": "maxTagCount",
|
||
"t": "number",
|
||
"d": "标签区最多显示几个,超出的折成一枚「+N」(悬停看全量、点击展开弹层);缺省全部显示"
|
||
},
|
||
{
|
||
"n": "searchable",
|
||
"t": "boolean",
|
||
"d": "是否可输入过滤;缺省 true"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean | Signal<boolean>",
|
||
"d": "禁用整个下拉"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(values: string[]) => void",
|
||
"d": "变更回调(传副本)"
|
||
}
|
||
],
|
||
"example": "multiSelect({ value: tagPick, max: 3, placeholder: '选择技术栈', options: [{ value: 'ts', label: 'TypeScript' }, { value: 'css', label: 'CSS 令牌' }] })",
|
||
"steps": [
|
||
"选中项以标签形式回显,点标签上的 × 或空输入时按退格即可删除末个标签",
|
||
"点输入区展开弹层并聚焦输入框,输入字符即时过滤(大小写不敏感)",
|
||
"点选项只切换该项、弹层不收起,适合连续多选;到 max 后未选项置灰不可点",
|
||
"↑↓ 环形移动高亮、Enter 切换当前项、Esc 收起(与其它弹层共用一个 Esc 栈)",
|
||
"value 传数组信号即受控,外部写入会同步标签区",
|
||
"选中项多时传 maxTagCount: 2 折叠:前 2 枚标签照常逐个删除,其余折成虚线「+N」,悬停 title 给出被折叠的项名,点它直接展开弹层;受控数组一变折叠数就重算",
|
||
"候选分组同样用 group 字段:小标题是不可点击的 li(不占键盘高亮下标),选中后的标签顺序仍按点击顺序而非分组顺序"
|
||
]
|
||
},
|
||
{
|
||
"id": "anchor",
|
||
"title": "锚点导航 Anchor",
|
||
"demo": "sec-anchor",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "AnchorItem[]",
|
||
"d": "锚点项集合,每项含 href(形如 #sec-x)、title、可选 disabled"
|
||
},
|
||
{
|
||
"n": "offset",
|
||
"t": "number",
|
||
"d": "定位与高亮判定预留的顶部距离,适合有吸顶栏的页面;缺省 0"
|
||
},
|
||
{
|
||
"n": "container",
|
||
"t": "HTMLElement | Window",
|
||
"d": "监听的滚动容器;缺省 window(整页滚动)"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "nav 的无障碍名称;缺省「锚点导航」"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(href: string) => void",
|
||
"d": "跳转后的回调,参数为命中的 href(适合埋点或联动)"
|
||
}
|
||
],
|
||
"example": "anchor({ items: [{ href: '#sec-button', title: '按钮' }, { href: '#sec-card', title: '卡片' }], offset: 72 })",
|
||
"steps": [
|
||
"在页面任意位置放一个容器,调用 anchor 并把返回的 nav 塞进去(固定侧栏或吸顶栏均可)",
|
||
"items 里每条 href 指向页面上真实存在的区块 id,标题即链接文案;目标缺失的项在滚动判定时自动跳过",
|
||
"页面有吸顶导航时把它的高度传给 offset,跳转与高亮判定都会预留这段距离",
|
||
"滚动容器不是整页时用 container 传入该元素;点击走平滑滚动,系统开启「减少动效」时改为瞬移,hash 用 replaceState 回写不堆历史"
|
||
]
|
||
},
|
||
{
|
||
"id": "popconfirm",
|
||
"title": "二次确认 Popconfirm",
|
||
"demo": "sec-popconfirm",
|
||
"props": [
|
||
{
|
||
"n": "title",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "气泡标题,通常写成一句问句(如「删除这条记录?」)"
|
||
},
|
||
{
|
||
"n": "description",
|
||
"t": "string",
|
||
"d": "补充说明:影响范围、是否可撤销;缺省给一句通用提示"
|
||
},
|
||
{
|
||
"n": "confirmText",
|
||
"t": "string",
|
||
"d": "确认按钮文案;缺省「确定」"
|
||
},
|
||
{
|
||
"n": "cancelText",
|
||
"t": "string",
|
||
"d": "取消按钮文案;缺省「取消」"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'default' | 'danger'",
|
||
"d": "确认按钮语义色;删除类操作传 danger,缺省 default"
|
||
},
|
||
{
|
||
"n": "placement",
|
||
"t": "'top' | 'bottom'",
|
||
"d": "气泡方位;缺省 top,压在触发器上方不盖住被操作的行"
|
||
},
|
||
{
|
||
"n": "onConfirm",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "点确定后的回调:普通回调先收起再执行;返回 Promise 时按钮转「处理中…」并禁用,成功才收起、失败留在气泡里可重试"
|
||
},
|
||
{
|
||
"n": "onCancel",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "取消 / 点外部 / Esc 收起时的回调"
|
||
},
|
||
{
|
||
"n": "open",
|
||
"t": "Signal<boolean>",
|
||
"d": "受控开合信号;不传则内部维护,传了可由外部编程开合"
|
||
}
|
||
],
|
||
"example": "popconfirm({ title: '删除这条记录?', description: '删除后无法恢复', variant: 'danger', confirmText: '删除', onConfirm: () => toast.success('已删除') }, button({ label: '删除', variant: 'danger' }))",
|
||
"steps": [
|
||
"把要保护的触发器元素(按钮 / 图标)作为第二个参数传进去,返回的包裹节点替换原位置",
|
||
"写清 title(问句)与 description(影响范围),删除类操作把 variant 设成 danger,确认按钮会自动变成危险色",
|
||
"点确定先收起弹层再触发 onConfirm,回调里可以放心同步改数据;取消、点外部、Esc 一律走 onCancel",
|
||
"展开时焦点自动落在「取消」上,误按回车不会执行危险操作;需要外部控制开合时传 open 信号",
|
||
"删除 / 归档这类要打后端的操作把 onConfirm 写成 async:气泡不会立刻收起,确认按钮变「处理中…」并禁用,resolve 后才关,reject 时保持展开供重试"
|
||
]
|
||
},
|
||
{
|
||
"id": "calendar",
|
||
"title": "月历 Calendar",
|
||
"demo": "sec-calendar",
|
||
"props": [
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"d": "选中日期(YYYY-MM-DD)信号;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "month",
|
||
"t": "Signal<string>",
|
||
"d": "展示月份(YYYY-MM);不传则内部维护,初始跟随 value 或今天"
|
||
},
|
||
{
|
||
"n": "marker",
|
||
"t": "(date: string) => string | null",
|
||
"k": 1,
|
||
"d": "每日标记:返回文案即渲染小徽标(如「3 项」),同时写进 aria-label;返回 null 不标记"
|
||
},
|
||
{
|
||
"n": "onSelect",
|
||
"t": "(date: string) => void",
|
||
"k": 1,
|
||
"d": "选中日期回调(点击或 Enter / 空格)"
|
||
},
|
||
{
|
||
"n": "onMonthChange",
|
||
"t": "(month: string) => void",
|
||
"k": 1,
|
||
"d": "翻月回调,参数为 YYYY-MM(可在此拉取当月数据)"
|
||
}
|
||
],
|
||
"example": "const day = signal(''); calendar({ value: day, marker: (d) => (d === '2026-10-26' ? '2 项' : null), onSelect: (d) => toast.info(d) })",
|
||
"steps": [
|
||
"把 calendar 放在页面容器里即可,它自带标题行(月份 + 上下月箭头)、星期表头与 6 行网格",
|
||
"选中日期用 value 信号双向绑定;不传则组件内部维护,onSelect 里拿字符串自己处理",
|
||
"需要显示每天有几件事就传 marker:返回非空文案即渲染小徽标,并拼进 aria-label 让读屏也听得到",
|
||
"键盘:Tab 进入网格一次,←→ 逐日、↑↓ 逐周、Enter / 空格 选中;跨月会自动翻页并交接焦点"
|
||
]
|
||
},
|
||
{
|
||
"id": "typography",
|
||
"title": "排版文本 Typography",
|
||
"demo": "sec-typography",
|
||
"props": [
|
||
{
|
||
"n": "content",
|
||
"t": "string | Node",
|
||
"r": 1,
|
||
"d": "文本内容:字符串按纯文本渲染;传 DOM 则原样插入(此时不参与截断与复制)"
|
||
},
|
||
{
|
||
"n": "variant",
|
||
"t": "'title' | 'text' | 'paragraph'",
|
||
"df": "text",
|
||
"d": "排版形态,决定语义标签与字号:title 出 h1-h5、paragraph 出 p、text 出 span"
|
||
},
|
||
{
|
||
"n": "level",
|
||
"t": "1 | 2 | 3 | 4 | 5",
|
||
"df": "3",
|
||
"d": "标题层级,仅 variant=title 生效;越界值会被夹到 1-5"
|
||
},
|
||
{
|
||
"n": "type",
|
||
"t": "'secondary' | 'success' | 'warning' | 'danger'",
|
||
"d": "语义色调;缺省主文本色"
|
||
},
|
||
{
|
||
"n": "strong",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "加粗强调"
|
||
},
|
||
{
|
||
"n": "underline",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "下划线(可为链接式文案保留指引视觉)"
|
||
},
|
||
{
|
||
"n": "delete",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "删除线,用于作废文案;会同时弱化文字颜色"
|
||
},
|
||
{
|
||
"n": "mark",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "高亮底纹(警示色 24% 混透明),用于搜索命中片段"
|
||
},
|
||
{
|
||
"n": "code",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "等宽代码样式,用于行内字段名 / 命令"
|
||
},
|
||
{
|
||
"n": "align",
|
||
"t": "'left' | 'center' | 'right'",
|
||
"d": "水平对齐;缺省左对齐"
|
||
},
|
||
{
|
||
"n": "ellipsis",
|
||
"t": "number",
|
||
"d": "截断行数(1 起):超出显示省略号,纯文本自动挂 title 供悬停看全文;缺省不截断"
|
||
},
|
||
{
|
||
"n": "copyable",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "右侧附复制按钮:点击写剪贴板并回显「已复制 / 复制失败」,1.2 秒后复原"
|
||
}
|
||
],
|
||
"example": "typography({ variant: 'title', level: 2, content: '部署说明' }); typography({ content: 'RB-2048', code: true, copyable: true }); typography({ variant: 'paragraph', ellipsis: 2, content: '一段很长的说明……' })",
|
||
"steps": [
|
||
"正文一律用 variant='text' 或 'paragraph',标题用 variant='title' + level,标签自动出 h1-h5,别自己写 h 标签再叠样式",
|
||
"文内强调用装饰位组合:strong 加粗、mark 高亮、code 等宽、underline / delete 表示链接与作废,一个组件内可叠加",
|
||
"长描述(卡片摘要、表格说明)用 ellipsis 给行数:超出显示省略号并自动挂 title,悬停即看全文,不必额外做展开按钮",
|
||
"订单号 / 密钥 / 命令这类要复制的内容传 copyable:按钮独立于文本节点,复制失败会回显「复制失败」而不是静默",
|
||
"content 传 DOM 时组件只负责语义标签与类名:截断与复制对 DOM 内容不生效(取不到稳定文本)",
|
||
"段落连续排列时相邻 .ua-type-paragraph 自动留出间距,单个段落不额外撑高,嵌进卡片也不会有空隙"
|
||
]
|
||
},
|
||
{
|
||
"id": "grid",
|
||
"title": "栅格容器 Grid",
|
||
"demo": "sec-grid",
|
||
"props": [
|
||
{
|
||
"n": "columns",
|
||
"t": "number",
|
||
"df": "3",
|
||
"d": "固定列数(按等宽铺开);窄屏(≤640px)由样式表统一降为单列"
|
||
},
|
||
{
|
||
"n": "min",
|
||
"t": "string",
|
||
"d": "自适应最小列宽(如 '220px'):给了就按 auto-fit 铺列,列数随可用宽度自然增减,此时忽略 columns"
|
||
},
|
||
{
|
||
"n": "gutter",
|
||
"t": "SpaceSize",
|
||
"df": "md",
|
||
"d": "列间距:sm/md/lg 走令牌,数字按像素"
|
||
},
|
||
{
|
||
"n": "align",
|
||
"t": "'start' | 'center' | 'end' | 'stretch'",
|
||
"df": "stretch",
|
||
"d": "交叉轴对齐:同行子项高度不一时决定各自贴哪边或拉满"
|
||
},
|
||
{
|
||
"n": "dense",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "紧凑填充:小项回填前面留下的空洞(瀑布式排布)"
|
||
}
|
||
],
|
||
"example": "grid({ columns: 3, gutter: 'md' }, card({ title: '实例数' }, statistic({ value: total })), card({ title: '错误率' }, statistic({ value: errRate }))); grid({ min: '240px', align: 'start' }, ...items)",
|
||
"steps": [
|
||
"页面骨架用 grid({ columns: n }) 铺等宽卡片:子项宽度由列模板算,别在子项上写 width,窄屏会自动变成单列",
|
||
"卡片宽度要求「不窄于 X」时用 min(如 '240px'):auto-fit 会按可用宽度决定列数,比手写断点稳,此时不要再传 columns",
|
||
"列间距统一走 gutter(sm/md/lg 令牌或像素),不要在子项上写 margin——相邻 margin 会叠加成双倍间距",
|
||
"同行卡片高度参差时:要等高(默认 stretch),要各自顶端对齐传 align: 'start'",
|
||
"子项必须是单个元素(card / div / 自定义块),grid 只负责排布且已对子项设 min-width: 0,长文本不会撑破列宽",
|
||
"只要横向一排展示且元素不等宽,用 space 更轻;grid 面向二维对齐"
|
||
]
|
||
},
|
||
{
|
||
"id": "floatButton",
|
||
"title": "悬浮操作按钮 FloatButton",
|
||
"demo": "sec-floatbutton",
|
||
"props": [
|
||
{
|
||
"n": "icon",
|
||
"t": "string | Node",
|
||
"d": "图标:字符串按字符(emoji / 符号)渲染,传 DOM 原样插入"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"d": "文字标签;给了就出「图标 + 文字」胶囊,缺省为纯图标圆形"
|
||
},
|
||
{
|
||
"n": "badge",
|
||
"t": "string | number",
|
||
"d": "角标(数字或短文案,如 3 / '新');缺省不渲染"
|
||
},
|
||
{
|
||
"n": "tooltip",
|
||
"t": "string",
|
||
"d": "悬停提示,同时用作 aria-label;纯图标时必传,否则读屏听到默认「快捷操作」"
|
||
},
|
||
{
|
||
"n": "position",
|
||
"t": "'right-bottom' | 'left-bottom'",
|
||
"df": "right-bottom",
|
||
"d": "停靠角落;右下与 backTop 同侧,需同时出现时错开一左一右"
|
||
},
|
||
{
|
||
"n": "offset",
|
||
"t": "number",
|
||
"df": "24",
|
||
"d": "距边距离(px);同角落堆叠多个时按倍数递增"
|
||
},
|
||
{
|
||
"n": "onClick",
|
||
"t": "() => void",
|
||
"k": 1,
|
||
"d": "点击回调"
|
||
}
|
||
],
|
||
"example": "document.body.append(floatButton({ icon: '+', label: '新建工单', position: 'left-bottom', onClick: () => toast.success('已新建') })); document.body.append(floatButton({ icon: '?', tooltip: '帮助中心', badge: 3 }))",
|
||
"steps": [
|
||
"把返回节点挂到 document.body(不是页面容器内):它是 fixed 定位,挂在容器里会被 overflow: hidden 裁剪",
|
||
"只有图标时务必传 tooltip:它同时是 aria-label,读屏用户靠它知道按钮干什么;有 label 时 tooltip 可省",
|
||
"label 存在即「图标 + 文字」胶囊,只有图标即 44px 圆形;两种形态都保证触摸目标不小于 44px",
|
||
"同一角落要放多个按钮时递增 offset(如 24 / 80),或把它们并进同一个 flex 容器统一排布——组件自身不做堆叠计算",
|
||
"右下角已被 backTop 占用,页面同时有返回顶部时用 position: 'left-bottom'",
|
||
"纯展示 + 单回调,没有选中态与开合状态;需要多级操作请用 menu / popover 挂在 onClick 上"
|
||
]
|
||
},
|
||
{
|
||
"id": "mentions",
|
||
"title": "@提及输入 Mentions",
|
||
"demo": "sec-mentions",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "MentionItem[]",
|
||
"r": 1,
|
||
"d": "候选池:每项含 name(插入正文的名字)、label(显示名)、description(说明)、keywords(只参与过滤的额外匹配词)"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"d": "受控文本信号;不传则组件内部维护(拿不到外部读取时不要传)"
|
||
},
|
||
{
|
||
"n": "rows",
|
||
"t": "number",
|
||
"df": "3",
|
||
"d": "输入框行数"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "占位文案"
|
||
},
|
||
{
|
||
"n": "onSelect",
|
||
"t": "(item: MentionItem) => void",
|
||
"k": 1,
|
||
"d": "选中某人后的回调(弹层已收起,正文已替换)"
|
||
},
|
||
{
|
||
"n": "onInput",
|
||
"t": "(text: string) => void",
|
||
"k": 1,
|
||
"d": "每次输入的回调,参数为当前全文(已写回 value 信号)"
|
||
}
|
||
],
|
||
"example": "const text = signal(''); mentions({ items: [{ name: 'zhangsan', label: '张三', description: '前端', keywords: ['zs'] }], value: text, placeholder: '输入 @ 叫人', onSelect: (it) => toast.info(it.label) })",
|
||
"steps": [
|
||
"在输入框里键入 @ 触发弹层,继续打字按 name / keywords 过滤(拼音、工号放 keywords,不出现在候选行里)",
|
||
"键盘:↑↓ 环形移动高亮、Enter 或 Tab 选中、Esc 只收这一层弹层(stopPropagation,不会连带关掉外层弹窗)",
|
||
"选中后组件按光标位置把 @片段替换成「@名字 + 空格」,光标落在其后,可以接着打字",
|
||
"受控用法:传 value 信号并在 onSelect / onInput 里读最新全文;外部改信号会同步回输入框,别同时再手写 input 事件",
|
||
"弹层贴在输入框下沿而非跟随光标行(多行输入里更稳定,也不会被行高算错),需要跟随光标请自绘",
|
||
"要限制可选人范围就在 items 上做过滤,组件不做远程搜索;远程场景用 onInput 拉数据后重建 items"
|
||
]
|
||
},
|
||
{
|
||
"id": "treeSelect",
|
||
"title": "树形选择 TreeSelect",
|
||
"demo": "sec-treeselect",
|
||
"props": [
|
||
{
|
||
"n": "nodes",
|
||
"t": "TreeNode[]",
|
||
"r": 1,
|
||
"d": "根节点列表,结构同 tree:key / label / children / disabled"
|
||
},
|
||
{
|
||
"n": "value",
|
||
"t": "Signal<string>",
|
||
"d": "单选选中 key;不传则内部维护"
|
||
},
|
||
{
|
||
"n": "checked",
|
||
"t": "Signal<string[]>",
|
||
"d": "多选勾选 key 集合(multi 时生效);不传则内部维护"
|
||
},
|
||
{
|
||
"n": "multi",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "多选模式:显示勾选框、父子级联、选中后弹层不收起"
|
||
},
|
||
{
|
||
"n": "searchable",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "弹层内带搜索框(按标签过滤并保留父链)"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"df": "请选择",
|
||
"d": "未选中时触发器文案"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "禁用整个触发器(不响应点击)"
|
||
},
|
||
{
|
||
"n": "onSelect",
|
||
"t": "(node: TreeNode) => void",
|
||
"k": 1,
|
||
"d": "单选选中回调(此时弹层已收起)"
|
||
},
|
||
{
|
||
"n": "onChange",
|
||
"t": "(keys: string[]) => void",
|
||
"k": 1,
|
||
"d": "多选勾选变更回调,传副本且父子已级联"
|
||
}
|
||
],
|
||
"example": "const dept = signal(''); treeSelect({ nodes: [{ key: 'tech', label: '技术中心', children: [{ key: 'fe', label: '前端组' }] }], value: dept, searchable: true, onSelect: (n) => toast.info(n.label) })",
|
||
"steps": [
|
||
"表单里选层级归属(部门 / 分类 / 区域)用它,不要用 cascader——cascader 要求逐级选到叶子,treeSelect 允许选中间节点,也允许任意深度",
|
||
"单选默认选中即收起;要连续看结构就传 multi,勾选后弹层保持展开,触发器回显「已选 N 项」",
|
||
"需要在大树里找人/找分类就开 searchable:搜索按标签过滤并保留命中节点的父链,路径不会断",
|
||
"Esc 与其他弹层共享键盘栈(只关最上层),点外部也会收起;展开时焦点自动落到搜索框",
|
||
"选中值只存 key(单选 value / 多选 checked 数组),显示名由 nodes 反查——改 label 不需要迁移数据",
|
||
"禁用单个节点用 nodes 里的 disabled(该节点不可选,子节点仍可选);禁用整个控件用 disabled"
|
||
]
|
||
},
|
||
{
|
||
"id": "link",
|
||
"title": "链接 Link",
|
||
"demo": "sec-link",
|
||
"props": [
|
||
{
|
||
"n": "href",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "目标地址:站内相对路径或绝对 URL"
|
||
},
|
||
{
|
||
"n": "label",
|
||
"t": "string",
|
||
"r": 1,
|
||
"d": "链接文案"
|
||
},
|
||
{
|
||
"n": "target",
|
||
"t": "'_self' | '_blank' | '_parent' | '_top'",
|
||
"d": "打开方式;缺省同窗口。传 _blank 会自动按站外处理(补 rel 与图标)"
|
||
},
|
||
{
|
||
"n": "external",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "站外链接:补 ↗ 图标、强制新窗口、加 rel=noopener noreferrer,并给读屏播报「在新窗口打开」"
|
||
},
|
||
{
|
||
"n": "muted",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "弱化色,用于次要链接与页脚"
|
||
},
|
||
{
|
||
"n": "disabled",
|
||
"t": "boolean",
|
||
"df": "false",
|
||
"d": "禁用:去掉 href、拦点击,保留可聚焦与 aria-disabled(仍能被 Tab 到,读屏能听出不可用)"
|
||
},
|
||
{
|
||
"n": "onClick",
|
||
"t": "(e: Event) => void",
|
||
"k": 1,
|
||
"d": "点击回调(禁用时不触发;可用于埋点,returnValue 不影响默认跳转)"
|
||
}
|
||
],
|
||
"example": "link({ href: '/docs', label: '查看文档' }); link({ href: 'https://example.com', label: '外部站点', external: true }); link({ href: '#', label: '说明页', muted: true })",
|
||
"steps": [
|
||
"站内跳转用相对路径、站外用 external: true:会自动补 ↗、target=_blank、rel=noopener noreferrer,并给读屏播报开了新窗口",
|
||
"页脚 / 次要说明用 muted 弱化;正文里的行内链接保持默认主色,与 typography 的 underline 区分用途(后者不跳转)",
|
||
"需要「点了先确认再跳」这类拦截,在 onClick 里做判断后自行处理,组件不接管默认行为(除禁用外都放行)",
|
||
"禁用态只是去掉 href 并保持 aria-disabled,不做视觉上的「像普通文字」处理——需要完全不可聚焦请在外层控制渲染",
|
||
"纯样式的「看起来像链接」用 typography({ underline: true }),不要为了样式引入带 href 的 a 标签"
|
||
]
|
||
},
|
||
{
|
||
"id": "avatarGroup",
|
||
"title": "头像组 AvatarGroup",
|
||
"demo": "sec-avatar",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "AvatarItem[]",
|
||
"r": 1,
|
||
"d": "成员列表:每项含 name(姓名)、src(图片地址)、variant(首字底色语义档)"
|
||
},
|
||
{
|
||
"n": "max",
|
||
"t": "number",
|
||
"df": "4",
|
||
"d": "最多展示几个,超出折叠成 +N(至少展示 1 个)"
|
||
},
|
||
{
|
||
"n": "size",
|
||
"t": "number",
|
||
"df": "32",
|
||
"d": "头像直径像素,与 avatar 的 size 同义"
|
||
}
|
||
],
|
||
"example": "avatarGroup({ items: [{ name: '张三' }, { name: '李四', variant: 'success' }, { name: '王五' }], max: 4 })",
|
||
"steps": [
|
||
"协作列表 / 评论头部展示参与人用它,比并排 avatar 省横向空间:叠放用负边距 + 底色描边切出入口",
|
||
"成员多时设 max:前 max 个照常显示,其余折叠成一个 +N,悬停任意头像都能看到全部姓名",
|
||
"只传 name 即首字占位,有图片就传 src;底色语义档用 variant(default / success / warning / danger)",
|
||
"整组是展示型元素:要有点击行为就在外面包 button 或 link,不要给单个头像加交互",
|
||
"size 与单个 avatar 一致,混排时把并排 avatar 与 avatarGroup 传同一个 size,视觉才对得上"
|
||
]
|
||
},
|
||
{
|
||
"id": "virtualList",
|
||
"title": "虚拟列表 VirtualList",
|
||
"demo": "sec-vlist",
|
||
"props": [
|
||
{
|
||
"n": "items",
|
||
"t": "unknown[]",
|
||
"r": 1,
|
||
"d": "全量数据(不切片,由组件按滚动位置取窗口)"
|
||
},
|
||
{
|
||
"n": "itemHeight",
|
||
"t": "number",
|
||
"r": 1,
|
||
"d": "每行固定高度像素;虚拟化前提是行高一致"
|
||
},
|
||
{
|
||
"n": "height",
|
||
"t": "number",
|
||
"r": 1,
|
||
"d": "容器可视高度像素,超出即容器内滚动"
|
||
},
|
||
{
|
||
"n": "overscan",
|
||
"t": "number",
|
||
"d": "上下各多渲染的缓冲条数;缺省 5,滚动更平滑",
|
||
"df": "5"
|
||
},
|
||
{
|
||
"n": "renderItem",
|
||
"t": "(item, index) => unknown",
|
||
"r": 1,
|
||
"d": "行内容渲染器:返回字符串 / DOM / 响应式值"
|
||
}
|
||
],
|
||
"example": "const vlist = virtualList({ items: users, itemHeight: 44, height: 320, renderItem: (u) => u.name }); vlist.scrollTo(500);",
|
||
"steps": [
|
||
"itemHeight 必须与每行真实高度一致:行高由组件钉死,行内容超高会溢出,请保持单行或内部滚动",
|
||
"height 是容器可视高度(像素),列表在容器内滚动,不会撑开页面",
|
||
"renderItem 第二参是原始下标(全量数据中的位置),不是窗口内序号",
|
||
"滚回某条用句柄 scrollTo(index),越界自动钳制;DOM 里只存在可视窗口的行,勿按全量下标查行节点",
|
||
"数据整体变化(换数据源)请销毁重建实例:items 是创建时快照,不响应外部数组突变",
|
||
"滚动事件内部已 rAF 合帧,滚动回调无需节流"
|
||
]
|
||
},
|
||
{
|
||
"id": "commandPalette",
|
||
"title": "命令面板 CommandPalette",
|
||
"demo": "sec-cmdk",
|
||
"props": [
|
||
{
|
||
"n": "open",
|
||
"t": "Signal<boolean>",
|
||
"r": 1,
|
||
"d": "显隐信号:外部写 true 唤起,组件内部关闭也会写回"
|
||
},
|
||
{
|
||
"n": "items",
|
||
"t": "CommandPaletteItem[]",
|
||
"r": 1,
|
||
"d": "全量命令,创建时快照;含 id / label / group / hint / keywords"
|
||
},
|
||
{
|
||
"n": "placeholder",
|
||
"t": "string",
|
||
"d": "搜索框占位文案;缺省「搜索命令…」"
|
||
},
|
||
{
|
||
"n": "emptyText",
|
||
"t": "string",
|
||
"d": "无匹配时的空状态文案;缺省「没有匹配的命令」"
|
||
},
|
||
{
|
||
"n": "hotkey",
|
||
"t": "boolean",
|
||
"d": "是否注册 ⌘K / Ctrl+K 全局唤起;缺省 true,多实例场景可关掉自己绑",
|
||
"df": "true"
|
||
},
|
||
{
|
||
"n": "onSelect",
|
||
"t": "(item) => void",
|
||
"r": 1,
|
||
"d": "回车或点击选中后回调:先收起并归还焦点,再回调"
|
||
}
|
||
],
|
||
"example": "const cmdk = commandPalette({ open: signal(false), items: [{ id: 'save', label: '保存当前页', group: '文件', hint: '⌘S' }, { id: 'invite', label: '邀请成员', group: '成员' }], onSelect: (it) => toast.info(`执行 ${it.label}`) }); document.body.append(cmdk.el); cmdk.open();",
|
||
"steps": [
|
||
"把返回的 el 挂到 body(面板是 fixed + 遮罩,挂在 section 里也定位正确),之后用 open 信号或句柄 open()/close() 控制显隐",
|
||
"items 是创建时快照:命令表变化(权限变化后菜单增减)请重建实例,不要直接改数组",
|
||
"过滤看 label / hint / keywords 三层:中文别名与拼音首字母塞进 keywords 即可支持,不必改组件",
|
||
"同组连续排列才合并成一段并画组标题;分组顺序按 items 顺序,组件不排序",
|
||
"键盘:↑↓ 环形选择、Home/End 跳首尾、回车执行、Esc 交给弹层栈(嵌套弹窗时只关最上层);鼠标划过即高亮",
|
||
"hotkey 缺省注册 ⌘K / Ctrl+K,同一页面多个面板请只留一个开 hotkey,其余传 hotkey: false"
|
||
]
|
||
}
|
||
]
|
||
}
|
||
} |