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, toast, tooltip, } 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-tooltip', '悬浮提示', 'tooltip 包裹触发器:hover / 聚焦延迟 150ms 出场,移出、失焦或 Esc 收起,气泡不抢指针。', h( 'div', { class: 'row' }, tooltip({ content: '保存后立即生效' }, button({ label: '保存' })), tooltip({ content: '该操作不可撤销', placement: 'bottom' }, button({ label: '删除', variant: 'danger' })), tooltip({ content: '聚焦输入框同样会显示' }, textField({ placeholder: '聚焦我…' })), ), ), section( 'sec-toast', '消息提示', 'toast 右上角堆叠弹出,默认 3s 自动淡出移除;语义色四档,出入场微动画。', h( 'div', { class: 'row' }, button({ label: '成功', onClick: () => toast.success('保存成功') }), button({ label: '错误', variant: 'danger', onClick: () => toast.error('网络请求失败') }), button({ label: '警告', onClick: () => toast.warning('磁盘空间不足') }), button({ label: '信息(1.5s)', onClick: () => toast.info('已同步云端', 1500) }), ), ), 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);