Modale

Un modale è un elemento popover. Qualunque pulsante il cui popovertarget combacia con l’id del modale lo apre — nessuno script da nessuna parte, sfondo, chiusura leggera, Escape e gestione del focus compresi, di cui è il browser a farsi carico.

È per questo che id è obbligatorio e che il componente solleva un errore senza — l’id è tutto il collegamento.

Modale di base

Ogni modale di questa pagina si apre davvero — provalo.

fragment(
  button({ popovertarget: 'demo-basic' }, 'Apri il modale'),
  modal({ id: 'demo-basic', title: 'Ricostruire il sito?' },
    'Esegue sitelo build e ripubblica dist/.',
  ),
)

Con un piè di pagina

Un pulsante di chiusura è un qualunque pulsante che punta allo stesso id con popovertargetaction="hide".

fragment(
  button({ color: 'danger', popovertarget: 'demo-confirm' }, 'Elimina la pagina…'),
  modal({
    id: 'demo-confirm',
    title: 'Eliminare questa pagina?',
    footer: stack({ direction: 'row', gap: 'sm' },
      button({
        variant: 'ghost',
        color: 'neutral',
        popovertarget: 'demo-confirm',
        popovertargetaction: 'hide',
      }, 'Annulla'),
      button({ color: 'danger' }, 'Elimina'),
    ),
  }, 'Non si può annullare. L’HTML generato viene rimosso alla prossima build.'),
)

Dimensioni

fragment(
  stack({ direction: 'row', gap: 'sm', wrap: true },
    button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-sm' }, 'Piccolo'),
    button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-md' }, 'Medio'),
    button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-lg' }, 'Grande'),
  ),
  modal({ id: 'demo-sm', size: 'sm', title: 'Piccolo' }, 'size: sm — circa 24rem.'),
  modal({ id: 'demo-md', title: 'Medio' }, 'Il predefinito — circa 32rem.'),
  modal({ id: 'demo-lg', size: 'lg', title: 'Grande' }, 'size: lg — circa 48rem.'),
)

Form dentro un modale

fragment(
  button({ variant: 'soft', popovertarget: 'demo-form' }, 'Nuova pagina…'),
  modal({
    id: 'demo-form',
    title: 'Nuova pagina',
    footer: stack({ direction: 'row', gap: 'sm' },
      button({ variant: 'ghost', color: 'neutral', popovertarget: 'demo-form', popovertargetaction: 'hide' }, 'Annulla'),
      button({ type: 'submit' }, 'Crea'),
    ),
  },
    stack({ gap: 'md' },
      textField({ label: 'Titolo', name: 'modal-title', placeholder: 'Informazioni' }),
      selectField({ label: 'Estensione', name: 'modal-ext', options: ['.ht.js', '.ht.ts', '.ht.jsx'] }),
    ),
  ),
)

Senza pulsante di chiusura

closable: false toglie la × nell’angolo. Escape e il clic fuori lo chiudono comunque — un popover non si può rendere davvero bloccante, e di solito è il comportamento giusto.

fragment(
  button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-bare' }, 'Nessun pulsante di chiusura'),
  modal({ id: 'demo-bare', title: 'Premi Escape', closable: false },
    'Oppure clicca in un punto qualsiasi fuori da questa finestra.',
  ),
)

Contenuto lungo

Il corpo scorre; intestazione e piè di pagina restano fermi.

fragment(
  button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-long' }, 'Modale lungo'),
  modal({
    id: 'demo-long',
    title: 'Note di rilascio',
    footer: button({ popovertarget: 'demo-long', popovertargetaction: 'hide' }, 'Chiudi'),
  },
    stack({ gap: 'md' },
      ...Array.from({ length: 12 }, (unused, index) =>
        text({ variant: 'small', tone: 'muted' }, 'Modifica ' + (index + 1) + ' — qualcosa è stato corretto.'),
      ),
    ),
  ),
)

Scorrimento dello sfondo

La pagina dietro un modale aperto non scorre. È l’unica cosa che l’API popover lascia a te, e qui è fatta in CSS — nessuno script, e niente da inizializzare. Passa lockScroll: false per lasciare che lo sfondo scorra come al solito.

Supporto dei browser

L’API popover è disponibile in tutti i browser attuali. In uno troppo vecchio per conoscerla, il modale viene renderizzato in linea nella pagina invece che sopra di essa — visibile e usabile, solo non sovrapposto. Non sparisce nulla.

Props

PropTipoPredefinitoDescrizione
idstring—Obbligatorio. Ciò a cui punta il popovertarget di un innesco.
titleChild—Intestazione, e nome accessibile della finestra di dialogo.
size'sm' | 'md' | 'lg''md'Larghezza massima.
footerChild—Riga in fondo, su una fascia tinta a sé.
closablebooleantrueMostra la × nell’intestazione.
closeLabelstring'Close'Nome accessibile di quel pulsante.
lockScrollbooleantrueImpedisce alla pagina dietro di scorrere mentre è aperto.

closeButton({ target }) renderizza quella × da sola, per un’intestazione che costruisci tu.