NumberInput
Cifra con botones de restar y sumar, límites y separador de miles.
Usar
import { NumberInput } from "@unozero-ai/ui/react";
<NumberInput></NumberInput>Límites
Para cantidades cortas que se suben de una en una: huéspedes, noches, habitaciones. value es un número, o null si el campo está vacío. Ya no hace falta controlarlo: el elemento guarda su valor como un <input> y, con name, lo envía en el FormData. Con min y max el botón de ese extremo se apaga al llegar, y lo escrito fuera del rango se ajusta al límite al confirmar (perder el foco o Enter), no en cada tecla: con un mínimo de 10 se puede borrar y teclear 25. ↑ y ↓ suman o restan; los botones quedan fuera del orden de Tab porque las flechas hacen lo mismo.
Paso
step marca cuánto suma o resta cada botón y cada flecha. Elige el paso con el que se piensa el dato: de 5 en 5 para un descuento, de media en media hora para un late check-out. No invalida lo que se escriba a mano: 7 % vale aunque el paso sea 5.
Separador de miles
formatThousands pinta el punto de miles mientras se escribe (12.000), con el idioma de setLocale, así un cero de más se ve antes de guardar. Solo cambia cómo se ve: value y el FormData siguen siendo 12000. Admite solo enteros. Las cifras van en mono con cifras tabulares (la Regla de los Tres Papeles). Si el número es dinero, usa MoneyInput, que pone la moneda y sus decimales.
Tamaños
Los mismos tres tamaños que Input, con la altura de los tokens de densidad, para que un formulario mixto quede alineado. Sin fullWidth el campo mide lo que su contenido.
Error y deshabilitado
Cuando el límite depende de otro dato (la disponibilidad de esas noches), no lo fijes con max: deja escribir y explica con error por qué no vale. El usuario entiende un mensaje; un botón que se apaga sin más, no. En React, onInput llega al escribir y al pulsar −, + o las flechas, y e.target.value ya es el número.
Resumen de la estancia
Un caso real: noches, adultos y tarifa recalculan el total al momento. Los tres están controlados con value y onInput, porque el total depende de ellos. Con fullWidth (full-width) los campos ocupan su columna. La tarifa es un importe, así que va en un MoneyInput.
Total de la estancia: $ 1.260.000
API
Generada del custom-elements.json que publica @unozero-ai/ui. Las partes se estilan desde tu CSS con el nombre del elemento: uz-button::part(base).