Ir al contenido
Formularios

PhoneInput

Un teléfono en E.164: el país con su bandera y código adentro del campo, y el número en dígitos. Sin libphonenumber.

import { PhoneInput } from "sebs7n-ui/phone-input"

Ejemplos

Básico

El país con su bandera y código adentro del campo, y el número en dígitos. El valor es E.164.

import { Field, FieldDescription, FieldLabel } from "sebs7n-ui"
import { PhoneInput } from "sebs7n-ui/phone-input"
import { useState } from "react"

function Basic() {
  const [phone, setPhone] = useState("+5491155552002")
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Teléfono de contacto</FieldLabel>
      <PhoneInput name="phone" onValueChange={setPhone} value={phone} />
      <FieldDescription>
        Se guarda como <code>{phone || "—"}</code>.
      </FieldDescription>
    </Field>
  )
}

Con validación

`isValidPhone` (de `sebs7n-ui/lib/phone`, también en el servidor) mira el largo del número de cada país.

import { Field, FieldError, FieldLabel } from "sebs7n-ui"
import { PhoneInput } from "sebs7n-ui/phone-input"
import { isValidPhone } from "sebs7n-ui/lib/phone"
import { useState } from "react"

function WithValidation() {
  const [phone, setPhone] = useState("+598991234")
  const invalid = phone !== "" && !isValidPhone(phone)
  return (
    <Field className="w-full max-w-sm" invalid={invalid}>
      <FieldLabel>Teléfono para avisos de cobro</FieldLabel>
      <PhoneInput aria-invalid={invalid} defaultCountry="UY" onValueChange={setPhone} value={phone} />
      <FieldError alert match={invalid}>Al número le faltan dígitos.</FieldError>
    </Field>
  )
}

Para mostrar

`formatPhone` de `sebs7n-ui/lib/phone`: internacional legible, o nacional si el teléfono es argentino y quien lo lee también (`country: "AR"`).

import { formatPhone } from "sebs7n-ui/lib/phone"

function Display() {
  return (
    <dl className="grid w-full max-w-md grid-cols-[1fr_auto_auto] overflow-x-auto gap-x-4 gap-y-2 text-callout">
      <dt className="text-label-secondary">Cliente</dt>
      <dt className="text-label-secondary">Internacional</dt>
      <dt className="text-label-secondary">Desde Argentina</dt>
      {contacts.map((contact) => (
        <div className="contents" key={contact.client}>
          <dd className="text-label">{contact.client}</dd>
          <dd className="whitespace-nowrap tabular-nums text-label">{formatPhone(contact.phone)}</dd>
          <dd className="whitespace-nowrap tabular-nums text-label">{formatPhone(contact.phone, { country: "AR" })}</dd>
        </div>
      ))}
    </dl>
  )
}

Datos viejos

Un valor guardado sin «+» se toma como número nacional del país (`011 5555-2002` → +54 11 5555 2002) y el formulario ya lleva el E.164. Si no es un número posible, queda el texto y el campo inválido.

import { Field, FieldDescription, FieldLabel } from "sebs7n-ui"
import { PhoneInput } from "sebs7n-ui/phone-input"

function Legacy() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <Field>
        <FieldLabel>Teléfono de Acme S.A.</FieldLabel>
        <PhoneInput defaultValue="011 5555-2002" />
      </Field>
      <Field>
        <FieldLabel>Teléfono de Globex SRL</FieldLabel>
        <PhoneInput defaultValue="llamar a la tarde" />
        <FieldDescription>No es un número: corregilo a mano.</FieldDescription>
      </Field>
    </div>
  )
}

Props

Generadas del TypeScript del paquete. Las propias del componente, más las heredadas del primitivo que tienen algo que explicar —marcadas «heredada de Base UI»—. El resto está en la línea «hereda de».

PhoneInput

PropTipoPor defectoDescripción
aria-describedbystring—El id de la ayuda o del error del campo.
aria-invalidboolean—Marca el campo inválido (borde rojo).
aria-labelstring—Nombre accesible del elemento.
aria-labelledbystring—El id del elemento que nombra el número, si no es un <label>.
defaultCountrystring"AR"El país del selector si el valor no dice otro. Por defecto, «AR».
defaultValuestring""El valor inicial. Es la versión no controlada de value.
disabledboolean—Apaga el selector y el número.
idstring—El id del campo del número, para un <Label htmlFor>.
labelsPartial<Labels["phoneInput"]>—Textos: country (antes del país, en el nombre del selector) y unknownCode (lo que se anuncia al pegar un código que no está). El idioma de los nombres es countryPicker.locale.
localestring—El idioma de los nombres de la lista. Por defecto, el de labels (countryPicker.locale).
namestring—El nombre con el que el E.164 viaja en un formulario.
onValueChange(value: string) => void—Avisa el teléfono en E.164, o vacío si se borró el número.
placeholderstring—Lo que dice el número vacío.
size"sm" | "md" | "lg""md"28, 36 (default) o 40, como los campos.
valuestring—El teléfono en E.164 («+5491155552002»); vacío sin número. Pasarlo lo vuelve controlado. Un valor sin «+» (un dato viejo) se toma como número nacional del país de defaultCountry (o internacional, si empieza con su código: «5491155552002»); si no es un número posible, se muestra tal cual y el campo queda inválido después de tocarlo o de enviar. Hasta que se edite, el form manda el valor original: migrar los datos es de la app.
classNamestring—Clases de la superficie.

Teclado

Tab
Entra al selector de país y después al número.
Enter · Espacio · ↓
En el selector, abre la lista de países.
a–z
Con la lista abierta, salta al país que empieza así.
0–9
En el número, escriben; lo que no es un dígito no entra.

Accesibilidad

  • El selector se llama «Código de país: Argentina (+54)» (labels.country más el país): la bandera es decorativa.
  • El número es un <input type="tel"> con inputMode="tel" (el teclado numérico en el teléfono): nombralo con un FieldLabel o aria-label.
  • No se valida solo: para el error, isValidPhone y el FieldError del campo (con alert si se valida mientras se escribe).

Reglas de uso

  • El valor es E.164 (+5491155552002) y viaja así con name: se guarda tal cual y sirve para un link de WhatsApp.
  • isValidPhone y parsePhone están en sebs7n-ui/lib/phone, sin "use client": validan igual en una Server Action. Miran el largo del número de cada país, no el tipo de línea.
  • Datos viejos: un value/defaultValue sin «+» («011 5555-2002») se toma como número nacional del país de defaultCountry (o internacional si empieza con su código: «5491155552002»); si no es un número posible, queda el texto como estaba y el campo va en rojo recién después de tocarlo o de enviar. Hasta que se edite, ni onValueChange ni el form cambian: el name manda el valor original. Migrar los datos es de la app.
  • Para mostrar un teléfono guardado, formatPhone(e164, { country }) de sebs7n-ui/lib/phone: «+54 9 11 5555-2002», o nacional («011 15-5555-2002») si es argentino y country es "AR". Argentina sale con los formatos de libphonenumber (el área de 2, 3 o 4 dígitos parte al abonado: «+54 9 341 456-7890», «02478 15-40-9043»); el resto no es el formato oficial de cada país: siempre internacional, en grupos desde la derecha sin grupos de un dígito. Lo que no es un teléfono válido (isValidPhone) vuelve tal cual, también un número a medio escribir.
  • El número se corta en el largo máximo del país. Pegar un número que empieza con «+» o «00» cambia el país solo; si el código no está en la tabla, el número no cambia y se anuncia «Código de país desconocido».
  • El prefijo nacional no entra al E.164: «011 5555 2002» es +541155552002 (Italia no tiene: su 0 se queda). En Argentina, el celular con 15 («11 15 5555 2002») pasa a la forma con 9, +5491155552002. isValidPhone rechaza un número que empieza con el prefijo nacional.
  • Tiene 45 países (América, Europa y los más comunes); uno que falte se suma a la tabla de lib/phone con los largos de libphonenumber.
  • No formatea mientras se escribe. Solo por subpath (sebs7n-ui/phone-input): no está en el barrel, por peso.

Relacionados