# Pin Code Field

O `PinCodeFieldComponent` é um componente customizado que encapsula múltiplos inputs para entrada de códigos de autenticação (tokens, OTP, etc.). O componente implementa `ControlValueAccessor`, tornando-o totalmente compatível com formulários reativos e template-driven do Angular.

## Features

- Compatível com `ngModel` e `ReactiveForms` (`ControlValueAccessor`)
- Validação flexível delegada ao `FormControl` do usuário
- Configuração flexível de comprimento do código
- Suporte a caracteres alfanuméricos e especiais (opcional)
- Navegação entre campos com setas (esquerda/direita)
- Suporte a colar código (trunca se maior que o comprimento)
- Suporte a backspace com navegação automática
- Estados visuais: normal, foco, desabilitado e erro
- Controle de estado de erro via input `invalid`
- Help text descritivo
- Acessibilidade completa (ARIA labels, roles, etc.)
- Integração com `s-control-errors` para exibição de mensagens de erro

## Dependências

Nenhuma dependência adicional além do Angular.

## Utilização

**[Exemplo completo de implementação](../../../../src/app/components/pin-code-field-showcase)**

- **Componente**: `PinCodeFieldComponent`
- **Seletor do componente**: `s-pin-code-field`

### Import

```typescript
import { PinCodeFieldComponent } from '@seniorsistemas/angular-components/pin-code-field';
```

## Exemplos de utilização

### Exemplo básico com template-driven forms

```html
<form #myForm="ngForm">
    <s-pin-code-field
        name="code"
        [(ngModel)]="code"
        [length]="6"
        helpText="Digite o código enviado para seu email"
    >
    </s-pin-code-field>
</form>
```

### Exemplo com formulário reativo

```typescript
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
    selector: 'app-auth-form',
    template: `
        <form [formGroup]="form">
            <s-pin-code-field
                formControlName="code"
                [length]="6"
                [alphanumeric]="false"
                [invalid]="(form.get('code')?.invalid && form.get('code')?.dirty) || false"
                helpText="Digite o código de 6 dígitos"
            >
            </s-pin-code-field>
            <s-control-errors
                [control]="form.get('code')"
                [errorMessages]="errorMessages"
                [displayErrors]="submitted"
            >
            </s-control-errors>
        </form>
    `,
})
export class AuthFormComponent {
    form: FormGroup;
    submitted = false;
    errorMessages = {
        required: 'Este campo é obrigatório e não foi preenchido.',
    };

    constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
            code: ['', [Validators.required()]],
        });
    }
}
```

### Exemplo com listener de preenchimento

```html
<s-pin-code-field
    [(ngModel)]="code"
    [length]="6"
    (codeFilled)="onCodeFilled($event)"
>
</s-pin-code-field>
```

```typescript
export class MyComponent {
    code: string = '';

    onCodeFilled(code: string) {
        console.log('Código completado:', code);
        // Realizar verificação do código
    }
}
```

### Exemplo com caracteres alfanuméricos

```html
<s-pin-code-field
    [(ngModel)]="code"
    [length]="8"
    [alphanumeric]="true"
    helpText="Código pode conter letras, números e caracteres especiais"
>
</s-pin-code-field>
```

## Inputs

| Nome           | Tipo             | Valor Padrão | Obrigatório | Descrição                                                                                                                                      |
| -------------- | ---------------- | ------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `length`       | `input<number>`  | 6            | Não         | Quantidade de caracteres que o código deve ter.                                                                                                |
| `alphanumeric` | `input<boolean>` | false        | Não         | Se true, aceita letras, números e caracteres especiais. Se false, aceita apenas números.                                                       |
| `helpText`     | `input<string>`  | ''           | Não         | Texto de ajuda exibido abaixo dos campos.                                                                                                      |
| `invalid`      | `input<boolean>` | false        | Não         | Controla se o componente exibe estado de erro (borda vermelha). Use em conjunto com `FormControl.invalid && FormControl.dirty` para validação. |
| `disabled`     | `model<boolean>` | false        | Não         | Desabilita todos os campos impedindo entrada de dados. Two-way binding.                                                                        |

## Outputs

| Nome         | Tipo                   | Descrição                                                                                          |
| ------------ | ---------------------- | -------------------------------------------------------------------------------------------------- |
| `codeFilled` | `EventEmitter<string>` | Emitido quando todos os caracteres do código são preenchidos. Emite o código completo como string. |

## Validators

O componente exporta dois validators personalizados que trabalham como `ValidatorFn` do Angular:

### `incomplete(length?: number)`

Valida se o código foi totalmente preenchido. Retorna erro `incomplete` se houver preenchimento parcial.

```typescript
const control = new FormControl('', [Validators.required(), incomplete(6)]);
```

**Parâmetro:**

- `length` (opcional): Comprimento esperado do código (padrão: 6)

**Retorna:**

- `{ incomplete: true }` se o valor parcial for preenchido
- `null` se o código estiver completo ou vazio

### `pasteError(component?: PinCodeFieldComponent)`

Valida se houve erro ao colar conteúdo inválido. Retorna erro `pasteError` quando um paste inválido é detectado.

```typescript
const control = new FormControl('', [pasteError()]);
```

**Nota:** Este validator monitora o estado interno do componente para detectar tentativas de paste com conteúdo inválido.

## Comportamentos

### Navegação

- **Seta Direita**: Move o foco para o próximo campo
- **Seta Esquerda**: Move o foco para o campo anterior
- **Seta Para Cima/Baixo**: Sem efeito

### Entrada de Dados

- A digitação automática avança para o próximo campo quando um caractere é inserido
- No último campo, a digitação não causa mudança de foco
- Todos os campos são preenchidos ao colar um código válido

### Backspace

- **Campo preenchido**: Limpa o campo atual e mantém o foco
- **Campo vazio**: Move para o campo anterior, limpa-o e mantém o foco no anterior

### Paste (Colar)

- Filtra caracteres inválidos de acordo com a configuração `alphanumeric`
- Se o texto colado é maior que o comprimento, trunca automaticamente
- Preenche os campos disponíveis com o texto colado filtrado
- Emite o evento `codeFilled` quando todos os campos são preenchidos
- A validação de valores aceitáveis é responsabilidade do `FormControl` validator

### Estados Visuais

- **Normal**: Borda cinza, fundo branco
- **Foco**: Borda azul com ring de foco
- **Desabilitado**: Fundo cinza claro, texto cinza, sem interação
- **Erro**: Borda e ring vermelhos (quando `invalid` input é true)

## Validação

O componente **não exporta validators pré-construídos**. A validação é responsabilidade do usuário via `FormControl.validator`.

Exemplos de validators customizados:

```typescript
// Validar que o código tem exatamente 6 dígitos
const requiredValidator = Validators.required();

// Validador customizado para rejeitar valor específico
const notBlacklisted: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
    const value = control.value as string;
    if (value === '123456') {
        return { blacklisted: true };
    }
    return null;
};

const form = this.fb.group({
    code: ['', [requiredValidator, notBlacklisted]],
});
```

### Mostrando Erros de Validação

Use o input `invalid` para controlar visualmente o estado de erro:

```html
<s-pin-code-field
    formControlName="code"
    [invalid]="(form.get('code')?.invalid && form.get('code')?.dirty) || false"
>
</s-pin-code-field>
```

## Acessibilidade

O componente implementa padrões de acessibilidade completos:

- Cada input tem um `aria-label` descritivo com sua posição no código
- O container tem `role="group"` e associação com help text
- `aria-invalid` é definido quando há erro
- `aria-describedby` conecta inputs ao help text
- `inputmode` é configurado corretamente (numeric/text)
- `autocomplete="off"` previne preenchimento automático indesejado
- Todos os estados visuais incluem indicadores para leitores de tela

## Exemplos Completos

### Integração com Reactive Forms e validação customizada

```typescript
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

// Validador customizado que rejeita um valor específico
const notBlacklisted: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
    const value = control.value as string;
    if (value === '123456') {
        return { blacklisted: true };
    }
    return null;
};

@Component({
    selector: 'app-verify-code',
    template: `
        <form [formGroup]="verifyForm">
            <s-pin-code-field
                formControlName="verificationCode"
                [length]="6"
                [alphanumeric]="false"
                [invalid]="(control?.invalid && control?.dirty) || false"
                helpText="Digite o código de 6 dígitos enviado"
                (codeFilled)="verifyCode($event)"
            >
            </s-pin-code-field>

            <s-control-errors
                [control]="control"
                [errorMessages]="errorMessages"
                [displayErrors]="submitted"
            >
            </s-control-errors>

            <button
                [disabled]="!verifyForm.valid"
                (click)="submit()"
            >
                Verificar
            </button>
        </form>
    `,
})
export class VerifyCodeComponent implements OnInit {
    verifyForm!: FormGroup;
    submitted = false;
    errorMessages = {
        required: 'Código obrigatório',
        blacklisted: 'Este código não é válido',
    };

    get control() {
        return this.verifyForm.get('verificationCode');
    }

    constructor(private fb: FormBuilder) {}

    ngOnInit() {
        this.verifyForm = this.fb.group({
            verificationCode: ['', [Validators.required, notBlacklisted]],
        });
    }

    verifyCode(code: string) {
        console.log('Código completo:', code);
        // Enviar para API de verificação
    }

    submit() {
        this.submitted = true;
        if (this.verifyForm.valid) {
            this.verifyCode(this.verifyForm.value.verificationCode);
        }
    }
}
```

### Com loading e desabilitação após envio

```typescript
export class VerifyCodeComponent {
    verifyForm!: FormGroup;
    isLoading = false;

    verifyCode(code: string) {
        this.isLoading = true;
        // Desabilita o campo durante a requisição
        this.verifyForm.get('verificationCode')?.disable();

        this.authService.verify(code).subscribe(
            (response) => {
                // Sucesso
                this.isLoading = false;
            },
            (error) => {
                // Erro - reabilita o formulário
                this.isLoading = false;
                this.verifyForm.get('verificationCode')?.enable();
                this.verifyForm.get('verificationCode')?.reset();
            },
        );
    }
}
```

