# Dialog — Padrão de Componente

> **Stack**: Vue 3 + PrimeVue 4 + Tailwind CSS
> **Tema-aware**: header e footer respeitam dark/light automaticamente via CSS vars

---

## Regras gerais

| Propriedade | Valor obrigatório |
|---|---|
| `modal` | sempre `true` |
| `maximizable` | sempre presente — botão nativo do PrimeVue, sem estado manual |
| `:draggable` | sempre `false` |
| `:closable` | `!saving` — desabilita o X durante operações assíncronas |
| `:dismissableMask` | `!saving` — impede fechar clicando fora durante saving |
| `pt:mask:class` | `backdrop-blur-xs` |
| Largura | `w-[50rem]` (padrão); responsivo via `:breakpoints` |
| Breakpoints | `{ '1199px': '90vw', '768px': '94vw' }` |

---

## Sistema de cores (tema-aware)

O dialog **nunca** deve usar `bg-gray-100` ou cores hardcoded — isso quebra no dark mode.
Usar sempre as CSS vars do projeto:

| Var | Light | Dark | Uso |
|---|---|---|---|
| `--surface-card` | `--p-surface-0` (branco) | `--p-surface-900` (quase preto) | Fundo do **corpo** do dialog (default) |
| `--surface-ground` | `--p-surface-100` (cinza claro) | `--p-surface-950` (preto) | Fundo do **header** e **footer** — um shade mais escuro que o card |
| `--surface-border` | `--p-content-border-color` | idem | Borda separadora entre header/content/footer |
| `--text-color` | preto | branco | Título principal |
| `--text-color-secondary` | cinza médio | cinza claro | Subtítulo, hints |

> Resumo: `bg-[var(--surface-ground)]` no header/footer fica **sempre um pouco mais escuro que o corpo**, em ambos os temas. Definido em `_light.scss:19` e `_dark.scss:19`.

---

## Estrutura obrigatória

```
<Dialog>
  ├── #header   ← dot de cor (se aplicável), título/subtítulo, btn Excluir
  ├── Banner    ← preview visual (opcional — apenas quando há cor/identidade visual)
  ├── Corpo     ← campos do formulário
  └── #footer   ← Cancelar (flat) | Salvar (primary)
```

---

## Configuração completa do `<Dialog>`

```vue
<Dialog
  v-model:visible="visible"
  modal
  :draggable="false"
  :closable="!saving"
  :dismissableMask="!saving"
  maximizable
  class="dc-dialog w-[50rem]"
  :breakpoints="{ '1199px': '90vw', '768px': '94vw' }"
  :pt="{
    header:           { class: '!p-3 !rounded-t-[12px] border-b border-[var(--surface-border)] bg-[var(--surface-ground)]' },
    content:          { class: '!p-3' },
    footer:           { class: '!p-0 !rounded-b-[12px] border-t border-[var(--surface-border)] bg-[var(--surface-ground)]' },
    pcCloseButton:    { root: { class: '!rounded-md hover:!text-red-500' } },
    pcMaximizeButton: { root: { class: '!rounded-md hover:!text-primary' } },
  }"
  pt:mask:class="backdrop-blur-xs"
>
```

### Detalhes do `pt`

| Chave | O que faz |
|---|---|
| `header` | `!p-3` padding uniforme; `!rounded-t-[12px]` borda top arredondada; `border-b` separador; `bg-[var(--surface-ground)]` fundo um shade mais escuro que o card (tema-aware) |
| `content` | `!p-3` padding interno do corpo (herda `bg-[var(--surface-card)]` do PrimeVue) |
| `footer` | `!p-0` remove padding nativo (controlado pelo wrapper interno); `!rounded-b-[12px]` borda bottom arredondada; `border-t` separador; `bg-[var(--surface-ground)]` mesmo fundo do header |
| `pcCloseButton` | `!rounded-md` remove o círculo nativo; `hover:!text-red-500` feedback de danger no hover |
| `pcMaximizeButton` | `!rounded-md` remove o círculo nativo; `hover:!text-primary` feedback de cor primária no hover |

> O `!` (important) é necessário porque o PrimeVue injeta estilos inline nos botões e no root do Dialog — sem ele o Tailwind perde a disputa de especificidade.

> **Migração de dialogs antigos**: trocar `bg-gray-100` por `bg-[var(--surface-ground)]`. O `shadow-[0_1px_0_0_rgba(255,255,255,0.06)]` antigo era um hack pro dark mode; pode ser removido (a borda já dá a separação).

---

## Header — slot `#header`

```
[dot-cor]  [título / subtítulo]          [btn-excluir]   ← Close e Maximize nativos vêm após
```

- O PrimeVue injeta **Maximize** e **Close** automaticamente à direita do slot `#header`.
- O botão **Excluir** fica **sempre no header**, nunca no footer.
- Excluir desabilitado quando o registro é nativo/padrão: `:disabled="saving || isNativeRecord"`.

```vue
<template #header>
  <div class="flex w-full items-center justify-between gap-3 px-1">
    <div class="flex items-center gap-3 min-w-0">
      <!-- Dot de cor (omitir se não houver cor associada) -->
      <span
        class="shrink-0 w-3.5 h-3.5 rounded-full border-2 border-white/30
               shadow-[0_0_0_3px_rgba(0,0,0,0.08)] transition-colors duration-200"
        :style="{ backgroundColor: previewBgColor }"
      />
      <div class="min-w-0">
        <div class="text-base font-semibold truncate text-[var(--text-color)]">
          {{ form.name || (mode === 'create' ? 'Novo item' : 'Editar item') }}
        </div>
        <div class="text-xs text-[var(--text-color-secondary)]">
          {{ mode === 'create' ? 'Criando novo registro' : 'Editando registro' }}
        </div>
      </div>
    </div>

    <div class="flex items-center gap-1 shrink-0">
      <!-- Excluir — visível apenas em edit, desabilitado se nativo -->
      <Button
        v-if="mode === 'edit' && canDelete !== undefined"
        icon="pi pi-trash"
        severity="danger"
        text
        rounded
        :disabled="saving || isNativeRecord"
        v-tooltip.top="'Excluir'"
        @click="emitDelete"
      />
      <!-- Maximize e Close nativos do PrimeVue são injetados aqui automaticamente -->
    </div>
  </div>
</template>
```

> **Cores**: usar `text-[var(--text-color)]` no título e `text-[var(--text-color-secondary)]` no subtítulo. Não usar `opacity-50` — a cor secondary já tem contraste calibrado por tema.

---

## Footer — slot `#footer`

```vue
<template #footer>
  <div class="flex items-center justify-end gap-2 px-3 py-3">
    <!-- Cancelar: sempre flat, hover vermelho suave -->
    <Button
      label="Cancelar"
      severity="secondary"
      text
      class="rounded-full hover:!text-red-500"
      :disabled="saving"
      @click="close"
    />
    <!-- Salvar: sempre primary -->
    <Button
      label="Salvar"
      icon="pi pi-check"
      class="rounded-full"
      :loading="saving"
      :disabled="!canSubmit"
      @click="submit"
    />
  </div>
</template>
```

> **Regra**: Cancelar = `severity="secondary" text` + `hover:!text-red-500`. Salvar = primary (sem severity, usa o padrão do tema). Padding controlado pelo `div` interno (`px-3 py-3`), não pelo `pt.footer`.

---

## Maximizar

Use a prop nativa `maximizable`. O PrimeVue injeta e gerencia o botão automaticamente — sem `ref`, sem `isMaximized`, sem `<Button>` manual.

```vue
<Dialog maximizable ...>
```

Se você precisar customizar a largura/altura quando maximizado (ex: `100vw`), use `:style` reativo a um ref `maximized` E passe `:maximizable="false"` + um botão manual no `#header`. Padrão preferido: deixar o PrimeVue gerenciar.

---

## Dialogs aninhados (Dialog dentro de Dialog)

Quando um Dialog secundário (criar tag, criar grupo, criar convênio) é aberto a partir do form de um Dialog principal:

- Cada Dialog é independente — `v-model:visible` próprio
- O Dialog secundário usa o **mesmo blueprint** (mesmo `pt`, mesmas cores)
- Pode ser menor: `w-[36rem]` é o tamanho típico de "cadastro rápido"
- Z-index: PrimeVue gerencia automaticamente (último aberto fica em cima)
- Ao salvar no Dialog secundário, o item criado pode ser auto-selecionado no Dialog principal (UX comum em formulários grandes)

---

## Checklist antes de publicar um Dialog

- [ ] `modal`, `:draggable="false"`, `:closable="!saving"`, `:dismissableMask="!saving"` presentes
- [ ] `maximizable` na prop (botão nativo, sem estado manual)
- [ ] `class="dc-dialog w-[50rem]"` + `:breakpoints="{ '1199px': '90vw', '768px': '94vw' }"`
- [ ] `pt` completo: header, content, footer, pcCloseButton, pcMaximizeButton
- [ ] Header com `bg-[var(--surface-ground)]`, `border-b`, e `!rounded-t-[12px]`
- [ ] Footer com `bg-[var(--surface-ground)]`, `border-t`, e `!rounded-b-[12px]`
- [ ] **Nenhum `bg-gray-100` ou cor hardcoded** — só CSS vars tema-aware
- [ ] Botão **Excluir** no header (nunca no footer), desabilitado se nativo
- [ ] Cancelar = `text` + `hover:!text-red-500` | Salvar = primary
- [ ] Padding do footer via `px-3 py-3` no `div` interno
- [ ] Texto usa `text-[var(--text-color)]` e `text-[var(--text-color-secondary)]`

---

## Variações de largura

| Uso | Classe |
|---|---|
| Cadastro rápido / formulário simples | `w-[36rem]` |
| Formulário padrão | `w-[50rem]` ← **padrão** |
| Formulário complexo (multi-coluna) | `w-[70rem]` |
| Cadastro completo (paciente, agenda) | `w-[1100px]` |
| Tela cheia | `maximizable` — usuário controla |

---

## Anti-pattern

```vue
<!-- ❌ NÃO fazer: -->
<Dialog :pt="{
  header: { class: 'bg-gray-100' },           // quebra no dark
  footer: { class: 'bg-gray-100' },           // quebra no dark
}" />

<!-- ❌ NÃO fazer: -->
<div class="text-base opacity-50">subtítulo</div>  <!-- usar text-color-secondary -->
```

```vue
<!-- ✅ Pattern correto: -->
<Dialog :pt="{
  header: { class: 'bg-[var(--surface-ground)] border-b border-[var(--surface-border)]' },
  footer: { class: 'bg-[var(--surface-ground)] border-t border-[var(--surface-border)]' },
}" />

<div class="text-xs text-[var(--text-color-secondary)]">subtítulo</div>
```
