# Datepicker

![Status](https://img.shields.io/badge/status-beta-yellow)

Componente completo de seleção de datas com campo de input e painel flutuante. Substituto direto do `p-datepicker` do PrimeNG, com API moderna baseada em Angular Signals e posicionamento nativo via CDK Overlay.

## Quando usar

- Formulários que exigem seleção de data, hora ou intervalo de datas
- Filtros de período com seleção de data de início e fim (`mode="range"`)
- Campos de hora isolados sem calendário (`mode="time"`)
- Seleção de competência (mês/ano) sem necessidade de escolher o dia (`mode="month"`)

## Quando não usar

- Para exibir datas sem interação — use `DatePipe` do Angular diretamente
- Quando o campo está dentro de um modal e o painel precisa escapar do `overflow: hidden` — o CDK Overlay já trata isso nativamente; não é necessário configuração extra

## Instalação

```typescript
import { DatepickerComponent } from '@seniorsistemas/angular-components/datepicker';

@Component({
  standalone: true,
  imports: [DatepickerComponent],
})
export class MeuComponent {}
```

## Uso básico

```html
<s-datepicker [formControl]="dataCtrl" mode="date" />
```

## API

### Inputs

| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|---|---|---|:-----------:|---|
| `inputId` | `string` | auto-gerado | Não | HTML `id` aplicado ao elemento `<input>`. |
| `name` | `string` | `undefined` | Não | Atributo `name` do `<input>` nativo. |
| `placeholder` | `string` | `undefined` | Não | Texto exibido quando nenhum valor está selecionado. |
| `mode` | `DatePickerMode` | `'date'` | Não | Modo de seleção do componente. Ver tabela de modos abaixo. |
| `selectionMode` | `'single' \| 'multiple'` | `'single'` | Não | Modo de seleção quando `mode="date"`. `'multiple'` permite selecionar várias datas avulsas (valor `Date[]`). |
| `multipleSeparator` | `string` | `', '` | Não | Separador exibido entre as datas no modo `selectionMode="multiple"`. |
| `showIcon` | `boolean` | `true` | Não | Exibe o botão de ícone ao lado do campo. |
| `icon` | `string` | `'fas fa-calendar'` | Não | Classe CSS do ícone. No modo `time`, usa `'fas fa-clock'` automaticamente. |
| `min` | `Date \| null` | `null` | Não | Data mínima selecionável. Dias anteriores ficam desabilitados. |
| `max` | `Date \| null` | `null` | Não | Data máxima selecionável. Dias posteriores ficam desabilitados. |
| `defaultDate` | `Date \| null` | `null` | Não | Data exibida ao abrir o painel quando nenhuma data está selecionada. |
| `dateFormat` | `string` | resolvido do locale, fallback `'dd/MM/yyyy'` | Não | Formato de exibição da data (tokens `dd`, `MM`, `yyyy`). Quando omitido, é resolvido a partir do locale ativo (chave de tradução `platform.angular_components.date_format`). Vale para todos os modos exceto `year`. |
| `hourFormat` | `'12' \| '24'` | resolvido do locale, fallback `'24'` | Não | Formato de exibição das horas. Quando omitido, é resolvido a partir do locale ativo (chave de tradução `platform.angular_components.hour_format`). |
| `minuteStep` | `number` | `5` | Não | Incremento em minutos ao usar as setas do time picker. |
| `showSeconds` | `boolean` | `false` | Não | Exibe o campo de segundos no time picker. |
| `rangeSeparator` | `string` | `' — '` | Não | Separador exibido entre as datas no modo `range`. |
| `showOnFocus` | `boolean` | `true` | Não | Abre o painel ao focar o campo. O foco permanece no input, não pula automaticamente para o dropdown. |
| `readonlyInput` | `boolean` | `false` | Não | Impede digitação manual; o valor só pode ser alterado pelo calendário. |
| `firstDayOfWeek` | `number` | `0` | Não | Primeiro dia da semana exibido no calendário (`0` = domingo, `1` = segunda-feira, ..., `6` = sábado). |
| `showButtonBar` | `boolean` | `true` | Não | Exibe a barra de botões "Hoje" e "Limpar" no rodapé do painel. |
| `showClear` | `boolean` | `false` | Não | Exibe ícone de limpar inline no campo de input. |
| `showOtherMonths` | `boolean` | `true` | Não | Exibe dias de meses adjacentes na grade do calendário. |
| `selectOtherMonths` | `boolean` | `false` | Não | Permite selecionar dias de meses adjacentes exibidos na grade. |
| `hideOnDateTimeSelect` | `boolean` | `false` | Não | Fecha o painel automaticamente após selecionar uma data no modo `datetime` (por padrão, esse modo só fecha ao confirmar). |
| `stepHour` | `number` | `1` | Não | Incremento de horas ao usar as setas do time picker. |
| `stepSecond` | `number` | `1` | Não | Incremento de segundos ao usar as setas do time picker. |
| `tabindex` | `number` | `undefined` | Não | Índice de tabulação do campo de input. |
| `autofocus` | `boolean` | `false` | Não | Foca automaticamente o campo ao carregar o componente. |
| `disabledDates` | `Date[]` | `[]` | Não | Datas específicas desabilitadas no calendário. |
| `disabledDays` | `number[]` | `[]` | Não | Dias da semana desabilitados (`0` = domingo, ..., `6` = sábado). |
| `disabled` | `boolean` | `false` | Não | Desabilita o componente (model two-way). |

### Outputs

| Evento | Tipo emitido | Descrição |
|---|---|---|
| `blurred` | `Event` | Emitido quando o campo perde o foco. |
| `focused` | `Event` | Emitido quando o campo recebe o foco. |
| `selected` | `Date \| DateRange \| Date[] \| null` | Emitido ao selecionar uma data, mês, horário, intervalo ou lista de datas (`selectionMode="multiple"`). |
| `closed` | `HTMLElement` | Emitido quando o painel do calendário fecha. Emite o elemento HTML do painel. |
| `show` | `void` | Emitido quando o painel do calendário abre. |
| `valueInput` | `Event` | Emitido quando o usuário digita manualmente no campo. |
| `todayClick` | `Date` | Emitido ao clicar no botão "Hoje". |
| `clearClick` | `MouseEvent` | Emitido ao clicar no botão "Limpar" ou no ícone de limpar inline. |
| `monthChange` | `{ month: number; year: number }` | Emitido quando o mês visualizado muda. |
| `yearChange` | `{ month: number; year: number }` | Emitido quando o ano visualizado muda. |

### Tipos

```typescript
type DatePickerMode = 'date' | 'month' | 'year' | 'datetime' | 'time' | 'range';

interface DateRange {
  start: Date | null;
  end: Date | null;
}
```

### Modos (`DatePickerMode`)

| Modo | Valor emitido | Descrição |
|---|---|---|
| `date` | `Date \| Date[]` | Calendário com seleção de dia (padrão). Com `selectionMode="multiple"`, emite `Date[]`. |
| `month` | `Date` | Seleção de mês e ano — o `Date` emitido aponta para o primeiro dia do mês. |
| `year` | `Date` | Seleção apenas de ano — o `Date` emitido aponta para 1º de janeiro do ano escolhido. |
| `datetime` | `Date` | Calendário + time picker. O painel só fecha ao clicar em "Confirmar" (ou automaticamente se `hideOnDateTimeSelect="true"`). |
| `time` | `Date` | Apenas seletor de hora, sem calendário. |
| `range` | `DateRange` | Seleção de intervalo com data de início e fim. |

## Exemplos

### Com `formControl`

```html
<s-datepicker [formControl]="dataCtrl" mode="date" />
```

```typescript
dataCtrl = new FormControl<Date | null>(null, Validators.required);
```

### Com `ngModel`

```html
<s-datepicker [(ngModel)]="dataSelecionada" mode="datetime" />
```

### Data e hora

```html
<s-datepicker [formControl]="ctrl" mode="datetime" [minuteStep]="15" [showSeconds]="true" />
```

### Intervalo de datas

```html
<s-datepicker [formControl]="periodoCtrl" mode="range" rangeSeparator=" até " />
```

```typescript
periodoCtrl = new FormControl<DateRange | null>(null);
```

### Com limites de data

```html
<s-datepicker [formControl]="ctrl" mode="date" [min]="dataMinima" [max]="dataMaxima" />
```

### Seleção de mês

```html
<s-datepicker [formControl]="competenciaCtrl" mode="month" placeholder="Selecione a competência" />
```

### Seleção de ano

```html
<s-datepicker [formControl]="anoCtrl" mode="year" placeholder="Selecione o ano" />
```

### Seleção múltipla de datas avulsas

```html
<s-datepicker [formControl]="datasCtrl" mode="date" selectionMode="multiple" multipleSeparator=", " />
```

```typescript
datasCtrl = new FormControl<Date[] | null>(null);
```

### Apenas hora

```html
<s-datepicker [formControl]="horaCtrl" mode="time" [minuteStep]="1" [showSeconds]="true" />
```

### Com Dynamic Form

```typescript
const config: CalendarFieldConfig = {
  type: 'date',
  name: 'dataNascimento',
  label: 'Data de nascimento',
};
```

```html
<s-dynamic-form [config]="config" />
```

## Digitação por segmento

Nos modos `date`, `month`, `time` e `datetime`, o campo não usa `<input type="date">` nativo — o formato exibido segue o `dateFormat`/`hourFormat` (explícito ou resolvido do locale via ngx-translate), não o locale do browser/SO. A digitação replica o comportamento nativo:

- Clicar em um segmento (dia, mês, ano, hora, minuto, segundo) seleciona o segmento inteiro
- `Tab`/`Shift+Tab` e `←`/`→` navegam entre os segmentos sem sair do campo; nas bordas (último/primeiro segmento), o `Tab` volta a se comportar normalmente e move o foco para fora do campo, enquanto `←`/`→` simplesmente não fazem nada (como num `<input type="date">` nativo)
- `↑`/`↓` incrementam/decrementam o valor do segmento ativo, dando a volta (wrap) dentro do range válido
- Digitar um número preenche o segmento e avança automaticamente ao completar (ex.: digitar `1`, `5` no dia avança para o mês)
- Ao sair de um segmento incompleto (Tab, clique fora ou avanço automático), o valor é completado com zero à esquerda e ajustado aos limites (ex.: `2` no mês vira `02`; um dia maior que o total de dias do mês é reduzido para o último dia válido, considerando ano bissexto)
- Alterar o mês ou o ano com o dia já preenchido reajusta o dia automaticamente se ele deixar de ser válido (ex.: dia 31 + mês fevereiro → dia 28/29)

Em dispositivos touch (`(pointer: coarse)`), o campo cai automaticamente para o mesmo comportamento de `mode="range"`/`mode="year"` — somente leitura, com seleção exclusiva pelo painel do calendário — evitando abrir o teclado do aparelho no lugar do seletor nativo.

## Acessibilidade

- O trigger usa `role="combobox"` com `aria-haspopup="grid"`, `aria-expanded` e `aria-controls` apontando para o painel
- O painel usa `role="dialog"` com `aria-label` traduzido e `cdkTrapFocus` para prender o foco dentro do popup
- Ao abrir o painel, o foco é movido automaticamente para o primeiro botão do calendário ou para o botão de incremento de horas no modo `time`
- Ao fechar o painel, o foco retorna ao botão de ícone do input trigger
- Tecla `Escape` fecha o painel e devolve o foco ao trigger
- Dias do calendário usam `role="gridcell"` com `aria-label` contendo a data por extenso, `aria-selected` e `aria-disabled`
- Cabeçalhos dos dias da semana usam `role="columnheader"` com `aria-label`
- Setas do time picker têm `aria-label` descritivos; as horas/minutos têm `aria-live="polite"`
- Todos os ícones decorativos têm `aria-hidden="true"`

## Componentes relacionados

- [`DynamicFormComponent`](../dynamic-form/README.md) — usa `s-datepicker` internamente para campos do tipo `date`, `time`, `dateTime` e `localDateTime`
