數量調整器(React)
電商數量 ± 步進器的 React 元件:長按加速連續步進、上下限到界抖動、可點數字直接輸入、歸零變垃圾桶圖示、數字逐位滾動,提供緊湊與膠囊兩種樣式。
購物車、商品頁少不了的 −/+ 數量鈕,把細節一次補齊:點一下步進一次,按住不放 0.4 秒後開始連續步進、2 秒後再加速;碰到上下限時整個元件水平抖一下、該側按鈕停用;min 為 0 且開啟 allowRemove 時,數字歸零減號會變成垃圾桶改觸發 onRemove;開啟 editable 可以直接點數字輸入(失焦或 Enter 套用並 clamp、Esc 取消,含中文輸入法組字防呆)。數字變化採逐位滾動,方向依增減而定;支援受控/非受控、compact 與 pill 兩種樣式與三種尺寸。
載入預覽⋯
npx shadcn@latest add https://webberui.com/r/quantity-stepper.jsonPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
0
10
<QuantityStepper />
安裝
npx shadcn@latest add https://webberui.com/r/quantity-stepper.json或在 components.json 設定 registries 後,改用 @webberui/quantity-stepper 安裝。
使用
import { QuantityStepper } from "@/components/ui/quantity-stepper";
// 非受控:預設 min 1、max 99
<QuantityStepper defaultValue={1} onChange={(n) => console.log(n)} />
// 購物車列:受控、歸零變垃圾桶、可直接輸入、限購 3 件
<QuantityStepper
value={qty}
onChange={setQty}
min={0}
max={3}
allowRemove
onRemove={() => removeItem(id)}
editable
variant="pill"
size="sm"
/>
// 加單位顯示(螢幕閱讀器也會唸這段)
<QuantityStepper formatValue={(n) => `${n} 件`} />Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | number | — | 受控值;不提供時為非受控模式 |
defaultValue | number | 1 | 非受控模式的初始值,會先 clamp 進 min–max |
onChange | (value: number) => void | — | 值變動時回呼,參數為 clamp 後的新值 |
min | number | 1 | 下限 |
max | number | 99 | 上限 |
step | number | 1 | 每次步進的量;含小數時顯示與修圓都跟著它的小數位數 |
allowRemove | boolean | false | min 為 0 且值歸零時,減號鈕改為垃圾桶並改觸發 onRemove(而不是停用) |
onRemove | () => void | — | 點擊垃圾桶鈕時回呼,需搭配 allowRemove 且 min 為 0 |
editable | boolean | false | 可點數字直接輸入,失焦或 Enter 套用並 clamp,Esc 取消 |
variant | "compact" | "pill" | "compact" | 外觀樣式:compact 為帶邊框的方角群組、pill 為膠囊底+圓形按鈕 |
size | "sm" | "md" | "lg" | "md" | 尺寸 |
disabled | boolean | false | 停用全部互動 |
formatValue | (value: number) => string | — | 自訂數值顯示文字(例如加單位);aria-valuetext 也用這段 |
labels | QuantityStepperLabels | 見下表 | 各按鈕與數值的 aria-label |
className | string | — | 透傳到最外層容器 |
QuantityStepperLabels
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
decrement | string | "減少數量" | 減號鈕的 aria-label |
increment | string | "增加數量" | 加號鈕的 aria-label |
remove | string | "移除" | 垃圾桶鈕的 aria-label |
value | string | "數量" | 數值(spinbutton)的 aria-label |
另外具名匯出 clampQuantity(n, min, max, step?) 與 QuantityStepperVariant、QuantityStepperSize、QuantityStepperLabels 型別,方便在表單層重用同一套 clamp 邏輯。
細節
- 長按時序:
pointerdown立即步進一次,持續按住 400ms 後每 120ms 步進一次,自按下起滿 2 秒加速為每 60ms;pointerup/pointercancel掛在window上,手指滑出按鈕或按鈕在按住期間變成 disabled 都收得到;到界即停止連續步進,元件卸載時清除所有計時器 - 到界抖動:只有「使用者的動作撞到邊界」才抖(
x: [0, -4, 4, -2, 2, 0]),初始值本來就在邊界不會一載入就抖;減號在移除模式下歸零是變垃圾桶而不是撞牆,不抖 - 逐位滾動:每個字元一欄,欄以「從右數」的位置當 key,
9 → 10只有個位滾動、十位從左側展開;增加時新字元由下往上進場,減少時相反 - 直接輸入:全形數字會自動轉半形;輸入不是數字時放棄變更;套用後焦點回到數值元素,鍵盤使用者不會掉到 body
可及性
- 數值元素為
role="spinbutton",帶aria-valuenow/aria-valuemin/aria-valuemax,並以aria-valuetext朗讀formatValue後的文字;−/+ 為原生button並以aria-controls指向數值元素 - 鍵盤:焦點在元件內任一處時 ↑/↓ 步進一次、PageUp/PageDown 步進 10 倍、Home/End 跳到下限/上限;
editable時聚焦數值按 Enter/Space 或直接敲數字即開始輸入,Enter 在中文等 IME 組字中不會送出 - 用按鈕改值時焦點不在 spinbutton 上,另有
aria-live="polite"隱藏區塊朗讀新值;到界的按鈕以原生disabled停用,垃圾桶鈕會換成對應的 aria-label - 使用者系統開啟「減少動態效果」時,停用抖動、逐位滾動與圖示切換動畫,數字直接更新