Dialog
Import
import { Dialog } from '@pzeta/vue-components'
// oppure con plugin selettivo:
import { OverlaysPlugin } from '@pzeta/vue-components'
app.use(OverlaysPlugin)
Esempio Base
Props
| Prop | Tipo | Default | Descrizione |
|---|---|---|---|
modelValue | boolean | false | Visibilità del dialog (v-model) |
visible | boolean | false | Alias per modelValue (v-model:visible, compatibilità PrimeVue) |
header | string | null | null | Testo del titolo nell'header |
icon | string | null | null | Classe CSS dell'icona nell'header (es. pi pi-info) |
modal | boolean | true | Mostra overlay scuro dietro il dialog |
position | DialogPosition | 'center' | Posizione del dialog sullo schermo |
size | DialogSize | 'medium' | Larghezza massima del dialog (vedi Larghezza) |
closable | boolean | true | Mostra il pulsante di chiusura |
closeIcon | string | — | Classe CSS icona di chiusura (default dal ConfigPlugin) |
closeAriaLabel | string | — | Nome accessibile del pulsante di chiusura. Default "Chiudi" (i18n overlay.closeDialogAriaLabel); utile per un nome esplicito come "Chiudi Nuovo lead" |
dismissableMask | boolean | false | Chiude al click sull'overlay. Dalla 1.9.0 il default è false |
closeOnEscape | boolean | true | Chiude alla pressione di Escape |
blockScroll | boolean | true | Blocca lo scroll del body quando aperto |
showHeader | boolean | true | Mostra la sezione header |
draggable | boolean | true | Permette di trascinare il dialog dall'header. Dalla 1.9.0 il default è true |
maximizable | boolean | false | Mostra pulsante per massimizzare a schermo intero |
keepInViewport | boolean | true | Impedisce al dialog di uscire dai bordi del viewport durante il drag |
responsive | boolean | true | Abilita comportamento responsive su schermi piccoli |
breakpoints | Record<string, string> | null | null | Larghezza per fasce di viewport: chiave = larghezza massima della viewport, valore = larghezza del dialog (es. { '960px': '75vw', '640px': '90vw' }). Vale la fascia più stretta che contiene la viewport. Dalla 1.22.0 (prima era dichiarata ma senza effetto) |
style | string | Record<string, string | number> | null | null | Stile inline per il container del dialog. Una width sostituisce il limite della taglia, fino alla viewport (dalla 1.22.0) |
contentStyle | string | Record<string, string | number> | null | null | Stile inline per l'area contenuto |
contentClass | string | null | null | Classe CSS aggiuntiva per l'area contenuto |
DialogPosition
type DialogPosition =
| 'center' // Centro schermo (default)
| 'top' // Centro in alto
| 'bottom' // Centro in basso
| 'left' // Centro a sinistra
| 'right' // Centro a destra
| 'topleft' // Angolo superiore sinistro
| 'topright' // Angolo superiore destro
| 'bottomleft' // Angolo inferiore sinistro
| 'bottomright' // Angolo inferiore destro
DialogSize
type DialogSize = 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'
// larghezza massima: small 24rem (384px) | medium 28rem (448px, default)
// large 32rem (512px) | xlarge 42rem (672px) | xxlarge 64rem (1024px, dalla 1.22.0)
Larghezza
size fissa la larghezza massima. Una larghezza esplicita la sostituisce: con una width
nello style, o con una fascia di breakpoints attiva, il dialog prende quella larghezza fino
al bordo della viewport (meno il padding dell'overlay). Un maxWidth passato nello style
resta del chiamante. Una fascia di breakpoints prevale sulla width dello style; a dialog
massimizzato le fasce non si applicano.
Prima della 1.22.0 la max-width della taglia vinceva in silenzio: :style="{ width: '60rem' }"
si fermava a 28rem, e anche xlarge non superava i 42rem.
Emits
| Evento | Payload | Descrizione |
|---|---|---|
update:modelValue | boolean | Aggiornamento visibilità per v-model |
update:visible | boolean | Aggiornamento visibilità per v-model:visible |
show | — | Dialog reso visibile |
hide | — | Dialog nascosto |
close | Event | Dialog chiuso tramite pulsante o ESC |
maximize | Event | Dialog massimizzato |
unmaximize | Event | Dialog ripristinato dalla modalità massimizzata |
maskClick | Event | Click sull'overlay esterno. Emesso a ogni click sulla mask, anche quando non chiude il dialog |
Slot
| Slot | Scope | Descrizione |
|---|---|---|
default | — | Contenuto principale del dialog |
header | { close: () => void } | Sostituzione completa dell'header |
footer | — | Area footer (tipicamente pulsanti azione) |
closeicon | — | Icona personalizzata per il pulsante di chiusura |
maximizeicon | { maximized: boolean } | Icona personalizzata per il pulsante maximize/restore |
Chiusura e trascinamento
Dalla versione 1.9.0 il Dialog si comporta come una finestra: non si chiude al click fuori e si trascina dall'header. I due default proteggono lo stesso caso d'uso — un dialogo che ospita un form — dove un click accidentale scartava quanto digitato e la finestra copriva i dati necessari a compilarla.
| Gesto | Comportamento |
|---|---|
| Click sulla mask | Il dialog resta aperto e pulsa brevemente; maskClick viene emesso |
Escape | Chiude (disattivabile con :close-on-escape="false"). Con la tendina di un campo aperta chiude prima solo la tendina (vedi sotto) |
| Pulsante X | Chiude (rimuovibile con :closable="false") |
| Trascinamento dall'header | Sposta il dialog, entro i bordi del viewport |
Escape: un livello alla volta
Dalla 1.22.0, con la tendina di un campo aperta (Select, MultiSelect, TreeSelect,
AutoComplete, DatePicker, DateTimePicker, TimePicker) Escape chiude solo la
tendina; il dialog si chiude al tasto successivo. Prima si chiudevano insieme e quanto
scritto nel form andava perso. Lo stesso vale per l'annullamento di un trascinamento da
tastiera in OrderList e nella colonna rowReorder del DataTable.
Dalla 1.24.0 vale anche quando il focus non è nel campo: dopo il clic su un'opzione non
focalizzabile, o sul pulsante che apre il calendario, il focus finiva sul body e l'Escape non
passava dai gestori della tendina, chiudendo di nuovo anche il dialog. Ora a pannello aperto un
listener in fase di cattura su document chiude la tendina e ferma l'evento prima del dialog; se il
focus era perso torna sul campo. Nella MultiSelect, inoltre, un clic su un'opzione non toglie più il
focus al campo.
Un componente custom con un pannello teleportato deve fare lo stesso: a pannello aperto,
event.stopPropagation() sull'Escape che lo chiude. closeDropdownOnEscape di
useDropdownLifecycle lo fa già; per il caso del focus
fuori dal campo c'è useEscapeWhileOpen, già incluso in
useDropdownLifecycle.
Anche il Tab nei popup di DatePicker, DateTimePicker e TimePicker non arriva più al focus
trap del dialog, che riportava il focus nel dialog: il popup è teleportato fuori, e ha il suo trap.
Riattivare la chiusura dal backdrop
Per un dialogo di sola lettura, senza campi da compilare:
Reagire al click sulla mask senza chiudere
maskClick è emesso a ogni click sull'overlay, dismissibile o meno: utile per avvisare che
ci sono modifiche non salvate.
Dialog fisso al centro
:draggable="false" disattiva il trascinamento e riporta l'header a testo selezionabile.
Pulsazione di attenzione
Quando la mask non chiude, il pannello esegue una breve pulsazione (classe
hs-overlay-attention) invece di non reagire affatto: senza riscontro visivo l'interfaccia
sembra bloccata. L'animazione agisce sulla proprietà CSS scale — indipendente dal
transform usato dal trascinamento, che quindi non salta — ed è disattivata sotto
prefers-reduced-motion: reduce.
Migrazione da versioni precedenti alla 1.9.0
<!-- Prima della 1.9.0: chiudeva dal backdrop, non si trascinava -->
<Dialog v-model:visible="visible" header="Titolo" />
<!-- Stesso comportamento sulla 1.9.0 -->
<Dialog v-model:visible="visible" header="Titolo" dismissable-mask :draggable="false" />
Esempi
Dialog con posizione e dimensione
Dialog maximizable e draggable
Dialog senza header (solo close floating)
Compatibilità v-model:visible (pattern PrimeVue)
<Dialog v-model:visible="isOpen" header="Titolo">
<p>Contenuto</p>
</Dialog>
Alias deprecati
Il componente Modal è un alias backward-compatible che punta a Dialog. I tipi correlati sono anch'essi alias deprecati.
// Deprecato — usare Dialog
import { Modal } from '@pzeta/vue-components'
// Preferire:
import { Dialog } from '@pzeta/vue-components'
// Tipi deprecati
import type { ModalProps, ModalEmits, ModalPosition, ModalSize } from '@pzeta/vue-components'
// Preferire:
import type { DialogProps, DialogEmits, DialogPosition, DialogSize } from '@pzeta/vue-components'
Accessibilità
- Renderizzato con
role="dialog"earia-modal="true" aria-labelledbycollegato automaticamente all'ID del titolo nell'headeraria-describedbycollegato automaticamente all'ID dell'area contenuto- Il pulsante di chiusura (in header o floating) si chiama "Chiudi" (i18n
overlay.closeDialogAriaLabel; prima era "Chiudi dialog"): il contesto lo dà il nome del dialog, annunciato all'apertura. Per un nome diverso usarecloseAriaLabel - Chiusura tramite tasto
Escape(disabilitabile con:close-on-escape="false"); con la tendina di un campo aperta, Escape chiude prima la tendina - Blocco scroll del body quando aperto (disabilitabile con
:block-scroll="false") - Il componente usa
<Teleport to="body">per garantire corretto z-index e stacking context
TypeScript
import type { DialogProps, DialogEmits, DialogPosition, DialogSize } from '@pzeta/vue-components'
ConfirmPopup
Popup di conferma posizionato vicino all'elemento trigger. Gestito tramite il composable `useConfirmPopup()`, si ancora dinamicamente all'elemento HTML target e si riposiziona automaticamente rispetto ai bordi del viewport.
Drawer
Pannello laterale overlay con animazione slide configurabile. Supporta apertura da tutti e quattro i lati, tre dimensioni, variante floating e slot header/footer/container.