# Numeric Mask Directive

Diretiva Angular para formatação de valores numéricos com suporte completo a internacionalização e validação. Funciona com moedas, números decimais, notação científica e muito mais.

## Recursos

- ✅ Suporte completo a internacionalização (todos os locales)
- ✅ Formatação automática com separadores de milhar e decimal
- ✅ Validação integrada com Angular Forms
- ✅ Suporte a valores negativos
- ✅ Controle de casas decimais configurável (mínimas e máximas)
- ✅ Truncamento inteligente de decimais (nunca arredonda)
- ✅ Precisão arbitrária com BigNumber
- ✅ Suporte a notação científica com expoentes negativos
- ✅ Tratamento inteligente de colagem
- ✅ Teclado numérico em dispositivos móveis
- ✅ Acessibilidade (ARIA)
- ✅ Standalone component compatível

## Instalação

```bash
npm install @seniorsistemas/angular-components
```

## Uso Básico

### Standalone Components (Recomendado)

```typescript
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NumericMaskDirective } from '@seniorsistemas/angular-components/numeric-mask';

@Component({
    selector: 'app-example',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <input
            type="text"
            sNumericMask
            [locale]="'pt-BR'"
            [minDecimalPlaces]="2"
            [maxDecimalPlaces]="2"
            [(ngModel)]="value"
        />
    `,
})
export class ExampleComponent {
    value: string | null = null;
}
```

## API

### Inputs

| Input                     | Tipo                  | Padrão      | Descrição                                                                                                                                                     |
| ------------------------- | --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `locale`                  | `string \| undefined` | `undefined` | Locale para formatação (ex: 'pt-BR', 'en-US', 'de-DE', 'fr-FR'). Se não fornecido, usa automaticamente o locale do `LocaleService`                            |
| `minDecimalPlaces`        | `number`              | `0`         | Quantidade mínima de casas decimais a exibir. Na formatação, o valor sempre terá no mínimo esse número de casas decimais, preenchendo com zeros se necessário |
| `maxDecimalPlaces`        | `number`              | `10`        | Quantidade máxima de casas decimais permitidas. Na formatação, limita a exibição a esse número de casas decimais, truncando valores maiores                   |
| `allowNegative`           | `boolean`             | `false`     | Permite valores negativos. Use `+` e `-` para alternar o sinal                                                                                                |
| `allowScientificNotation` | `boolean`             | `true`      | Habilita suporte a notação científica (ex: `1e-6`). Quando `false`, bloqueia digitação e colagem de valores com `e`/`E`                                       |
| `min`                     | `number \| undefined` | `undefined` | Valor mínimo permitido. Quando definido, adiciona validação nativa                                                                                            |
| `max`                     | `number \| undefined` | `undefined` | Valor máximo permitido. Quando definido, adiciona validação nativa                                                                                            |

## Comportamento

### Casas Decimais (minDecimalPlaces e maxDecimalPlaces)

A diretiva agora aplica um controle preciso sobre as casas decimais exibidas:

- **minDecimalPlaces**: Define o número mínimo de casas decimais. Se o valor tiver menos casas decimais, zeros são adicionados à direita.
- **maxDecimalPlaces**: Define o número máximo de casas decimais. Se o valor tiver mais casas decimais, é **truncado** (não arredondado).

Exemplos com formatação (blur):

- Valor `12,1` com `minDecimalPlaces="3"` → exibido como `12,100`
- Valor `3,3333333` com `maxDecimalPlaces="4"` → exibido como `3,3333` (truncado)
- Valor `5` com `minDecimalPlaces="2"` e `maxDecimalPlaces="2"` → exibido como `5,00`

### Truncamento de Decimais

Ao contrário de outras máscaras numéricas que **arredondam**, a `numeric-mask` **trunca** valores que excedem `maxDecimalPlaces`. Isso é crucial para precisão em valores financeiros e científicos.

Exemplo com `maxDecimalPlaces="2"`:

- Valor digitado: `1,999`
- Valor no modelo: `1.999` (sem truncamento)
- Valor exibido (blur): `1,99` (truncado, não arredondado para `2,00`)
- Valor ao receber foco: `1,999` (mostra completo)

### Bloqueio de Caracteres Inválidos

A diretiva bloqueia automaticamente a digitação de caracteres que não sejam:

- Dígitos (0-9)
- Separador decimal (`,` ou `.` conforme o locale)
- Caracteres de controle (Tab, Backspace, Delete, etc.)
- Teclas de seta
- Ctrl/Cmd/Alt

Para **valores negativos**, você pode:

- Pressionar `-` no início do valor para ativar/desativar o sinal negativo
- Pressionar `+` para remover o sinal negativo

### Alinhamento de Texto

O texto dentro do input é sempre alinhado à **direita**, facilitando a leitura de valores numéricos.

### Focus

Quando o input recebe foco:

- Remove a formatação (separadores de milhar)
- Exibe o valor com o separador decimal do locale
- Auto-seleciona todo o valor para fácil substituição
- Mostra o valor **completo** do modelo (sem truncamento)

Exemplo:

- Valor formatado: `1.234,56`
- No focus: `1234,56` (apenas separador decimal, valor completo)

### Modelo vs Formatação

A diretiva separa claramente o valor armazenado do valor exibido:

| Situação           | Modelo                    | Display                             |
| ------------------ | ------------------------- | ----------------------------------- |
| Digitando          | `1.999`                   | `1,999`                             |
| Após blur          | `1.999`                   | `1,99` (truncado)                   |
| Com foco           | `1.999`                   | `1,999` (completo)                  |
| Notação científica | `0.000000000000000000001` | `0,00000000000000000000` (truncado) |

**Garantia:** O modelo **sempre** armazena o valor completo, sem truncamento. As propriedades `minDecimalPlaces` e `maxDecimalPlaces` afetam **apenas** a formatação visual.

### Blur

Quando o input perde foco:

- Reaplica a formatação completa com separadores de milhar
- Valida o valor conforme as regras configuradas

### Colagem (Paste)

- Processa automaticamente valores copiados
- Converte notação científica (ex: `1e-6`) em decimal
- Normaliza separadores para o locale configurado

## Exemplos

### Exemplo Básico

```typescript
@Component({
    selector: 'app-basic',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <input
            sNumericMask
            [locale]="'pt-BR'"
            [minDecimalPlaces]="2"
            [maxDecimalPlaces]="2"
            [(ngModel)]="value"
        />
        <p>Valor: {{ value }}</p>
    `,
})
export class BasicComponent {
    value: string | null = null;
}
```

### Exemplo com Valores Negativos

```typescript
@Component({
    selector: 'app-negative',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <input
            sNumericMask
            [allowNegative]="true"
            [locale]="'pt-BR'"
            [minDecimalPlaces]="2"
            [maxDecimalPlaces]="2"
            [(ngModel)]="value"
        />
        <p>Pressione - para alternar o sinal</p>
    `,
})
export class NegativeComponent {
    value: string | null = null;
}
```

### Exemplo com Notação Científica

```typescript
@Component({
    selector: 'app-scientific',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <input
            sNumericMask
            [allowScientificNotation]="true"
            [minDecimalPlaces]="0"
            [maxDecimalPlaces]="15"
            [(ngModel)]="value"
            placeholder="Ex: 1e-6, 1.5e-10"
        />
        <p>Valor: {{ value }}</p>
    `,
})
export class ScientificComponent {
    value: string | null = null;
}
```

### Exemplo sem Notação Científica

```typescript
@Component({
    selector: 'app-no-scientific',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <input
            sNumericMask
            [allowScientificNotation]="false"
            [locale]="'pt-BR'"
            [minDecimalPlaces]="2"
            [maxDecimalPlaces]="2"
            [(ngModel)]="value"
        />
    `,
})
export class NoScientificComponent {
    value: string | null = null;
}
```

### Exemplo com Validação Min/Max

```typescript
@Component({
    selector: 'app-min-max',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <input
            sNumericMask
            [minDecimalPlaces]="2"
            [maxDecimalPlaces]="2"
            [min]="0"
            [max]="1000"
            [(ngModel)]="price"
            #priceInput="ngModel"
        />

        @if (priceInput.errors?.['min']) {
            <span class="error"> Valor mínimo: {{ priceInput.errors?.['min'].min }} </span>
        }

        @if (priceInput.errors?.['max']) {
            <span class="error"> Valor máximo: {{ priceInput.errors?.['max'].max }} </span>
        }
    `,
})
export class MinMaxComponent {
    price: string | null = null;
}
```

### Exemplo com Alta Precisão

```typescript
@Component({
    selector: 'app-high-precision',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <input
            sNumericMask
            [locale]="'pt-BR'"
            [minDecimalPlaces]="0"
            [maxDecimalPlaces]="8"
            [(ngModel)]="cryptoValue"
        />
        <p>Valor da criptomoeda: {{ cryptoValue }}</p>
    `,
})
export class HighPrecisionComponent {
    cryptoValue: string | null = null;
}
```

## Integração com LocaleService

A diretiva se integra automaticamente com o `LocaleService` do `@seniorsistemas/angular-components/locale`. Quando o input `locale` não é fornecido, a diretiva:

1. Injeta opcionalmente o `LocaleService`
2. Chama `localeService.get()` para carregar as configurações de locale
3. Usa `getLocaleOptions()?.locale` para obter o locale do usuário
4. Usa 'pt-BR' como fallback se o serviço não estiver disponível

### Com LocaleService

```typescript
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NumericMaskDirective } from '@seniorsistemas/angular-components/numeric-mask';
import { LocaleModule } from '@seniorsistemas/angular-components/locale';

@Component({
    selector: 'app-with-locale-service',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective, LocaleModule],
    template: `
        <!-- Locale automático do usuário -->
        <input
            sNumericMask
            [minDecimalPlaces]="2"
            [maxDecimalPlaces]="2"
            [(ngModel)]="value"
        />
    `,
})
export class WithLocaleServiceComponent {
    value: string | null = null;
}
```

### Sem LocaleService (Locale Explícito)

```typescript
@Component({
    selector: 'app-explicit-locale',
    standalone: true,
    imports: [FormsModule, NumericMaskDirective],
    template: `
        <!-- Locale explícito, não usa LocaleService -->
        <input
            sNumericMask
            [locale]="'en-US'"
            [minDecimalPlaces]="2"
            [maxDecimalPlaces]="2"
            [(ngModel)]="value"
        />
    `,
})
export class ExplicitLocaleComponent {
    value: string | null = null;
}
```

## Acessibilidade

A diretiva configura automaticamente:

- `inputmode="decimal"` - Teclado numérico em dispositivos móveis
- `autocomplete="off"` - Desabilita autocomplete

Para melhor acessibilidade, adicione manualmente:

```html
<label for="price">Preço (até 2 casas decimais)</label>
<input
    id="price"
    type="text"
    sNumericMask
    [locale]="'pt-BR'"
    [minDecimalPlaces]="2"
    [maxDecimalPlaces]="2"
    [(ngModel)]="price"
    aria-label="Digite um número com até 2 casas decimais"
/>
```

## Compatibilidade

- Angular 18+
- Todos os navegadores modernos com suporte a `Intl.NumberFormat`

## Licença

MIT

