QuantityStepper
Prop-driven quantity stepper: a typeable number field between two step buttons,
clamped to [min, max] and stepping by step. A typed value is reported on Enter or
blur, snapped to the step; a step at once. With onRemove, the decrement at the
minimum is a remove button.
import { QuantityStepper } from "@evolve-storefront/ui/quantity-stepper";Design notes
The three labels are required, with no English default. The buttons are icon-only and the field has no visible label, so each name is the whole accessible name, and an omitted one is a control announced in the wrong language.
Preview
Usage
<QuantityStepper value={line.quantity} onValueChange={(quantity) => updateLine({ id: line.id, quantity })} min={1} label={t("quantityFor", { name: line.name })} decrementLabel={t("decreaseQuantityFor", { name: line.name })} incrementLabel={t("increaseQuantityFor", { name: line.name })}/>;Props
| Prop | Type | Description |
|---|---|---|
value | number | |
onValueChange | (value: number) => void | |
label | string | Accessible name of the quantity field. |
decrementLabel | string | Accessible label for the decrement button. |
incrementLabel | string | Accessible label for the increment button. |
min? | number | |
max? | number | |
step? | number | The pack size: steps go by it, and a typed value snaps to a multiple of it, with
|
onRemove? | (() => void) | At the minimum, the decrement becomes a remove button that calls this, such as removing a cart line. Without it the decrement is disabled there. |
removeLabel? | string | Accessible label for the remove button; required with |
disabled? | boolean | Locks the value, e.g. while the previous change saves: the buttons disable and the field turns read-only but keeps focus, so a pending save cannot blur it. |
Any other props go to the root element, like on a plain HTML element.
Examples
Pack SizeA pack size of 6: the buttons step by 6, a typed quantity snaps to the nearest pack, and the minimum of 1 becomes one pack.
// Sold per 6: steps and typed quantities keep to multiples of 6.<QuantityStepper value={quantity} onValueChange={setQuantity} min={1} step={6} {...labels} />;Remove At MinimumWith onRemove, the decrement at the minimum is a bin that removes the line, so the last step down does what the shopper meant instead of doing nothing.
<QuantityStepper value={line.quantity} onValueChange={setQuantity} min={1} onRemove={() => remove(line.id)} removeLabel={t("removeProduct", { name: line.name })} {...labels}/>;Locked While SavingLocked while the change saves, as a cart line does, and then left: the step is reported once.
<QuantityStepper value={line.quantity} onValueChange={(quantity) => updateLine.mutate({ id: line.id, quantity })} disabled={updateLine.isPending} {...labels}/>;
