Overlays

Dialog

Finestra modale overlay con supporto a posizionamento configurabile, trascinamento, massimizzazione e slot header/footer. `Modal` è un alias deprecato che punta a questo componente.

Import

import { Dialog } from '@pzeta/vue-components'
// oppure con plugin selettivo:
import { OverlaysPlugin } from '@pzeta/vue-components'
app.use(OverlaysPlugin)

Esempio Base

Props

PropTipoDefaultDescrizione
modelValuebooleanfalseVisibilità del dialog (v-model)
visiblebooleanfalseAlias per modelValue (v-model:visible, compatibilità PrimeVue)
headerstring | nullnullTesto del titolo nell'header
iconstring | nullnullClasse CSS dell'icona nell'header (es. pi pi-info)
modalbooleantrueMostra overlay scuro dietro il dialog
positionDialogPosition'center'Posizione del dialog sullo schermo
sizeDialogSize'medium'Larghezza massima del dialog (vedi Larghezza)
closablebooleantrueMostra il pulsante di chiusura
closeIconstring—Classe CSS icona di chiusura (default dal ConfigPlugin)
closeAriaLabelstring—Nome accessibile del pulsante di chiusura. Default "Chiudi" (i18n overlay.closeDialogAriaLabel); utile per un nome esplicito come "Chiudi Nuovo lead"
dismissableMaskbooleanfalseChiude al click sull'overlay. Dalla 1.9.0 il default è false
closeOnEscapebooleantrueChiude alla pressione di Escape
blockScrollbooleantrueBlocca lo scroll del body quando aperto
showHeaderbooleantrueMostra la sezione header
draggablebooleantruePermette di trascinare il dialog dall'header. Dalla 1.9.0 il default è true
maximizablebooleanfalseMostra pulsante per massimizzare a schermo intero
keepInViewportbooleantrueImpedisce al dialog di uscire dai bordi del viewport durante il drag
responsivebooleantrueAbilita comportamento responsive su schermi piccoli
breakpointsRecord<string, string> | nullnullLarghezza 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)
stylestring | Record<string, string | number> | nullnullStile inline per il container del dialog. Una width sostituisce il limite della taglia, fino alla viewport (dalla 1.22.0)
contentStylestring | Record<string, string | number> | nullnullStile inline per l'area contenuto
contentClassstring | nullnullClasse 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

EventoPayloadDescrizione
update:modelValuebooleanAggiornamento visibilità per v-model
update:visiblebooleanAggiornamento visibilità per v-model:visible
show—Dialog reso visibile
hide—Dialog nascosto
closeEventDialog chiuso tramite pulsante o ESC
maximizeEventDialog massimizzato
unmaximizeEventDialog ripristinato dalla modalità massimizzata
maskClickEventClick sull'overlay esterno. Emesso a ogni click sulla mask, anche quando non chiude il dialog

Slot

SlotScopeDescrizione
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.

GestoComportamento
Click sulla maskIl dialog resta aperto e pulsa brevemente; maskClick viene emesso
EscapeChiude (disattivabile con :close-on-escape="false"). Con la tendina di un campo aperta chiude prima solo la tendina (vedi sotto)
Pulsante XChiude (rimuovibile con :closable="false")
Trascinamento dall'headerSposta 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" e aria-modal="true"
  • aria-labelledby collegato automaticamente all'ID del titolo nell'header
  • aria-describedby collegato 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 usare closeAriaLabel
  • 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'