# Drawer

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

Componente de painel lateral deslizante que aparece sobre o conteúdo vindo do lado direito da tela. Suporta templates customizados para cabeçalho, corpo e rodapé via `sTemplate`, bloqueio automático de scroll e confirmação de fechamento.

Também responde ao seletor legado `s-sidebar` (deprecado, será removido na versão 20.0.0 — ver [`Sidebar`](../sidebar/README.md)).

## Quando usar

- Exibir detalhes de um item selecionado sem sair da página atual
- Formulários simples de edição rápida sem necessidade de uma nova rota
- Painéis de filtros avançados em listas e tabelas
- Configurações ou propriedades de um elemento da interface
- Fluxos complementares que não devem interromper o contexto principal

## Quando não usar

- Confirmações de ações destrutivas — prefira `ConfirmDialog`
- Conteúdo crítico que exige atenção exclusiva do usuário — prefira `Dialog` modal
- Navegação principal da aplicação — prefira um menu lateral (`nav`) fixo

## Instalação

```typescript
import { DrawerModule } from '@seniorsistemas/angular-components/drawer';

@NgModule({ imports: [DrawerModule] })
export class MeuModule {}
```

## Uso básico

```html
<s-drawer [(visible)]="drawerAberto" header="Detalhes">
  <p>Conteúdo do painel lateral.</p>
</s-drawer>

<button (click)="drawerAberto = true">Abrir Drawer</button>
```

## API

### Inputs

| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|-------------|------|--------|:-----------:|-----------|
| `visible` | `ModelSignal<boolean>` | `false` | Não | Controla a visibilidade do drawer. Suporta `[(visible)]` |
| `closable` | `boolean` | `true` | Não | Exibe o botão de fechar (X) no cabeçalho |
| `dismissible` | `boolean` | `true` | Não | Fechar ao clicar no backdrop (área escurecida fora do painel) |
| `closeOnEscape` | `boolean` | `true` | Não | Fechar ao pressionar a tecla Escape |
| `header` | `string` | `undefined` | Não | Texto exibido no cabeçalho padrão. Substituível via `sTemplate="header"` |
| `ariaLabel` | `string` | `undefined` | Não | Nome acessível do drawer quando o cabeçalho é um template customizado via `sTemplate="header"`. Ignorado quando `header` está definido |
| `largeSized` | `boolean` | `false` | Não | Aumenta a largura do drawer para acomodar mais conteúdo |
| `cache` | `boolean` | `false` | Não | Mantém o conteúdo no DOM quando fechado, preservando estado interno |
| `registerConfirmClose` | `() => boolean` | `() => true` | Não | Função chamada antes de fechar; retornar `false` bloqueia o fechamento |

## Exemplos

### Drawer básico

```html
<s-drawer [(visible)]="visible" header="Filtros Avançados" [closable]="true">
  <p>Conteúdo do painel.</p>
</s-drawer>
<s-button label="Abrir" (clicked)="visible = true"></s-button>
```

### Drawer grande

```html
<s-drawer [(visible)]="visible" header="Edição de Registro" [largeSized]="true">
  <!-- formulário complexo -->
</s-drawer>
```

### Com templates customizados

```html
<s-drawer [(visible)]="visible">
  <ng-template sTemplate="header">
    <div style="display: flex; align-items: center; gap: 8px;">
      <i class="fa fa-filter"></i>
      <span>Filtros Avançados</span>
    </div>
  </ng-template>
  <ng-template sTemplate="body">
    <!-- conteúdo do painel -->
  </ng-template>
  <ng-template sTemplate="footer">
    <s-button label="Aplicar" priority="primary" (clicked)="visible = false"></s-button>
  </ng-template>
</s-drawer>
```

### Com confirmação de fechamento

```typescript
confirmClose = (): boolean => {
  return window.confirm('Deseja realmente fechar? As alterações serão perdidas.');
};
```

```html
<s-drawer [(visible)]="visible" [registerConfirmClose]="confirmClose">
  <!-- formulário com alterações não salvas -->
</s-drawer>
```

## Acessibilidade

- O drawer utiliza `cdkTrapFocus` para aprisionar o foco dentro do painel enquanto aberto
- Enquanto visível, o painel recebe `role="dialog"` e `aria-modal="true"`
- O nome acessível é associado via `aria-labelledby`, apontando para o cabeçalho (`header`) renderizado como `<h2>`. Ao usar `sTemplate="header"`, forneça `ariaLabel` para que o painel continue tendo um nome acessível
- Pressionar `Escape` fecha o drawer quando `closeOnEscape = true`, mas apenas se a instância estiver visível — instâncias fechadas (inclusive as mantidas no DOM por `cache = true`) ignoram o Escape, mesmo com múltiplas instâncias de `s-drawer` montadas na página
- Se houver duas instâncias visíveis simultaneamente, um único Escape fecha as duas (a listener é por instância, sem controle centralizado de qual está "no topo")
- O painel entra e sai com animação de deslizamento (300ms) vindo da direita
- Ao abrir, o scroll da página é bloqueado automaticamente e restaurado ao fechar. O bloqueio é compartilhado com outros overlays da biblioteca (ex: `SideSheet`) e com outras instâncias de `s-drawer` — fechar um não reativa o scroll enquanto outro ainda estiver aberto, e o valor de `overflow` anterior ao primeiro bloqueio é preservado
- Ao fechar, o foco retorna automaticamente ao elemento que estava focado antes da abertura (ex: o botão que abriu o drawer)
- Com `cache = true`, o painel permanece no DOM enquanto oculto, mas recebe `inert` e `aria-hidden="true"`, e o focus trap é desabilitado — o conteúdo escondido não fica alcançável por teclado ou leitor de tela. Use `cache` apenas quando o estado interno do conteúdo realmente precisa ser preservado entre aberturas
- Como o backdrop (`dismissible`) só fecha por clique/toque, sem equivalente de teclado, definir `[closable]="false"` junto com `[closeOnEscape]="false"` remove todo caminho de fechamento acessível por teclado, a menos que o conteúdo customizado (`body`/`footer`) forneça o seu próprio. É responsabilidade de quem consome o componente garantir esse caminho ao optar por essa combinação

## Componentes relacionados

- [`Dialog`](../dialog/README.md) — modal centralizado para confirmações e conteúdo crítico
- [`SlideInBar`](../slide-in-bar/README.md) — painel que expande/colapsa embutido no layout
