iter: d02 select/radioGroup 补 disabled + 修复 select 箭头旋转
This commit is contained in:
@@ -64,6 +64,14 @@
|
||||
"acceptance": "packages/skin/ui 新增 menu.ts:触发器 + 自绘弹层,items 支持 disabled / danger / divider,点条目 / 点外部 / Esc 关闭,↑↓ 环形导航(禁用项跳过),Enter/Space 选中,Esc 收起后焦点回触发器;只消费 --ua-* 令牌并带 prefers-reduced-motion 兜底;导出 + manifest + 文档 + llms-full.txt + demo 同步;npm run check 全绿",
|
||||
"status": "done",
|
||||
"notes": "API menu({ label, items, variant, align, onSelect });MenuItem = { value, label, disabled?, danger?, divider? };弹层 ua-open 同时挂在根与弹层(根管箭头旋转、弹层管出入场),危险条目取 danger 色;键盘 ↑↓ 环形导航 + Esc 回焦触发器;demo 新增 sec-menu 双示例(含禁用/危险/分隔线与右对齐)"
|
||||
},
|
||||
{
|
||||
"id": "d02",
|
||||
"title": "select / radioGroup 补 disabled,修复 select 箭头旋转失效",
|
||||
"motivation": "c01/c02 已覆盖 button/checkbox/switch/输入类,选择类仍缺禁用;同时发现 select 的 .ua-select.ua-open 选择器永不命中(ua-open 只挂在弹层),展开时箭头不旋转",
|
||||
"acceptance": "SelectProps / RadioGroupProps 新增可选 disabled(boolean | Signal<boolean>);禁用时 select 触发器置灰且不展开弹层,radioGroup 整组置灰不可选;select 箭头在展开时旋转 180°;demo 加信号禁用与静态禁用示例;manifest/文档/llms-full.txt 同步;npm run check 全绿",
|
||||
"status": "done",
|
||||
"notes": "disabled 仍走 h() 透传(原生 button/input 拦截交互)零辅助代码;select 根节点补 class 函数加 ua-open,箭头旋转选择器命中;CSS 通用禁用态列表并入 .ua-select-trigger 与 .ua-radio,另加 :has() 让单选行整行弱化、禁用触发器 hover 不改边框色"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+15
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "ui-arch-registry-index/0.1",
|
||||
"generatedAt": "2026-10-23T08:45:45.013Z",
|
||||
"generatedAt": "2026-10-23T08:46:56.936Z",
|
||||
"repoRoot": ".",
|
||||
"packages": [
|
||||
{
|
||||
@@ -147,9 +147,9 @@
|
||||
"textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号禁用",
|
||||
"checkbox(props):复选框,checked 信号双向绑定,disabled 支持布尔或信号禁用",
|
||||
"switchButton(props):开关,checked 信号双向绑定,disabled 支持布尔或信号禁用",
|
||||
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画",
|
||||
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用",
|
||||
"menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭",
|
||||
"radioGroup(props):单选组,options + value 信号双向绑定",
|
||||
"radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用",
|
||||
"card(props, ...children):卡片容器",
|
||||
"badge(props):徽标,variant = default | success | warning | danger",
|
||||
"alert(props):提示条,variant = info | success | warning | danger",
|
||||
@@ -409,6 +409,11 @@
|
||||
"t": "string",
|
||||
"d": "未选中时触发器显示的占位文案"
|
||||
},
|
||||
{
|
||||
"n": "disabled",
|
||||
"t": "boolean | Signal<boolean>",
|
||||
"d": "禁用态:布尔直接置位,传信号则动态同步;禁用时触发器置灰且不展开弹层"
|
||||
},
|
||||
{
|
||||
"n": "onChange",
|
||||
"t": "(value: string) => void",
|
||||
@@ -485,6 +490,11 @@
|
||||
"t": "Signal<string>",
|
||||
"d": "选中值信号,双向绑定"
|
||||
},
|
||||
{
|
||||
"n": "disabled",
|
||||
"t": "boolean | Signal<boolean>",
|
||||
"d": "整组禁用:全部选项与文案置灰且不可选"
|
||||
},
|
||||
{
|
||||
"n": "onChange",
|
||||
"t": "(value: string) => void",
|
||||
@@ -496,7 +506,8 @@
|
||||
"steps": [
|
||||
"创建字符串信号,初值设为默认选项的 value",
|
||||
"传入 options 与 value,组内自动互斥",
|
||||
"读取 size() 获取当前选中值"
|
||||
"读取 size() 获取当前选中值",
|
||||
"需要整组禁用时传 disabled:布尔值静态禁用,也可传信号(如 locked)实现动态锁定"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
{"time":"2026-10-23T16:08:17+08:00","id":"c01","result":"done","summary":"button/checkbox/switchButton 补 disabled(boolean|Signal),复用 h() 布尔/信号既有语义零辅助代码;CSS 通用禁用态 + demo 信号联动示例;check 全绿"}
|
||||
{"time":"2026-10-23T16:10:31+08:00","id":"c02","result":"done","summary":"textField/textarea 补 disabled(boolean|Signal),与 c01 同模式;禁用态 CSS 覆盖输入类;demo locked 信号联动;check 全绿"}
|
||||
{"time": "2026-10-23T16:55:00+08:00", "id": "d01", "result": "done", "summary": "新增下拉菜单 menu:触发器 + 自绘弹层,disabled/danger/divider 条目,↑↓ 环形导航 + Esc 回焦,reduced-motion 兜底;manifest/llms-full/demo 同步,check 全绿"}
|
||||
{"time": "2026-10-23T17:02:00+08:00", "id": "d02", "result": "done", "summary": "select/radioGroup 补 disabled(布尔|信号),并修复 select 根节点缺 ua-open 导致箭头不旋转的既有缺陷;CSS :has() 行级弱化;demo/manifest/llms 同步,check 全绿"}
|
||||
|
||||
@@ -120,6 +120,13 @@ const app = h(
|
||||
switchButton({ label: '锁定', checked: locked }), // 关掉开关即解锁上面的勾选框与输入框
|
||||
textField({ placeholder: '锁定后不可输入…', disabled: locked }),
|
||||
),
|
||||
h(
|
||||
'div',
|
||||
{ class: 'row' },
|
||||
select({ options: cities.map((c) => ({ value: c, label: c })), value: city, placeholder: '信号禁用的下拉…', disabled: locked }),
|
||||
radioGroup({ options: [{ value: 's', label: '小' }, { value: 'm', label: '中' }], value: size, disabled: locked }),
|
||||
radioGroup({ options: [{ value: 'x', label: '静态禁用' }], value: size, disabled: true }),
|
||||
),
|
||||
),
|
||||
section(
|
||||
'sec-feedback',
|
||||
|
||||
+6
-3
@@ -1,6 +1,6 @@
|
||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||
|
||||
> 自动生成于 2026-10-23T08:45:45.013Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 自动生成于 2026-10-23T08:46:56.936Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||
|
||||
## 快速上手
|
||||
@@ -165,6 +165,7 @@ const night = signal(true); switchButton({ label: '夜间模式', checked: night
|
||||
- options(必填):OptionItem[] —— 选项列表,每项 { value, label }
|
||||
- value:Signal<string> —— 选中值信号,双向绑定
|
||||
- placeholder:string —— 未选中时触发器显示的占位文案
|
||||
- disabled:boolean | Signal<boolean> —— 禁用态:布尔直接置位,传信号则动态同步;禁用时触发器置灰且不展开弹层
|
||||
- onChange(回调):(value: string) => void —— 选中回调
|
||||
|
||||
示例:
|
||||
@@ -214,6 +215,7 @@ menu({ label: '操作', items: [{ value: 'edit', label: '编辑' }, { value: 'de
|
||||
|
||||
- options(必填):OptionItem[] —— 选项列表
|
||||
- value:Signal<string> —— 选中值信号,双向绑定
|
||||
- disabled:boolean | Signal<boolean> —— 整组禁用:全部选项与文案置灰且不可选
|
||||
- onChange(回调):(value: string) => void —— 变更回调
|
||||
|
||||
示例:
|
||||
@@ -227,6 +229,7 @@ const size = signal('m'); radioGroup({ options: [{ value: 's', label: '小' }, {
|
||||
1. 创建字符串信号,初值设为默认选项的 value
|
||||
2. 传入 options 与 value,组内自动互斥
|
||||
3. 读取 size() 获取当前选中值
|
||||
4. 需要整组禁用时传 disabled:布尔值静态禁用,也可传信号(如 locked)实现动态锁定
|
||||
|
||||
现场演示:apps/docs/demo.html#sec-form
|
||||
|
||||
@@ -542,9 +545,9 @@ API 速览:
|
||||
- textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号禁用
|
||||
- checkbox(props):复选框,checked 信号双向绑定,disabled 支持布尔或信号禁用
|
||||
- switchButton(props):开关,checked 信号双向绑定,disabled 支持布尔或信号禁用
|
||||
- select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画
|
||||
- select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用
|
||||
- menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭
|
||||
- radioGroup(props):单选组,options + value 信号双向绑定
|
||||
- radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用
|
||||
- card(props, ...children):卡片容器
|
||||
- badge(props):徽标,variant = default | success | warning | danger
|
||||
- alert(props):提示条,variant = info | success | warning | danger
|
||||
|
||||
@@ -52,9 +52,9 @@
|
||||
- textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号禁用
|
||||
- checkbox(props):复选框,checked 信号双向绑定,disabled 支持布尔或信号禁用
|
||||
- switchButton(props):开关,checked 信号双向绑定,disabled 支持布尔或信号禁用
|
||||
- select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画
|
||||
- select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用
|
||||
- menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭
|
||||
- radioGroup(props):单选组,options + value 信号双向绑定
|
||||
- radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用
|
||||
- card(props, ...children):卡片容器
|
||||
- badge(props):徽标,variant = default | success | warning | danger
|
||||
- alert(props):提示条,variant = info | success | warning | danger
|
||||
|
||||
@@ -6,7 +6,12 @@ export function radioGroup(props: RadioGroupProps): HTMLElement {
|
||||
const groupName = `ua-radio-${Math.random().toString(36).slice(2, 8)}`; // 组内唯一 name
|
||||
const radios: HTMLInputElement[] = [];
|
||||
for (const opt of props.options) {
|
||||
const input = h('input', { type: 'radio', name: groupName, class: 'ua-radio' }) as HTMLInputElement;
|
||||
const input = h('input', {
|
||||
type: 'radio',
|
||||
name: groupName,
|
||||
class: 'ua-radio',
|
||||
disabled: props.disabled, // 布尔或信号禁用,h() 直接透传
|
||||
}) as HTMLInputElement;
|
||||
input.value = opt.value;
|
||||
radios.push(input);
|
||||
input.addEventListener('change', () => {
|
||||
|
||||
@@ -24,6 +24,7 @@ export function select(props: SelectProps): HTMLElement {
|
||||
class: 'ua-select-trigger',
|
||||
'aria-haspopup': 'listbox',
|
||||
'aria-expanded': () => String(open()),
|
||||
disabled: props.disabled, // 布尔或信号禁用,h() 直接透传(原生 button 拦截点击)
|
||||
onClick: () => open(!open()),
|
||||
},
|
||||
h('span', { class: () => `ua-select-value${value() ? '' : ' is-empty'}` }, () => current() || props.placeholder || ''),
|
||||
@@ -46,7 +47,7 @@ export function select(props: SelectProps): HTMLElement {
|
||||
);
|
||||
const pop = h('div', { class: () => `ua-select-pop${open() ? ' ua-open' : ''}`, role: 'listbox' }, ...options);
|
||||
|
||||
const root = h('div', { class: 'ua-select' }, trigger, pop);
|
||||
const root = h('div', { class: () => `ua-select${open() ? ' ua-open' : ''}` }, trigger, pop); // 根带开合类,供箭头旋转
|
||||
|
||||
root.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') open(false); // Esc 关闭弹层
|
||||
|
||||
@@ -67,6 +67,7 @@ export interface SelectProps {
|
||||
options: OptionItem[]; // 选项列表
|
||||
value?: Signal<string>; // 选中值信号,双向绑定
|
||||
placeholder?: string; // 未选中时的占位项文案
|
||||
disabled?: boolean | Signal<boolean>; // 禁用态:布尔直接置位,传信号则动态同步
|
||||
onChange?: (value: string) => void; // 变更回调
|
||||
}
|
||||
|
||||
@@ -74,6 +75,7 @@ export interface SelectProps {
|
||||
export interface RadioGroupProps {
|
||||
options: OptionItem[]; // 选项列表
|
||||
value?: Signal<string>; // 选中值信号,双向绑定
|
||||
disabled?: boolean | Signal<boolean>; // 禁用态:整组置灰且不可选
|
||||
onChange?: (value: string) => void; // 变更回调
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
"textarea(props):多行输入,value 信号双向绑定,disabled 支持布尔或信号禁用",
|
||||
"checkbox(props):复选框,checked 信号双向绑定,disabled 支持布尔或信号禁用",
|
||||
"switchButton(props):开关,checked 信号双向绑定,disabled 支持布尔或信号禁用",
|
||||
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画",
|
||||
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用",
|
||||
"menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭",
|
||||
"radioGroup(props):单选组,options + value 信号双向绑定",
|
||||
"radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用",
|
||||
"card(props, ...children):卡片容器",
|
||||
"badge(props):徽标,variant = default | success | warning | danger",
|
||||
"alert(props):提示条,variant = info | success | warning | danger",
|
||||
@@ -280,6 +280,11 @@
|
||||
"t": "string",
|
||||
"d": "未选中时触发器显示的占位文案"
|
||||
},
|
||||
{
|
||||
"n": "disabled",
|
||||
"t": "boolean | Signal<boolean>",
|
||||
"d": "禁用态:布尔直接置位,传信号则动态同步;禁用时触发器置灰且不展开弹层"
|
||||
},
|
||||
{
|
||||
"n": "onChange",
|
||||
"t": "(value: string) => void",
|
||||
@@ -356,6 +361,11 @@
|
||||
"t": "Signal<string>",
|
||||
"d": "选中值信号,双向绑定"
|
||||
},
|
||||
{
|
||||
"n": "disabled",
|
||||
"t": "boolean | Signal<boolean>",
|
||||
"d": "整组禁用:全部选项与文案置灰且不可选"
|
||||
},
|
||||
{
|
||||
"n": "onChange",
|
||||
"t": "(value: string) => void",
|
||||
@@ -367,7 +377,8 @@
|
||||
"steps": [
|
||||
"创建字符串信号,初值设为默认选项的 value",
|
||||
"传入 options 与 value,组内自动互斥",
|
||||
"读取 size() 获取当前选中值"
|
||||
"读取 size() 获取当前选中值",
|
||||
"需要整组禁用时传 disabled:布尔值静态禁用,也可传信号(如 locked)实现动态锁定"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+12
-1
@@ -852,9 +852,20 @@
|
||||
/* —— 通用禁用态:置灰 + 禁用光标 —— */
|
||||
.ua-btn:disabled,
|
||||
.ua-checkbox:disabled,
|
||||
.ua-radio:disabled,
|
||||
.ua-switch-input:disabled,
|
||||
.ua-input:disabled,
|
||||
.ua-textarea:disabled {
|
||||
.ua-textarea:disabled,
|
||||
.ua-select-trigger:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
/* 禁用时悬停不改边框色,避免误以为可交互 */
|
||||
.ua-select-trigger:disabled:hover {
|
||||
border-color: var(--ua-color-border);
|
||||
}
|
||||
/* 单选组禁用:整行文案同步弱化 */
|
||||
.ua-check-row:has(.ua-radio:disabled) {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user