Files
shawn 0de9b6e66e init: @ui-arch 私人组件库 monorepo
- core/table-core: Headless 表格内核(排序/筛选/分页/选择插件 + 冒烟 9 项)
- core/frame: signal/computed/effect + hyperscript 微框架(零依赖)
- skin/ui: 16 个 DOM 组件(含自绘 select、滑动指示条 tabs、行选择 dataTable、form 表单校验)
- design/tokens: --ua-* 设计令牌(light/dark)
- tools/cli: ua 命令行(index/list/info/docs/style/mcp)+ MCP 注册表服务
- apps: demo 演示站(esbuild 单文件)+ docs 双轨文档(人类版 + llms-full.txt)
2026-10-23 14:58:53 +08:00

165 lines
6.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { h, mount, signal } from '@ui-arch/frame';
import {
alert,
badge,
button,
card,
checkbox,
dataTable,
form,
formItem,
modal,
progress,
radioGroup,
select,
spinner,
switchButton,
tabs,
textarea,
textField,
} from '@ui-arch/ui';
/*** 生成演示区块;id 供文档站 iframe 锚点定位 ***/
function section(id: string, title: string, desc: string, ...body: unknown[]): HTMLElement {
return h(
'section',
{ class: 'sec', id },
h('h2', null, title),
h('p', { class: 'desc' }, desc),
h('div', { class: 'stage' }, ...body),
);
}
// —— 演示数据 ——
const cities = ['北京', '上海', '深圳', '杭州'];
const users = [
'Alice', 'Bob', 'Charlie', 'Dave', 'Eve', 'Frank', 'Grace', 'Hank',
'Ivy', 'Jack', 'Kate', 'Liam', 'Mia', 'Noah', 'Olivia', 'Paul',
'Quinn', 'Rose', 'Sam', 'Tina', 'Uma', 'Victor', 'Wendy', 'Xavier',
].map((name, i) => ({ id: i + 1, name, age: 22 + ((i * 7) % 20), city: cities[i % cities.length] }));
// —— 响应式状态演示 ——
const count = signal(0);
// —— 双向绑定演示 ——
const name = signal('');
// —— 表单控件状态集中声明 ——
const agreed = signal(false);
const nightMode = signal(true);
const city = signal('');
const size = signal('m');
const bio = signal('');
// —— 反馈组件状态 ——
const percent = signal(0);
setInterval(() => percent((percent() + 5) % 105), 400); // 自动推进演示进度
// —— 标签页 / 弹窗状态 ——
const activeTab = signal('a');
const modalOpen = signal(false);
// —— 表单校验状态集中声明 ——
const username = signal('');
const email = signal('');
const formResult = signal('');
const app = h(
'div',
{ class: 'page' },
h(
'header',
null,
h('h1', null, '@ui-arch 组件库 · 现场演示'),
h('p', { class: 'sub' }, '驱动:@ui-arch/frame(自研微框架) + @ui-arch/ui(组件库) + @ui-arch/table-core(表格内核)'),
),
section(
'sec-counter',
'响应式状态',
'signal 驱动:按钮修改信号,文本自动更新,无虚拟 DOM。',
button({ label: 'count +1', onClick: () => count(count() + 1) }),
button({ label: '重置', variant: 'ghost', onClick: () => count(0) }),
h('span', { class: 'out' }, () => `count = ${count()}`),
),
section(
'sec-bind',
'双向绑定',
'textField 与信号双向同步:输入即回显。',
textField({ placeholder: '输入名字…', value: name }),
h('span', { class: 'out' }, () => (name() ? `你好,${name()}!` : '等待输入…')),
),
section(
'sec-form',
'表单控件',
'checkbox / switch / select / radioGroup / textarea:状态一律由调用方信号持有。',
h('div', { class: 'row' }, checkbox({ label: '同意条款', checked: agreed }), h('span', { class: 'out' }, () => (agreed() ? '已同意' : '未同意'))),
h('div', { class: 'row' }, switchButton({ label: '夜间模式', checked: nightMode }), h('span', { class: 'out' }, () => (nightMode() ? '开' : '关'))),
h('div', { class: 'row' }, select({ options: cities.map((c) => ({ value: c, label: c })), value: city, placeholder: '选择城市…' }), h('span', { class: 'out' }, () => (city() ? `城市:${city()}` : '未选择'))),
h('div', { class: 'row' }, radioGroup({ options: [{ value: 's', label: '小' }, { value: 'm', label: '中' }, { value: 'l', label: '大' }], value: size })),
textarea({ placeholder: '一句话介绍…', value: bio, rows: 2 }),
h('span', { class: 'out' }, () => `简介 ${bio().length} 字`),
),
section(
'sec-feedback',
'反馈与提示',
'badge / alert / spinner / progress:语义色全部来自令牌,暗色自动切换。',
badge({ label: '默认' }),
badge({ label: '成功', variant: 'success' }),
badge({ label: '警告', variant: 'warning' }),
badge({ label: '危险', variant: 'danger' }),
alert({ message: '信息提示:这是一条 info 提示条。' }),
alert({ message: '警告提示:操作可能存在风险。', variant: 'warning' }),
h('div', { class: 'row' }, spinner(), spinner({ size: 26 })),
progress({ value: percent }),
),
section(
'sec-formlab',
'表单校验',
'form + formItem:必填 / 长度 / 正则校验,全部通过才触发提交;失焦校验、输入清错。',
form(
{ submitText: '注册', onSubmit: () => formResult(`提交成功:${username()} / ${email()}`) },
[
formItem({ label: '用户名', required: true, value: username, rules: [{ min: 2, max: 12, message: '用户名需 2-12 个字符' }] }, textField({ placeholder: '2-12 个字符', value: username })),
formItem({ label: '邮箱', required: true, value: email, rules: [{ pattern: /^[\w.-]+@[\w-]+\.[\w.]+$/, message: '邮箱格式不正确' }] }, textField({ placeholder: 'you@example.com', value: email })),
],
),
h('span', { class: 'out' }, () => formResult() || '等待提交…'),
),
section(
'sec-tabs-modal',
'标签页与弹窗',
'tabs 滑动指示条 + 方向键切换(←→),禁用页签置灰;modal 支持 Esc / 遮罩 / × 关闭。',
tabs({ tabs: [{ value: 'a', label: '概览' }, { value: 'b', label: '明细' }, { value: 'c', label: '设置' }, { value: 'd', label: '已停用', disabled: true }], active: activeTab }),
h('span', { class: 'out' }, () => `当前页签:${activeTab()}`),
button({ label: '打开弹窗', onClick: () => modalOpen(true) }),
modal(
{ title: '确认操作', open: modalOpen, onClose: () => modalOpen(false) },
h('p', null, '这是一个由信号驱动的弹窗组件。'),
h('div', { class: 'row' }, button({ label: '知道了', onClick: () => modalOpen(false) }), badge({ label: 'modal', variant: 'success' })),
),
),
section(
'sec-table',
'数据表格',
'粘性表头、斑马纹、行选择(全选三态 + 已选计数,选中行低视觉比重)、点表头排序、工具栏筛选、底部分页。',
card({ title: '用户表' }, dataTable({
data: users,
selectable: true,
columns: [
{ key: 'name', header: '姓名', sortable: true },
{ key: 'age', header: '年龄', sortable: true },
{ key: 'city', header: '城市' },
],
pageSize: 5,
})),
),
section(
'sec-card',
'卡片容器',
'card 组件:标题 + 任意子内容,样式全部来自设计令牌。',
card({ title: '令牌驱动' }, h('p', { class: 'desc' }, '这里没有硬编码颜色 —— 全部消费 --ua-* CSS 变量,暗色模式自动切换。')),
),
);
mount('#app', () => app);