184 lines
6.2 KiB
Markdown
184 lines
6.2 KiB
Markdown
# Dialog — Padrão de Componente
|
|
|
|
> **Stack**: Vue 3 + PrimeVue 4 + Tailwind CSS
|
|
|
|
---
|
|
|
|
## 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' }` |
|
|
|
|
---
|
|
|
|
## 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)] shadow-[0_1px_0_0_rgba(255,255,255,0.06)] bg-gray-100' },
|
|
content: { class: '!p-3' },
|
|
footer: { class: '!p-0 !rounded-b-[12px] border-t border-[var(--surface-border)] shadow-[0_1px_0_0_rgba(255,255,255,0.06)] bg-gray-100' },
|
|
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` + `shadow` separador com profundidade; `bg-gray-100` fundo levemente cinza |
|
|
| `content` | `!p-3` padding interno do corpo |
|
|
| `footer` | `!p-0` remove padding nativo (controlado pelo wrapper interno); `!rounded-b-[12px]` borda bottom arredondada; `border-t` + `shadow` separador; `bg-gray-100` fundo levemente cinza |
|
|
| `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.
|
|
|
|
---
|
|
|
|
## 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">
|
|
{{ form.name || (mode === 'create' ? 'Novo item' : 'Editar item') }}
|
|
</div>
|
|
<div class="text-xs opacity-50">
|
|
{{ 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>
|
|
```
|
|
|
|
---
|
|
|
|
## 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 ...>
|
|
```
|
|
|
|
---
|
|
|
|
## 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-gray-100`, `border-b`, shadow e `!rounded-t-[12px]`
|
|
- [ ] Footer com `bg-gray-100`, `border-t`, shadow e `!rounded-b-[12px]`
|
|
- [ ] 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
|
|
|
|
---
|
|
|
|
## Variações de largura
|
|
|
|
| Uso | Classe |
|
|
|---|---|
|
|
| Formulário simples | `w-[36rem]` |
|
|
| Formulário padrão | `w-[50rem]` ← **padrão** |
|
|
| Formulário complexo | `w-[70rem]` |
|
|
| Tela cheia | `maximizable` — usuário controla |
|