Modal

Modal adalah elemen popover. Tombol mana pun yang popovertarget-nya cocok dengan id modalnya akan membukanya — tanpa skrip di mana pun, termasuk latar, penutupan ringan, Escape, dan penanganan fokus, yang semuanya dimiliki peramban.

Itulah sebabnya id bersifat wajib dan komponennya melempar galat tanpa itu: id-nya adalah keseluruhan sambungannya.

Setiap modal di halaman ini benar-benar terbuka — cobalah.

fragment(
  button({ popovertarget: 'demo-basic' }, 'Buka modal'),
  modal({ id: 'demo-basic', title: 'Bangun ulang situsnya?' },
    'Ini menjalankan sitelo build dan menerbitkan ulang dist/.',
  ),
)

Dengan kaki

Tombol tutup adalah tombol mana pun yang menunjuk id yang sama dengan popovertargetaction="hide".

fragment(
  button({ color: 'danger', popovertarget: 'demo-confirm' }, 'Hapus halaman…'),
  modal({
    id: 'demo-confirm',
    title: 'Hapus halaman ini?',
    footer: stack({ direction: 'row', gap: 'sm' },
      button({
        variant: 'ghost',
        color: 'neutral',
        popovertarget: 'demo-confirm',
        popovertargetaction: 'hide',
      }, 'Batal'),
      button({ color: 'danger' }, 'Hapus'),
    ),
  }, 'Ini tidak bisa dibatalkan. HTML yang dihasilkan akan dihapus pada build berikutnya.'),
)

Ukuran

fragment(
  stack({ direction: 'row', gap: 'sm', wrap: true },
    button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-sm' }, 'Kecil'),
    button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-md' }, 'Sedang'),
    button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-lg' }, 'Besar'),
  ),
  modal({ id: 'demo-sm', size: 'sm', title: 'Kecil' }, 'size: sm — sekitar 24rem.'),
  modal({ id: 'demo-md', title: 'Sedang' }, 'Bawaannya — sekitar 32rem.'),
  modal({ id: 'demo-lg', size: 'lg', title: 'Besar' }, 'size: lg — sekitar 48rem.'),
)

Formulir di dalam modal

fragment(
  button({ variant: 'soft', popovertarget: 'demo-form' }, 'Halaman baru…'),
  modal({
    id: 'demo-form',
    title: 'Halaman baru',
    footer: stack({ direction: 'row', gap: 'sm' },
      button({ variant: 'ghost', color: 'neutral', popovertarget: 'demo-form', popovertargetaction: 'hide' }, 'Batal'),
      button({ type: 'submit' }, 'Buat'),
    ),
  },
    stack({ gap: 'md' },
      textField({ label: 'Judul', name: 'modal-title', placeholder: 'Tentang' }),
      selectField({ label: 'Ekstensi', name: 'modal-ext', options: ['.ht.js', '.ht.ts', '.ht.jsx'] }),
    ),
  ),
)

Tanpa tombol tutup

closable: false membuang × di sudutnya. Escape dan klik di luar tetap menutupnya — popover tidak bisa dibuat benar-benar menghalangi, dan itu biasanya justru perilaku yang tepat.

fragment(
  button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-bare' }, 'Tanpa tombol tutup'),
  modal({ id: 'demo-bare', title: 'Tekan Escape', closable: false },
    'Atau klik di mana saja di luar dialog ini.',
  ),
)

Konten panjang

Badannya bergulir; kepala dan kakinya tetap di tempat.

fragment(
  button({ variant: 'outline', color: 'neutral', popovertarget: 'demo-long' }, 'Modal panjang'),
  modal({
    id: 'demo-long',
    title: 'Catatan rilis',
    footer: button({ popovertarget: 'demo-long', popovertargetaction: 'hide' }, 'Tutup'),
  },
    stack({ gap: 'md' },
      ...Array.from({ length: 12 }, (unused, index) =>
        text({ variant: 'small', tone: 'muted' }, 'Perubahan ' + (index + 1) + ' — ada yang diperbaiki.'),
      ),
    ),
  ),
)

Penggulungan latar

Halaman di belakang modal yang terbuka tidak tergulir. Itulah satu-satunya hal yang diserahkan API popover kepada Anda, dan di sini ia dikerjakan dengan CSS — tanpa skrip, dan tanpa apa pun yang perlu diinisialisasi. Berikan lockScroll: false agar latarnya tergulir seperti biasa.

Dukungan peramban

API popover tersedia di setiap peramban masa kini. Pada peramban yang terlalu tua untuk mengenalnya, modalnya dirender sebaris di dalam halaman alih-alih di atasnya — terlihat dan bisa dipakai, hanya saja tidak menumpang di atas. Tidak ada yang hilang.

Props

PropTipeBawaanDeskripsi
idstring—Wajib. Apa yang ditunjuk popovertarget sebuah pemicu.
titleChild—Judul, sekaligus nama dialognya yang dapat diakses.
size'sm' | 'md' | 'lg''md'Lebar maksimum.
footerChild—Baris bawah, pada pita berwarnanya sendiri.
closablebooleantrueMenampilkan × di kepalanya.
closeLabelstring'Close'Nama yang dapat diakses untuk tombol itu.
lockScrollbooleantrueMenghentikan penggulungan halaman di belakangnya selagi ia terbuka.

closeButton({ target }) merender × itu sendiri, untuk kepala yang Anda bangun sendiri.