import { effect, h } from '@ui-arch/frame'; import { button } from './button.js'; import type { InputNumberProps } from './types.js'; /*** 数字输入:−/+ 按钮 + 输入框三段式,value 信号双向绑定,min/max/step 超界自动夹取,键盘 ↑↓ 调节 ***/ export function inputNumber(props: InputNumberProps): HTMLDivElement { const min = props.min ?? -Infinity; // 无下限缺省 const max = props.max ?? Infinity; // 无上限缺省 const step = props.step ?? 1; // 步长缺省 1 const clamp = (v: number): number => Math.min(max, Math.max(min, v)); // 任何来源的值都夹回合法区间 const emit = (v: number): void => { const n = clamp(v); props.value(n); if (props.onChange) props.onChange(n); }; // 写回信号并回调 const input = h('input', { class: 'ua-input ua-input-number-field', inputmode: 'decimal', disabled: props.disabled, 'aria-label': '数字输入' }) as HTMLInputElement; const sync = (): void => { const text = String(props.value()); if (input.value !== text) input.value = text; }; // 外部变化写回输入框 effect(sync); input.addEventListener('change', () => { const n = parseFloat(input.value); if (Number.isNaN(n)) sync(); else emit(n); }); // 非法输入回滚当前值 input.addEventListener('keydown', (e: KeyboardEvent) => { if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return; // 卫语句:只拦截上下方向键 e.preventDefault(); emit(props.value() + (e.key === 'ArrowUp' ? step : -step)); // ↑ 加步长 / ↓ 减步长 }); const dec = button({ label: '−', variant: 'ghost', disabled: props.disabled, onClick: () => emit(props.value() - step) }); const inc = button({ label: '+', variant: 'ghost', disabled: props.disabled, onClick: () => emit(props.value() + step) }); dec.setAttribute('aria-label', '减少'); inc.setAttribute('aria-label', '增加'); return h('div', { class: 'ua-input-number', role: 'group' }, dec, input, inc) as HTMLDivElement; }