input-number —— 數字輸入框
이 콘텐츠는 아직 번역되지 않았습니다.
數字輸入框在輸入框之上加了三件事:步進(按 step 加減)、鉗制(不超出 min/max)、精度(小數位)。core/input-number 把這三件事的純計算抽出來,UI 層只負責把增減按鈕的事件接上。
- 各端實作
LhInputNumber:數量選擇、金額輸入、分頁大小。 - 不該用它:純文字數字(用
core/input,使用者可能想輸入「大約 10 個」這類非嚴格數值)、滑桿式選擇(用core/slider更直觀)。
import { incrementValue, decrementValue, shouldDisableIncrement, shouldDisableDecrement, getDisplayValue, getInputNumberAria,} from '@lh-ui/share/core/input-number'import type { LhInputNumberState } from '@lh-ui/share/core/input-number'incrementValue / decrementValue 的三道防線:
- null 起始值:
value === null時加按step、減按-step,而不是從 0 開始——使用者還沒填值就點加號,應當落到「一個步進」而非「min」。 - 精度淨化:
precision經shared/sanitize的safePrecision淨化到[0, 100]整數,避免toFixed(101)拋 RangeError。這道防線與core/statistic共用。 - 非有限結果兜底:內部
clampSafe在結果為 NaN 時回退到clamp(0, min, max)——髒 props(step: NaN)不會讓值變成 NaN 傳播到檢視。
shouldDisableIncrement 的判定是 value !== null && value >= max:null 時不停(點了會變成 step),到達上界才停。這個 !== null 不能省,否則空輸入的加號按鈕是灰的。
| 函式 | 簽名 | 傳回 | 作用 |
|---|---|---|---|
incrementValue |
(value: number | null, step: number, min: number, max: number, precision?: number) => number |
新值 | 加步長 |
decrementValue |
(value: number | null, step: number, min: number, max: number, precision?: number) => number |
新值 | 減步長 |
shouldDisableIncrement |
(state: LhInputNumberState) => boolean |
布林 | disabled 或已到上界 |
shouldDisableDecrement |
(state: LhInputNumberState) => boolean |
布林 | disabled 或已到下界 |
getDisplayValue |
(value: number | null, _placeholder: string) => string |
顯示字串 | 有值 String 化,null 空字串 |
clampValue |
同 shared/sanitize.clamp |
鉗制值 | 相容性別名再匯出 |
注意 getDisplayValue 的第二個參數 _placeholder 前綴底線、函式體未使用——佔位符的渲染是 UI 層的職責(placeholder 屬於「沒值時的提示」,與「值的顯示」是兩件事)。
export interface LhInputNumberState { value: number | null min: number max: number step: number disabled: boolean size: LhComponentSize controls: boolean // 是否顯示增減按鈕 precision: number | undefined placeholder: string}getInputNumberAria 輸出 APG 的 spinbutton 語義:
getInputNumberAria({ value: 5, min: 0, max: 10, step: 1 })// { role: 'spinbutton', 'aria-valuemin': 0, 'aria-valuemax': 10, 'aria-valuenow': 5 }
getInputNumberAria({ value: null, min: 0, max: 10, step: 1 })// { role: 'spinbutton', 'aria-valuemin': 0, 'aria-valuemax': 10 } —— null 不掛 valuenowvalue != null才掛aria-valuenow;空值掛aria-valuenow="NaN"會讓螢幕閱讀器播報異常。disabled掛aria-disabled(而非依賴原生 disabled,因為部分端用自訂控制項)。ariaValueText掛aria-valuetext——當數值本身不足以表達語義時用(如「3 天」而非「3」),優先於 valuenow 播報。
import { incrementValue, shouldDisableIncrement } from '@lh-ui/share/core/input-number'
incrementValue(4, 1, 0, 10) // 5incrementValue(null, 2, 0, 10) // 2(null 起始按 step)incrementValue(9, 1, 0, 10) // 10(鉗制到 max)incrementValue(9, 1, 0, 10, 2) // 10,且 toFixed(2) 後 Number 化decrementValue(null, 2, 0, 10) // -2(鉗制前是 -2,再被 clamp 到 0)
shouldDisableIncrement({ value: 10, max: 10, min: 0, step: 1, disabled: false, ... }) // trueshouldDisableIncrement({ value: null, max: 10, ... }) // false<!-- Vue3 --><div :class="['lh-input-number', `lh-input-number--${size}`]"> <button v-if="controls" class="lh-input-number__decrease" :disabled="shouldDisableDecrement(state)" aria-label="减少" @click="emit('update:modelValue', decrementValue(modelValue, step, min, max, precision))" >−</button> <input class="lh-input-number__input" v-bind="getInputNumberAria({ value: modelValue, min, max, step, disabled })" /> <button v-if="controls" class="lh-input-number__increase" :disabled="shouldDisableIncrement(state)">+</button></div>// Reactconst next = incrementValue(value, step, min, max, precision)return <button onClick={() => onChange(next)} disabled={shouldDisableIncrement(state)}>+</button>// uniapp:增減按鈕用 view + @tap;步進值變化後受控回寫,注意鍵盤輸入與按鈕的競態| 類別名 | 語義 |
|---|---|
.lh-input-number |
基礎類 |
.lh-input-number--{small|large} |
尺寸檔(medium 為預設,無修飾類) |
.lh-input-number--disabled |
停用態 |
.lh-input-number__input |
中間輸入區 |
.lh-input-number__increase / __decrease |
增減按鈕 |
高度 32px(與 input 的 37px、select 的 32px 構成「高度令牌專項」的不統一清單)。
step為 0 或負數:incrementValue不驗證 step 合法性,step: 0會讓值不變;UI 層應保證 step 為正。- 精度與 step 的關係:
precision只管顯示位數,不自動對齊 step。step: 0.5但precision: 0會讓incrementValue(0, 0.5, ...)顯示成1(先算後取整)。 - 空值的語義:
value === null與value === 0是兩回事,前者是「未填寫」。getDisplayValue(null)傳回空字串而非'0'。 - 浮點累積:
0.1 + 0.2的經典問題在步進中真實存在;precision的toFixed路徑是唯一的防線,設了 precision 就穩,不設就可能漂。
@lh-ui/share/core/input-number → incrementValue( )返回值
(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue