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)
This commit is contained in:
shawn
2026-10-23 14:58:10 +08:00
committed by HIsheR
parent a186d71412
commit 0de9b6e66e
59 changed files with 5293 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
import { effect, h, signal } from '@ui-arch/frame';
import type { SelectProps } from './types.js';
/*** 下拉选择:自绘弹层替代原生 select,value 信号双向绑定;点选项 / 点外部 / Esc 关闭 ***/
export function select(props: SelectProps): HTMLElement {
// —— 状态集中声明 ——
const open = signal(false); // 弹层开合
const value = props.value ?? signal(''); // 选中值;未传信号则内部持有
const labelOf = (v: string) => props.options.find((o) => o.value === v)?.label ?? '';
const current = () => labelOf(value()); // 触发器显示文案(响应式)
const pick = (v: string) => {
value(v);
open(false);
props.onChange?.(v);
};
// —— 触发器 ——
const trigger = h(
'button',
{
type: 'button',
class: 'ua-select-trigger',
'aria-haspopup': 'listbox',
'aria-expanded': () => String(open()),
onClick: () => open(!open()),
},
h('span', { class: () => `ua-select-value${value() ? '' : ' is-empty'}` }, () => current() || props.placeholder || ''),
h('span', { class: 'ua-select-arrow' }, '▾'),
);
// —— 选项弹层 ——
const options = props.options.map((opt) =>
h(
'button',
{
type: 'button',
class: () => `ua-option${opt.value === value() ? ' ua-option-selected' : ''}`,
role: 'option',
'aria-selected': () => String(opt.value === value()),
onClick: () => pick(opt.value),
},
opt.label,
),
);
const pop = h('div', { class: () => `ua-select-pop${open() ? ' ua-open' : ''}`, role: 'listbox' }, ...options);
const root = h('div', { class: 'ua-select' }, trigger, pop);
root.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Escape') open(false); // Esc 关闭弹层
});
document.addEventListener('click', (e) => {
if (root.contains(e.target as Node)) return; // 卫语句:组件内点击不处理
open(false); // 点外部关闭
});
effect(() => open()); // 建立依赖,保证开合信号生效
return root;
}