WebberUI

數量調整器(React)

電商數量 ± 步進器的 React 元件:長按加速連續步進、上下限到界抖動、可點數字直接輸入、歸零變垃圾桶圖示、數字逐位滾動,提供緊湊與膠囊兩種樣式。

購物車、商品頁少不了的 −/+ 數量鈕,把細節一次補齊:點一下步進一次,按住不放 0.4 秒後開始連續步進、2 秒後再加速;碰到上下限時整個元件水平抖一下、該側按鈕停用;min0 且開啟 allowRemove 時,數字歸零減號會變成垃圾桶改觸發 onRemove;開啟 editable 可以直接點數字輸入(失焦或 Enter 套用並 clamp、Esc 取消,含中文輸入法組字防呆)。數字變化採逐位滾動,方向依增減而定;支援受控/非受控、compactpill 兩種樣式與三種尺寸。

載入預覽⋯
npx shadcn@latest add https://webberui.com/r/quantity-stepper.json

Playground

即時調整 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型別預設值說明
valuenumber受控值;不提供時為非受控模式
defaultValuenumber1非受控模式的初始值,會先 clamp 進 min–max
onChange(value: number) => void值變動時回呼,參數為 clamp 後的新值
minnumber1下限
maxnumber99上限
stepnumber1每次步進的量;含小數時顯示與修圓都跟著它的小數位數
allowRemovebooleanfalsemin0 且值歸零時,減號鈕改為垃圾桶並改觸發 onRemove(而不是停用)
onRemove() => void點擊垃圾桶鈕時回呼,需搭配 allowRemovemin0
editablebooleanfalse可點數字直接輸入,失焦或 Enter 套用並 clamp,Esc 取消
variant"compact" | "pill""compact"外觀樣式:compact 為帶邊框的方角群組、pill 為膠囊底+圓形按鈕
size"sm" | "md" | "lg""md"尺寸
disabledbooleanfalse停用全部互動
formatValue(value: number) => string自訂數值顯示文字(例如加單位);aria-valuetext 也用這段
labelsQuantityStepperLabels見下表各按鈕與數值的 aria-label
classNamestring透傳到最外層容器

QuantityStepperLabels

欄位型別預設值說明
decrementstring"減少數量"減號鈕的 aria-label
incrementstring"增加數量"加號鈕的 aria-label
removestring"移除"垃圾桶鈕的 aria-label
valuestring"數量"數值(spinbutton)的 aria-label

另外具名匯出 clampQuantity(n, min, max, step?)QuantityStepperVariantQuantityStepperSizeQuantityStepperLabels 型別,方便在表單層重用同一套 clamp 邏輯。

細節

  • 長按時序:pointerdown 立即步進一次,持續按住 400ms 後每 120ms 步進一次,自按下起滿 2 秒加速為每 60ms;pointeruppointercancel 掛在 window 上,手指滑出按鈕或按鈕在按住期間變成 disabled 都收得到;到界即停止連續步進,元件卸載時清除所有計時器
  • 到界抖動:只有「使用者的動作撞到邊界」才抖(x: [0, -4, 4, -2, 2, 0]),初始值本來就在邊界不會一載入就抖;減號在移除模式下歸零是變垃圾桶而不是撞牆,不抖
  • 逐位滾動:每個字元一欄,欄以「從右數」的位置當 key,9 → 10 只有個位滾動、十位從左側展開;增加時新字元由下往上進場,減少時相反
  • 直接輸入:全形數字會自動轉半形;輸入不是數字時放棄變更;套用後焦點回到數值元素,鍵盤使用者不會掉到 body

可及性

  • 數值元素為 role="spinbutton",帶 aria-valuenowaria-valueminaria-valuemax,並以 aria-valuetext 朗讀 formatValue 後的文字;−/+ 為原生 button 並以 aria-controls 指向數值元素
  • 鍵盤:焦點在元件內任一處時 ↑/↓ 步進一次、PageUp/PageDown 步進 10 倍、Home/End 跳到下限/上限;editable 時聚焦數值按 Enter/Space 或直接敲數字即開始輸入,Enter 在中文等 IME 組字中不會送出
  • 用按鈕改值時焦點不在 spinbutton 上,另有 aria-live="polite" 隱藏區塊朗讀新值;到界的按鈕以原生 disabled 停用,垃圾桶鈕會換成對應的 aria-label
  • 使用者系統開啟「減少動態效果」時,停用抖動、逐位滾動與圖示切換動畫,數字直接更新

本頁目錄