iter(g110): 下拉选择支持清空

This commit is contained in:
HIsheR
2026-10-26 14:03:19 +08:00
parent 9abadf0c4d
commit e042143468
9 changed files with 84 additions and 10 deletions
+19 -1
View File
@@ -24,6 +24,13 @@ export function select(props: SelectProps): HTMLElement {
props.onChange?.(v);
};
/*** 清空:可选筛选项回到「未选」态,信号与回调同源写回 ***/
const clear = () => {
if (!value()) return; // 卫语句:本来就没选,不重复派发回调
value('');
props.onChange?.('');
};
// —— 触发器 ——
const trigger = h(
'button',
@@ -72,7 +79,17 @@ export function select(props: SelectProps): HTMLElement {
blank,
);
const root = h('div', { class: () => `ua-select${open() ? ' ua-open' : ''}` }, trigger, pop); // 根带开合类,供箭头旋转
// —— 清空按钮:clearable 且有值时出现,绝对定位压在触发器右缘(与箭头错开)——
const clearBtn = h('button', {
type: 'button',
class: 'ua-select-clear',
'aria-label': '清空选择',
tabindex: '-1', // 不进 Tab 序:清空是鼠标 / 触屏便利,键盘用 Delete / Backspace
onClick: () => clear(),
}, '×');
effect(() => { clearBtn.hidden = !props.clearable || !value(); }); // 无值或未开启时藏起来(不进 Tab 序,隐藏即失效)
const root = h('div', { class: () => `ua-select${open() ? ' ua-open' : ''}` }, trigger, props.clearable ? clearBtn : null, pop); // 根带开合类,供箭头旋转
// —— 键入快跳缓冲(非 searchable 时生效,逻辑与 menu 一致)——
let typed = ''; // 最近键入的字母串,用于前缀匹配
@@ -90,6 +107,7 @@ export function select(props: SelectProps): HTMLElement {
root.addEventListener('keydown', (e: KeyboardEvent) => {
const list = visibleIdx();
if (props.clearable && !open() && (e.key === 'Backspace' || e.key === 'Delete')) { e.preventDefault(); clear(); return; } // 键盘等价:收起态下 Delete / Backspace 清空
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault(); // 阻止页面滚动
if (!open()) { open(true); options[list[0] ?? 0]?.focus(); return; } // 未展开:展开并聚焦首个可见选项
+1
View File
@@ -108,6 +108,7 @@ export interface SelectProps {
placeholder?: string; // 未选中时的占位项文案
disabled?: boolean | Signal<boolean>; // 禁用态:布尔直接置位,传信号则动态同步
searchable?: boolean; // 弹层顶部带搜索框,按标签包含匹配过滤;缺省 false
clearable?: boolean; // 有值时触发器右缘出现清空按钮(删除 / Backspace 亦可清);缺省 false
onChange?: (value: string) => void; // 变更回调
}
+8 -2
View File
@@ -20,7 +20,7 @@
"slider(props):滑块,value 信号双向绑定,min/max/step 可配,原生 range 承载键盘交互,右侧可显示当前值",
"checkbox(props):复选框,checked 信号双向绑定,disabled 支持布尔或信号禁用",
"switchButton(props):开关,checked 信号双向绑定,disabled 支持布尔或信号禁用",
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用,空间不足自动翻转,键入字母快跳选项;searchable 开启弹层搜索框",
"select(props):自绘下拉选择(非原生 select),value 信号双向绑定,点外部 / Esc 关闭,选项弹层带微动画,disabled 支持布尔或信号禁用,空间不足自动翻转,键入字母快跳选项;searchable 开启弹层搜索框,clearable 有值时给清空按钮",
"menu(props):下拉菜单,触发器 + 自绘弹层,条目支持禁用 / 危险 / 分隔线,↑↓ 环形导航,点外部 / Esc 关闭,空间不足自动翻转,键入字母快跳菜单项",
"flip(trigger, panel, preferred):浮层上下翻转判定,按触发器与面板的视口几何决定 preferred 方位放不下时是否翻转,返回实际生效方位 'top' | 'bottom'",
"radioGroup(props):单选组,options + value 信号双向绑定,disabled 整组禁用",
@@ -535,6 +535,11 @@
"t": "boolean",
"d": "弹层顶部带搜索框,按标签包含匹配过滤;缺省 false"
},
{
"n": "clearable",
"t": "boolean",
"d": "有值时触发器右缘出现清空按钮(收起态按 Delete / Backspace 也能清);缺省 false,用于「可选筛选项」"
},
{
"n": "disabled",
"t": "boolean | Signal<boolean>",
@@ -554,7 +559,8 @@
"点击触发器展开弹层,点选项写回信号并收起;点外部或按 Esc 也会收起",
"选中项在弹层中用淡色弱化标记,不抢视觉焦点",
"弹层展开时按 Esc 只收起下拉并回焦触发器;若下拉在弹窗/抽屉内,再按一次 Esc 才关外层(逐层关闭)",
"searchable: true 时打开即聚焦搜索框,输入实时过滤;↑↓/Home/End 只在可见项间移动,无命中显示「无匹配选项」"
"searchable: true 时打开即聚焦搜索框,输入实时过滤;↑↓/Home/End 只在可见项间移动,无命中显示「无匹配选项」",
"需要允许「不选」的筛选项就开 clearable:有值时右缘出现 ×,点它把值写成空串并回调 onChange(''),键盘在收起态按 Delete / Backspace 等价"
]
},
{
+33
View File
@@ -3751,3 +3751,36 @@ button.ua-step-dot:hover {
.ua-form-summary[hidden] {
display: none;
}
/* —— 下拉清空按钮 —— */
.ua-select-clear {
position: absolute;
top: 50%;
right: 24px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
padding: 0;
border: none;
border-radius: 50%;
background: none;
color: var(--ua-color-text-muted);
font-size: var(--ua-font-size-md);
line-height: 1;
cursor: pointer;
transform: translateY(-50%);
transition: color 0.15s, background-color 0.15s;
}
.ua-select-clear:hover {
color: var(--ua-color-text);
background: color-mix(in srgb, var(--ua-color-accent) 12%, transparent);
}
.ua-select-clear:focus-visible {
outline: 2px solid var(--ua-color-accent);
outline-offset: 1px;
}
.ua-select-clear[hidden] {
display: none;
}