iter(g216): 时间选择支持清空

This commit is contained in:
HIsheR
2026-10-26 19:57:20 +08:00
parent 050534a07f
commit 17da5618f5
6 changed files with 67 additions and 4 deletions
+26 -1
View File
@@ -28,6 +28,14 @@ export function timePicker(props: TimePickerProps): HTMLElement {
props.onChange?.(next);
};
/*** 清空:回到「未选」态,收起弹层并写回空串(与 select / multiSelect 的清空语义一致) ***/
const clear = () => {
if (!value()) return; // 卫语句:本来就没选,不重复派发回调
value('');
props.onChange?.('');
open(false);
};
/*** 列内聚焦导航:↑↓ 环形移动、Home/End 直达首末(列内按钮顺序即时间顺序) ***/
const navigate = (col: HTMLElement, cells: HTMLButtonElement[]) => {
col.addEventListener('keydown', (e: KeyboardEvent) => {
@@ -82,13 +90,23 @@ export function timePicker(props: TimePickerProps): HTMLElement {
h('span', { class: 'ua-timepicker-arrow', 'aria-hidden': 'true' }, '▾'),
);
// —— 清空按钮:clearable 且有值时出现,压在触发器右缘(与箭头错开)——
const clearable = props.clearable ?? false; // 缺省不带清空:没选的场景多,按钮会白占位置
const clearBtn = h('button', {
type: 'button',
class: 'ua-timepicker-clear',
'aria-label': '清空时间',
tabindex: '-1', // 不进 Tab 序:清空是鼠标 / 触屏便利,键盘用 Delete / Backspace
onClick: () => clear(),
}, '×') as HTMLElement;
const pop = h(
'div',
{ class: () => `ua-timepicker-pop${open() ? ' ua-open' : ''}${place() === 'top' ? ' is-flip' : ''}`, role: 'dialog', 'aria-label': '选择时间' },
hourCol.col,
minuteCol.col,
);
const root = h('div', { class: () => `ua-timepicker${open() ? ' is-open' : ''}` }, trigger, pop);
const root = h('div', { class: () => `ua-timepicker${open() ? ' is-open' : ''}${props.clearable && value() ? ' has-clear' : ''}` }, trigger, clearable ? clearBtn : null, pop);
/*** 把激活格滚到列中部:列是独立滚动容器,直接算 scrollTop 不惊动页面 ***/
const reveal = (col: HTMLElement, cells: HTMLButtonElement[]) => {
@@ -98,6 +116,13 @@ export function timePicker(props: TimePickerProps): HTMLElement {
col.scrollTop = Math.max(0, target);
};
effect(() => { clearBtn.hidden = !clearable || !value(); }); // 无值或未开启时藏起来(hidden 一并移出点击区)
root.addEventListener('keydown', (e: KeyboardEvent) => {
if (open() || !clearable) return; // 卫语句:展开时按键归列内导航,未开启清空则不接管
if (e.key !== 'Backspace' && e.key !== 'Delete') return; // 卫语句:只接管这两个键
e.preventDefault();
clear(); // 键盘等价:不必伸手去点 ×
});
bindOutside(root, open, () => open(false)); // 点外部收起:仅展开期间挂监听
effect(() => {
if (open()) {
+1
View File
@@ -670,6 +670,7 @@ export interface TimePickerProps {
value?: Signal<string>; // 选中时间(HH:mm),未传则组件内部持有
minuteStep?: number; // 分钟步长;缺省 5,超过 30 按 30 处理
placeholder?: string; // 未选时占位文案;缺省「请选择时间」
clearable?: boolean; // 显示清空按钮 ×(有值时才出现);缺省 false,键盘 Delete / Backspace 等价
disabled?: boolean | Signal<boolean>; // 禁用触发器
onChange?: (time: string) => void; // 写回回调(选时也会触发,分默认落到首个步长值)
}
+7 -1
View File
@@ -3669,6 +3669,11 @@
"t": "number",
"d": "分钟步长;缺省 5,超过 30 按 30 处理(如 15 只有 00/15/30/45)"
},
{
"n": "clearable",
"t": "boolean",
"d": "显示清空按钮 ×(仅在有值时出现);缺省 false,键盘 Delete / Backspace 等价"
},
{
"n": "placeholder",
"t": "string",
@@ -3691,7 +3696,8 @@
"点触发器展开时 / 分两列,当前值所在格高亮并自动滚到列中部,不必手动找",
"先点时列定小时(分默认落到首个步长值,值立刻可用),再点分列即完成并收起,焦点回到触发器",
"列内可用 ↑↓ 环形移动、Home/End 直达首末;Tab、Esc、点击外部都能正常进出与收起",
"需要 15 分钟一档就传 minuteStep: 15,步长只影响分列的可选值,时列始终 24 档"
"需要 15 分钟一档就传 minuteStep: 15,步长只影响分列的可选值,时列始终 24 档",
"需要允许回到「未选」态就传 clearable: true:有值时触发器右侧出现 ×(不进 Tab 序),聚焦触发器按 Delete / Backspace 也能清空,回调收到空串"
]
}
]
+29
View File
@@ -5144,3 +5144,32 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
.ua-multiselect-pop.ua-open.is-flip {
animation-name: ua-pop-rise; /* 翻到上方:入场方向随位置镜像 */
}
/* ---- 时间选择:清空按钮(clearable 且有值时才出现) ---- */
.ua-timepicker-clear {
position: absolute;
top: 50%;
right: var(--ua-space-6); /* 让到箭头左侧,不与 ▾ 重叠 */
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: var(--ua-leading-none); /* 行高压到 1:× 在 16px 圆里垂直居中 */
cursor: pointer;
transform: translateY(-50%);
transition: color var(--ua-duration-fast) var(--ua-ease), background-color var(--ua-duration-fast) var(--ua-ease);
}
.ua-timepicker-clear:hover {
background: var(--ua-color-surface-2); /* 悬停给一层淡底:小 × 只变颜色太难点中 */
color: var(--ua-color-text);
}
.ua-timepicker.has-clear .ua-timepicker-trigger {
padding-right: var(--ua-space-6); /* 给 × 让出位置:文案不会被压在按钮下面 */
}