Guia do Componente Drawer
Painel lateral deslizante para exibição de conteúdo adicional, formulários, detalhes e notificações.
Visão Geral
O componente Drawer é um painel que desliza a partir da borda direita da tela, sobrepondo o conteúdo principal. Ideal para:
- Formulários de edição/criação
- Painéis de detalhes
- Notificações e alertas
- Configurações
- Filtros avançados
Instalação
CSS
html
<link rel="stylesheet" href="/css/design-system/components/drawer.css">JavaScript
html
<script src="/css/design-system/components/drawer.js" defer></script>Uso Básico
Via JavaScript (Recomendado)
javascript
// Criar drawer simples
const drawer = Drawer.create({
title: 'Detalhes do Item',
content: '<p>Conteúdo do drawer aqui</p>'
});
// Fechar drawer
drawer.close();
// Ou pelo ID
Drawer.close('drawer-id');Via HTML com Data Attributes
html
<!-- Trigger -->
<button data-drawer-trigger="meu-drawer">Abrir Drawer</button>
<!-- Template do Drawer -->
<template id="meu-drawer"
data-drawer-title="Meu Título"
data-drawer-size="md">
<p>Conteúdo do drawer</p>
</template>API JavaScript
Drawer.create(options)
Cria e exibe um novo drawer.
javascript
const drawer = Drawer.create({
// Identificação
id: 'meu-drawer', // ID único (auto-gerado se omitido)
// Conteúdo
title: 'Título', // Título do header
subtitle: 'Subtítulo', // Subtítulo (opcional)
content: '<div>...</div>', // Conteúdo HTML ou HTMLElement
footer: '<button>...</button>', // Footer HTML ou HTMLElement (opcional)
// Aparência
size: 'md', // sm, md, lg, xl, full
// Comportamento
closeOnBackdrop: true, // Fecha ao clicar fora
closeOnEsc: true, // Fecha ao pressionar ESC
showClose: true, // Exibe botão X
trapFocus: true, // Mantém foco dentro do drawer
// Callbacks
onOpen: (drawer) => {}, // Ao abrir
onOpened: (drawer) => {}, // Após animação de abertura
onClose: (drawer) => {}, // Ao fechar
onClosed: (drawer) => {} // Após animação de fechamento
});Métodos da Instância
javascript
const drawer = Drawer.create({ ... });
// Fechar
drawer.close();
// Atualizar conteúdo
drawer.setTitle('Novo Título');
drawer.setContent('<p>Novo conteúdo</p>');
drawer.setFooter('<button>Novo botão</button>');
// Estado de loading
drawer.setLoading(true);
drawer.setLoading(false);
// Acessar elementos
const element = drawer.getElement();
const body = drawer.getBody();
const footer = drawer.getFooter();Métodos Estáticos
javascript
// Fechar drawer específico
Drawer.close('drawer-id');
// Fechar todos
Drawer.closeAll();
// Obter instância
const drawer = Drawer.get('drawer-id');
// Verificar se há drawers abertos
if (Drawer.hasOpen()) { ... }
// Obter drawer ativo
const active = Drawer.getActive();Tamanhos
| Classe | Largura | Uso Recomendado |
|---|---|---|
sm | 400px | Confirmações, detalhes simples |
md | 520px | Formulários médios (padrão) |
lg | 700px | Formulários complexos |
xl | 900px | Dashboards, visualizações |
full | 100% | Tela cheia |
javascript
Drawer.create({
title: 'Drawer Grande',
size: 'lg',
content: '...'
});Eventos
O drawer dispara eventos customizados que podem ser escutados:
javascript
// No elemento do drawer
document.addEventListener('drawer:open', (e) => {
console.log('Drawer abrindo:', e.detail.drawerId);
});
document.addEventListener('drawer:opened', (e) => {
console.log('Drawer aberto:', e.detail.drawerId);
});
document.addEventListener('drawer:close', (e) => {
console.log('Drawer fechando:', e.detail.drawerId);
});
document.addEventListener('drawer:closed', (e) => {
console.log('Drawer fechado:', e.detail.drawerId);
});Exemplos
Drawer com Formulário
javascript
Drawer.create({
title: 'Novo Usuário',
size: 'md',
content: `
<form class="drawer__form">
<div class="drawer__form-group">
<label class="drawer__label drawer__label--required">Nome</label>
<input type="text" class="input" name="nome" required>
</div>
<div class="drawer__form-group">
<label class="drawer__label drawer__label--required">E-mail</label>
<input type="email" class="input" name="email" required>
</div>
<div class="drawer__form-row">
<div class="drawer__form-group">
<label class="drawer__label">Telefone</label>
<input type="tel" class="input" name="telefone">
</div>
<div class="drawer__form-group">
<label class="drawer__label">CPF</label>
<input type="text" class="input" name="cpf">
</div>
</div>
</form>
`,
footer: `
<button class="btn btn-outline" data-drawer-close>Cancelar</button>
<button class="btn btn-primary" id="btn-salvar">Salvar</button>
`,
onOpened: (drawer) => {
// Focus no primeiro input
drawer.getBody().querySelector('input')?.focus();
// Bind do botão salvar
drawer.getElement().querySelector('#btn-salvar').addEventListener('click', async () => {
drawer.setLoading(true);
// ... lógica de salvamento
drawer.close();
});
}
});Drawer de Confirmação
javascript
// Usando helper
const confirmed = await createDrawerConfirm({
title: 'Confirmar Exclusão',
icon: 'delete',
message: 'Deseja realmente excluir este item?',
description: 'Esta ação não pode ser desfeita.',
confirmText: 'Sim, excluir',
cancelText: 'Cancelar',
size: 'sm'
});
if (confirmed) {
// Executar exclusão
}Drawer com Conteúdo Dinâmico
javascript
const drawer = Drawer.create({
title: 'Carregando...',
size: 'lg',
content: '<div class="drawer__empty"><div class="drawer__loading-spinner"></div></div>'
});
// Carregar conteúdo
const response = await fetch('/api/detalhes/123');
const data = await response.json();
drawer.setTitle(data.titulo);
drawer.setContent(`
<div class="drawer__section">
<h3 class="drawer__section-title">Informações</h3>
<p>${data.descricao}</p>
</div>
`);Drawer com HTMLElement
javascript
// Criar elementos programaticamente
const content = document.createElement('div');
content.innerHTML = '<p>Conteúdo criado via DOM</p>';
const footer = document.createElement('div');
footer.innerHTML = '<button class="btn btn-primary">OK</button>';
Drawer.create({
title: 'Drawer com Elements',
content: content,
footer: footer
});Estrutura HTML
html
<!-- Backdrop -->
<div class="drawer-backdrop is-active" data-drawer-backdrop="drawer-id"></div>
<!-- Drawer -->
<div class="drawer drawer--md is-active" data-drawer="drawer-id" role="dialog" aria-modal="true">
<!-- Header -->
<div class="drawer__header">
<div class="drawer__header-content">
<h2 class="drawer__title">Título</h2>
<p class="drawer__subtitle">Subtítulo opcional</p>
</div>
<button class="drawer__close" data-drawer-close aria-label="Fechar">
<i class="mdi mdi-close"></i>
</button>
</div>
<!-- Body -->
<div class="drawer__body">
<!-- Conteúdo aqui -->
</div>
<!-- Footer (opcional) -->
<div class="drawer__footer">
<button class="btn btn-outline" data-drawer-close>Cancelar</button>
<button class="btn btn-primary">Salvar</button>
</div>
</div>Classes CSS
| Classe | Descrição |
|---|---|
.drawer-backdrop | Fundo escurecido |
.drawer | Container principal |
.drawer--sm/md/lg/xl/full | Variantes de tamanho |
.drawer.is-active | Estado aberto |
.drawer__header | Cabeçalho |
.drawer__title | Título principal |
.drawer__subtitle | Subtítulo |
.drawer__close | Botão fechar |
.drawer__body | Área de conteúdo |
.drawer__footer | Rodapé com ações |
.drawer__footer--start/center/between | Alinhamento do footer |
.drawer__section | Seção de conteúdo |
.drawer__section-title | Título de seção |
.drawer__form | Container de formulário |
.drawer__form-group | Grupo de campo |
.drawer__form-row | Linha de campos (2 colunas) |
.drawer__form-row--3 | Linha de campos (3 colunas) |
.drawer__label | Label de campo |
.drawer__label--required | Label com asterisco |
.drawer__errors | Container de erros |
.drawer__empty | Estado vazio |
.drawer--loading | Estado de carregamento |
.drawer--no-header | Sem header |
.drawer--no-footer | Sem footer |
.drawer--no-padding | Body sem padding |
Dark Mode
O componente suporta dark mode automaticamente através das classes:
.darkno elemento pai[data-theme="dark"]no bodybody.dark-mode
Acessibilidade
O drawer implementa:
role="dialog"earia-modal="true"aria-labelledbyapontando para o título- Focus trap para navegação por teclado
- Fechamento via ESC
- Restauração do foco ao elemento anterior ao fechar
- Suporte a
prefers-reduced-motion
Responsivo
Em telas menores que 640px:
- Drawer ocupa 100% da largura
- Footer com botões em coluna
- Formulários em coluna única
Comparação com Modal
| Aspecto | Modal | Drawer |
|---|---|---|
| Posição | Centro da tela | Lateral (direita) |
| Animação | Scale + fade | Slide horizontal |
| Uso ideal | Confirmações, alertas | Formulários, detalhes |
| Scroll | Interno ao modal | Interno ao drawer |
| Tamanho | Limitado | Pode ser full-height |
Boas Práticas
- Use tamanhos apropriados:
smpara confirmações,mdpara formulários simples - Sempre inclua título descritivo: Ajuda na acessibilidade
- Mantenha ações no footer: Botões primários à direita
- Use loading states: Para operações assíncronas
- Evite drawers aninhados: Pode confundir o usuário
- Teste navegação por teclado: Verifique o focus trap
Troubleshooting
Drawer não abre
- Verifique se o CSS e JS foram carregados
- Verifique o console para erros
Z-index conflitante
- O drawer usa z-index 1000-1001
- Ajuste outros elementos se necessário
Scroll não funciona
- Verifique se
.drawer__bodytemoverflow-y: auto - O conteúdo precisa exceder a altura disponível
Focus não é capturado
- Verifique se
trapFocus: true(padrão) - Elementos focáveis devem estar dentro do drawer