Menu

Menu adalah sebuah <details> dengan panel bergaya. Itu pilihan yang disengaja ketimbang API popover: popover tinggal di lapisan teratas dan tidak bisa diposisikan terhadap pemicunya tanpa penempatan jangkar, yang belum tersedia di mana-mana. Sebuah <details> memposisikan dirinya dengan benar hari ini dan tidak butuh apa pun dimuat.

Pemicunya adalah <summary> itu, ditata seperti tombol — jadi Anda memberikan labelnya dan props tombolnya ke menu() alih-alih memberikan button() yang sudah dirender. Sebuah summary sudah interaktif, dan tombol di dalamnya menyarangkan dua kendali padahal hanya ada satu tindakan: markup tidak sah, dan dua perhentian tab untuk satu hal.

Penutupan lewat klik di luar dan Escape datang dari penangan ontoggle yang mengimpornya saat sebuah menu pertama kali dibuka — dan hanya saat itu. Jika modul itu tidak pernah datang, menunya tetap membuka dan menutup lewat summary-nya sendiri.

menu({ trigger: 'Tindakan' },
  menuItem({ href: '#edit' }, 'Sunting'),
  menuItem({ href: '#duplicate' }, 'Gandakan'),
  menuSeparator(),
  menuItem({ href: '#delete' }, 'Hapus'),
)

Perataan

Menu membuka dari tepi awal pemicunya. align: 'end' membaliknya, dan itulah yang dibutuhkan menu di dekat tepi kanan sebuah bilah.

Rata awal
Rata akhir
stack({ direction: 'row', gap: 'xl', justify: 'space-between', style: 'width: 100%' },
  menu({ trigger: 'Rata awal', variant: 'soft' },
    menuItem({ href: '#a' }, 'Pertama'),
    menuItem({ href: '#b' }, 'Kedua'),
  ),
  menu({ trigger: 'Rata akhir', variant: 'soft', align: 'end' },
    menuItem({ href: '#c' }, 'Pertama'),
    menuItem({ href: '#d' }, 'Kedua'),
  ),
)

Pemicu ikon

Ikon tanpa teks trigger butuh sebuah label — ia menjadi nama yang dapat diakses, yang tidak bisa diberikan ikonnya.

stack({ direction: 'row', gap: 'md' },
  menu({
    align: 'end',
    label: 'Tindakan lainnya',
    variant: 'ghost',
    icon: icon('more-horizontal'),
  },
    menuItem({ href: '#rename' }, 'Ganti nama'),
    menuItem({ href: '#move' }, 'Pindahkan'),
    menuSeparator(),
    menuItem({ href: '#archive' }, 'Arsipkan'),
  ),
)

Item dengan ikon

menu({ trigger: 'Berkas' },
  menuItem({
    href: '#new',
    icon: icon('plus'),
  }, 'Halaman baru'),
  menuItem({
    href: '#open',
    icon: icon('folder'),
  }, 'Buka…'),
  menuSeparator(),
  menuItem({
    href: '#build',
    icon: icon('zap'),
  }, 'Bangun situs'),
)

Tombol alih-alih tautan

Item tanpa href merender sebuah <button> — untuk tindakan yang terjadi di halaman itu, bukan navigasi.

Ekspor
menu({ trigger: 'Ekspor', variant: 'soft', color: 'primary' },
  menuItem({ onclick: "import('/su/toast.js').then(m=>m.toast('Diekspor sebagai JSON.',{color:'success'}))" }, 'Sebagai JSON'),
  menuItem({ onclick: "import('/su/toast.js').then(m=>m.toast('Diekspor sebagai CSV.',{color:'success'}))" }, 'Sebagai CSV'),
)

Di dalam bilah aplikasi

appBar({ brand: 'sitelo' },
  appBarSpacer(),
  appBarActions(
    themeToggle(),
    menu({
      align: 'end',
      label: 'Lainnya',
      variant: 'ghost',
      icon: icon('more-horizontal'),
    },
      menuItem({ href: '/docs' }, 'Dokumentasi'),
      menuItem({ href: '/examples' }, 'Contoh'),
      menuSeparator(),
      menuItem({ href: 'https://github.com/paul-browne/sitelo' }, 'GitHub'),
    ),
  ),
)

Aksesibilitas

Panelnya adalah role="menu" yang itemnya ber-role="menuitem", dan summary-nya membawa aria-haspopup. Sebuah <details> bukan widget menu bawaan, jadi ini pendekatan yang masuk akal, bukan yang sempurna — untuk daftar tautan biasa, sebuah nav di dalam details-nya sama sahnya dan mengklaim lebih sedikit.

Props

menu() — props pemicunya adalah props tombol:

PropTipeBawaanDeskripsi
triggerChild—Label yang terlihat. Berikan teks, bukan button() yang sudah dirender.
iconChild—Markup sebelum labelnya, atau sendirian untuk pemicu yang hanya berikon.
labelstring—Nama yang dapat diakses. Wajib ketika ada ikon dan tidak ada teks pemicu.
variant'solid' | 'soft' | 'outline' | 'ghost' | 'link''outline'Penataan pemicunya.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''neutral'Dari palet mana pemicunya menimba.
size'sm' | 'md' | 'lg''md'Ukuran pemicunya.
align'start' | 'end''start'Dengan tepi pemicu yang mana panelnya disejajarkan.
triggerClassstring—Kelas tambahan untuk pemicunya, bukan details pembungkusnya.

menuItem():

PropTipeBawaanDeskripsi
hrefstring—Merender jangkar; tanpa itu, sebuah tombol.
iconChild—Markup sebelum labelnya.
asstring'button'Elemen yang dirender ketika tidak ada href.

menuSeparator() tidak menerima props — ia adalah garis rambut di antara kelompok item.