Formularios
Estado: BetaSelector numérico
Capacidad de una clase, duración, créditos o la cantidad del carrito. Es un input numérico real (con su name, min, max y step) con botones de − y +: mantener presionado repite cada vez más rápido, los dígitos ruedan hacia arriba al sumar y hacia abajo al restar, y al llegar a un límite el botón se apaga y el número rebota. Dispara input y change reales, así que el carrito y cualquier otro controlador se enteran solos.
- Resorte
- Formularios
- Teclado
De 1 a 40 por clase
En pasos de 15
Tapete de corcho
$890 · quedan 5
De 1 a 40 por clase
En pasos de 15
Tapete de corcho
$890 · quedan 5
app/views/… (la demo de arriba)
<div class="@container w-full max-w-md">
<div class="grid gap-5">
<div class="grid gap-4 @sm:grid-cols-2">
<%= render Ui::NumberStepperComponent.new(label: "Capacidad", name: "studio_class[capacity]", value: 12, min: 1, max: 40,
unit: "lugares", full_width: true, hint: "De 1 a 40 por clase") %>
<%= render Ui::NumberStepperComponent.new(label: "Duración", name: "studio_class[duration]", value: 60, min: 15, max: 180, step: 15,
unit: "min", full_width: true, hint: "En pasos de 15") %>
</div>
<div class="flex items-center gap-3 rounded-xl border border-border bg-card p-3">
<span class="hidden size-11 shrink-0 place-items-center rounded-lg border border-border-subtle bg-raised text-subtle @sm:grid" aria-hidden="true">
<%= render Ui::IconComponent.new(:package, size: :lg) %>
</span>
<div class="min-w-0 flex-1">
<p class="truncate text-sm font-medium text-foreground">Tapete de corcho</p>
<p class="font-mono text-xs text-muted-foreground">$890 · quedan 5</p>
</div>
<%= render Ui::NumberStepperComponent.new(label: "Cantidad de tapetes", hide_label: true, name: "quantity", value: 1, min: 1, max: 5, size: :sm) %>
</div>
</div>
</div>Qué hace
- Mantener presionado repite con aceleración (de 150 a 40 ms por paso).
- Cada cifra rueda con su resorte en la dirección del cambio, también de 9 a 10.
- En el mínimo o el máximo el botón se deshabilita y el valor da un rebote.
- Lo escrito a mano se lleva al paso y al rango válidos al salir del campo.
- Dispara input (cada paso), change (al soltar o por tecla) y number-stepper:change.
Props
| Nombre | Tipo | Default | Descripción |
|---|---|---|---|
| label | String | — | Etiqueta del campo (Ui::FieldComponent); también nombra al spinbutton. |
| name | String | nil | Nombre del input. Con form + attribute sale del form builder. |
| form / attribute | FormBuilder / Symbol | nil | Toma name, id, valor y error del modelo. |
| value | Numeric | nil | Valor inicial (con form, el del modelo). |
| min / max / step | Numeric | nil / nil / 1 | Los del input nativo. Los pasos cuentan desde min. |
| unit | String | nil | Unidad junto al número ("min", "lugares"); se lee con el valor. |
| size | :sm | :md | :md | :sm para filas compactas como el carrito. |
| full_width | Boolean | false | Ocupa todo el ancho de la columna. |
| hint / error / required / hide_label | — | — | Los de Ui::FieldComponent. |
| input | Hash | {} | Atributos extra del input (data, autofocus…). |
Accesibilidad
- El campo es un input type=number (role spinbutton) con su etiqueta, ayuda y error.
- ↑/↓ un paso, RePág/AvPág diez pasos, Inicio/Fin al mínimo y al máximo.
- aria-valuetext incluye la unidad ("60 min"); los botones tienen nombre ("Sumar 15").
- Los cambios con botones y las correcciones se anuncian en una región aria-live.
- Con movimiento reducido los dígitos cambian sin rodar.