# Cómo aplicar máscara de CPF, CEP o fecha

> Formatear automáticamente CPF, CEP y fechas con máscara y leer los dígitos limpios.

Source: https://crossfly.dev/es/docs/cookbook/forms/mascara-cpf — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Cómo aplicar máscara de CPF, CEP o fecha

**Propósito.** Formatear la escritura automáticamente con separadores (puntos, guiones, barras) mostrando `123.456.789-00` mientras el usuario escribe solo los dígitos, y recuperar después el valor limpio (`12345678900`).

## APIs usadas
- `campo.<TextFieldStyle>getStyle().mask("999.999.999-99".toCharArray())` — define el patrón de la máscara. `9` = posición de dígito; cualquier otro carácter es un literal insertado automáticamente (`.`, `-`, `/`).
- `.mode(EditMode.NUMERIC, true)` — activa el modo numérico Y el `maskedEdit` (segundo parámetro `true`). Sin el `maskedEdit` la máscara no se aplica. El orden importa: llama a `mask(...)` y `mode(...)` juntos.
- `TextField.getText()` — devuelve el texto CON máscara (`"123.456.789-00"`), ideal para mostrar/guardar formateado.
- `TextField.getTextWithoutMask()` — devuelve solo los dígitos de las posiciones `9` (`"12345678900"`), ideal para validar y enviar al backend.

> Para **fechas**, prefiere `setMode(EditMode.DATE, true)`: deriva la máscara automáticamente del locale (`LocaleFormats.dateSeparator` y `LocaleFormats.dateFormat`, ej.: `dd/MM/yyyy` o `yyyy-MM-dd`) y además abre el DatePicker. Ver la receta [Fecha y hora](/docs/cookbook/forms/data-e-hora).

> Atención al orden en `mode(EditMode.NUMERIC, true)`: el modo `NUMERIC`/`NORMAL` solo recalcula el `maxLength` a partir de la máscara si la máscara ya está definida. Define `mask(...)` **antes** o en la misma cadena fluida.

## Ejemplo completo (copiar, pegar y ejecutar)
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // TextFieldStyle (config en el estilo)

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // CPF: 123.456.789-00
      TextField cpf = new TextField("");
      cpf.caption("CPF");
      cpf.<TextFieldStyle>getStyle()
         .mask("999.999.999-99".toCharArray())
         .mode(EditMode.NUMERIC, true);   // NUMERIC + maskedEdit

      // CEP: 12345-678
      TextField cep = new TextField("");
      cep.caption("CEP");
      cep.<TextFieldStyle>getStyle()
         .mask("99999-999".toCharArray())
         .mode(EditMode.NUMERIC, true);

      Label resultado = new Label("");

      Button enviar = new Button("Enviar");
      enviar.onAction(o ->
      {
         String cpfFormatado = cpf.getText();            // "123.456.789-00"
         String cpfLimpo     = cpf.getTextWithoutMask(); // "12345678900"
         if (cpfLimpo.length() != 11)
         {
            Hint.show("CPF deve ter 11 dígitos", 2000);
            return;
         }
         resultado.setText(cpfFormatado + " · CEP: " + cep.getText());
      });

      add(LAY_FILL, cpf);
      add(LAY_FILL, cep);
      add(LAY_AUTO, enviar);
      add(LAY_FILL, resultado);
   }
}
```

> El fragmento de abajo muestra **solo el núcleo**. Para ejecutarlo, colócalo en el esqueleto `MainWindow` de arriba.

## Fragmento mínimo
```java
import dev.crossfly.ui.*;
import dev.crossfly.ui.style.*;
import static dev.crossfly.ui.UIConstants.EditMode;

// CPF: 123.456.789-00
TextField cpf = new TextField("");
cpf.caption("CPF");
cpf.<TextFieldStyle>getStyle()
   .mask("999.999.999-99".toCharArray())
   .mode(EditMode.NUMERIC, true);   // NUMERIC + maskedEdit

// CEP: 12345-678
TextField cep = new TextField("");
cep.caption("CEP");
cep.<TextFieldStyle>getStyle()
   .mask("99999-999".toCharArray())
   .mode(EditMode.NUMERIC, true);

Button enviar = new Button("Enviar");
enviar.onAction(o ->
{
   String cpfFormatado = cpf.getText();            // "123.456.789-00"
   String cpfLimpo     = cpf.getTextWithoutMask(); // "12345678900"
   // valida cpfLimpo.length() == 11 y envía
});
```

## Profundiza
- [Máscaras y teclados](/docs/ui/forms-and-input/masks-and-keyboards) · [Campos de texto](/docs/ui/forms-and-input/text-fields)
- Recetas hermanas: [Validar un campo](/docs/cookbook/forms/validar-campo) · [Máscara de moneda](/docs/cookbook/forms/mascara-moeda) · [Fecha y hora](/docs/cookbook/forms/data-e-hora)
- Fuente canónica: Capítulo 14 — Widgets básicos · [Enums (UIConstants)](/docs/reference/enums-uiconstants)
