AppShell

Kerangka dashboard siap pakai: sidebar navy yang bisa diciutkan, header, drawer di HP, dan navigasi bawah. Satu daftar menu dipakai keempatnya.

Pemasangan

Pasang satu AppShell di layout, lalu isi kontennya dengan halaman. Satu daftar sections menghasilkan sidebar desktop, drawer di HP, dan navigasi bawah sekaligus, jadi tidak ada dua daftar menu yang harus dijaga tetap sama.

ts
import type { W4cAppShellNavSection } from '@waste4change/ui/components/AppShell.vue'

const sections: W4cAppShellNavSection[] = [
{
label: 'Operasional',
items: [
{ label: 'Dashboard', to: '/', icon: 'lucide:layout-dashboard', exact: true },
{ label: 'Pickup', to: '/pickup', icon: 'lucide:truck', badge: 4 },
{ label: 'Keluhan', to: '/keluhan', icon: 'lucide:circle-alert', badge: 2, badgeColor: 'danger' },
],
},
]
vue
<template>
<W4cAppShell :sections="sections" app-name="Waste Hub">
<NuxtPage />
</W4cAppShell>
</template>

Dasar

Contoh di bawah adalah shell utuh di dalam kotak setinggi 650px (contained), supaya bisa dicoba tanpa meninggalkan halaman docs. Tombol paling kiri di header menciutkan sidebar: 15rem menjadi rail 4rem berisi tile ikon, dan area konten mengikuti. Tombol itu tinggal di header untuk kedua keadaan — tidak ada kontrol yang mengambang di tepi sidebar.

Kerapatannya disetel untuk dashboard yang menunya banyak: baris 36px, ikon 18px, jarak antarbaris 2px. Delapan sampai empat belas menu masih terlihat tanpa menggulir.

Lewati ke konten
Waste Hub
Waste Hub

Selasa, 23 September 2026

Dashboard

Pickup hari ini

24

8 selesai

Berat terkumpul

1,8 ton

+12% dari kemarin

Perlu tindakan

4

Periksa jadwal

Aktivitas terbaru

Pickup PU-1042 telah selesai
Jadwal baru dari Kantor Bekasi
Laporan Agustus siap diunduh

Bawaannya menu aktif ditentukan dari route Nuxt yang sedang terbuka, jadi tidak ada state yang perlu disiapkan. Route anak ikut menandai menu induknya aktif; pakai exact: true untuk menu beranda supaya tidak selalu tampak aktif. Menu aktif ditandai satu cara saja — latar lebih terang dengan ikon teal — dan membawa aria-current="page" di sidebar, drawer, maupun navigasi bawah.

ts
// Menu aktif mengikuti route: /pickup/PU-0042 tetap menandai Pickup,
// karena route anak ikut dihitung kecuali item memakai exact.
{ label: 'Pickup', to: '/pickup', icon: 'lucide:truck' }

// Tanpa route sama sekali (wizard, tab di satu halaman), kendalikan sendiri:
// <W4cAppShell v-model:active="active" :sections="sections">

Navigasi di HP

Di bawah lebar md sidebar diganti dua hal: navigasi bawah untuk maksimal lima menu tersering, dan drawer yang memuat seluruh menu, dibuka dari tombol yang sama di header. Menu dengan mobile: false karena itu tetap terjangkau dari HP — ia hanya tidak ikut memenuhi bilah bawah.

Drawernya memakai <dialog> bawaan browser seperti Modal: lapisan teratas, perangkap fokus, Escape, dan kunci gulir halaman. Memilih menu langsung menutupnya, dan melebarkan jendela ke ukuran desktop juga menutupnya, supaya fokus tidak terkurung di panel yang sudah digantikan sidebar.

vue
<W4cAppShell
:sections="sections"
:mobile-items="mobileItems"
:mobile-limit="4"
>

Merek dan header sendiri

Slot brand, mobile-brand, header-start, dan header-end mengganti bagian-bagian kerangka tanpa menyentuh tata letaknya. Contoh ini memakai merek sendiri, pemilih lokasi di header, dan daftar menu bawah yang berbeda dari sidebar.

Lewati ke konten
WH Waste Hero
Waste Hero

Belum ada pesanan

Pesanan pengangkutan baru akan muncul di area ini.

API

Props

PropTipeDefaultKeterangan
sectionsW4cAppShellNavSection[][]Kelompok menu. Setiap kelompok punya label opsional dan daftar items; tanpa label, kelompok hanya menjadi pemisah.
mobileItemsW4cAppShellNavItem[]—Daftar khusus navigasi bawah. Tanpa ini, isinya diambil dari sections sesuai mobileLimit.
mobileLimitnumber5Batas menu di navigasi bawah. 0 berarti tanpa batas. Menu yang tidak masuk tetap ada di drawer.
activestring | number—Menu aktif, dikendalikan aplikasi. Tanpa prop ini, menu aktif ditentukan dari route Nuxt yang sedang terbuka.
collapsedboolean—Keadaan sidebar ciut, dikendalikan aplikasi lewat v-model:collapsed. Tanpa prop ini, komponen menyimpannya sendiri.
defaultCollapsedbooleanfalseKeadaan awal sidebar saat tidak dikendalikan aplikasi.
appNamestring'Dashboard'Nama aplikasi di samping logo, di sidebar, drawer, dan header HP. Logonya memakai susunan compact, dan stacked saat sidebar menjadi rail — keduanya membawa wordmark, karena panduan merek tidak mengizinkan mark berdiri sendiri.
contentAs'main' | 'div''main'Elemen area konten. Pakai div hanya kalau shell berada di dalam main milik halaman lain, supaya tidak ada dua landmark main.
contentLabelstring—Nama area konten. Wajib diisi kalau contentAs="div", karena div tanpa nama tidak menjadi landmark.
stickyHeaderbooleantrueHeader menempel di atas saat halaman digulir. Hanya berlaku pada shell setinggi layar.
showMobileNavigationbooleantrueMenampilkan navigasi bawah di layar sempit. Drawer tetap ada, karena itu satu-satunya jalan ke seluruh menu di HP.
containedbooleanfalseMengisi induk yang sudah punya tinggi, bukan layar. Dipakai contoh di halaman ini; aplikasi sungguhan hampir selalu memakai bawaannya.
sidebarLabel / mobileLabelstringNavigasi utama / Navigasi utama selulerNama landmark sidebar (dipakai juga oleh drawer) dan navigasi bawah untuk pembaca layar.
skipLabelstring'Lewati ke konten'Teks tautan lompat ke konten.
collapseLabel / expandLabelstringCiutkan sidebar / Perluas sidebarNama tombol ciut di header, mengikuti keadaannya.
menuLabel / closeMenuLabelstringBuka menu / Tutup menuNama tombol pembuka drawer di header dan penutupnya di dalam drawer.

W4cAppShellNavItem

FieldTipeDefaultKeterangan
labelstring—Teks menu, dipakai juga sebagai nama tautan untuk pembaca layar saat sidebar ciut.
toRouteLocationRaw—Tujuan NuxtLink. Menu tanpa tujuan internal tetap bisa dipakai lewat prop active.
iconstring—Nama ikon Iconify. Wajib: di rail ciut dan di navigasi bawah, ikon inilah penanda menunya.
valuestring | number—Nilai menu untuk active dan update:active. Tanpa ini, nilainya diambil dari path tujuan.
badgestring | number—Angka atau teks pendek: jumlah tugas yang menunggu, misalnya.
badgeColor'primary' | 'danger''primary'danger untuk angka yang berarti masalah, bukan sekadar jumlah. Merah dipakai hanya kalau diminta di sini.
exactbooleanfalseCocok hanya pada route yang sama persis. Tanpa ini, route anak ikut menandai menu induk sebagai aktif.
disabledbooleanfalseMenu dinonaktifkan: dirender sebagai span dengan aria-disabled, tidak bisa diklik, dan tidak mengirim event.
mobilebooleantrueSetel false untuk mengeluarkan menu dari navigasi bawah. Menunya tetap ada di drawer, jadi tidak pernah hilang dari HP.
target'_self' | '_blank'—Untuk tautan ke luar aplikasi; _blank otomatis memakai rel noopener noreferrer.

Slots

SlotTipeDefaultKeterangan
brand{ collapsed }—Mengganti logo dan nama aplikasi di kepala sidebar sekaligus di kepala drawer. collapsed memberi tahu bahwa yang tersedia hanya rail 4rem.
item{ item, active, collapsed, surface }—Mengganti isi satu baris menu. surface bernilai sidebar, drawer, atau bottom, sehingga satu slot bisa melayani ketiganya. Pembungkus tautannya tetap milik komponen.
sidebar-footer{ collapsed }—Area tetap di dasar sidebar dan drawer: profil, bantuan, atau tombol keluar.
mobile-brand——Mengganti nama aplikasi di header layar sempit.
header-start{ collapsed, toggle }—Isi header setelah tombol menu: breadcrumb, pemilih lokasi, atau pencarian.
header-end——Aksi di ujung header: notifikasi, pengalih tema, menu akun.
default——Isi halaman, di dalam area konten yang menggulir sendiri.

Events

EventTipeDefaultKeterangan
update:collapsed(value: boolean) => void—Sidebar diciutkan atau diperluas. Pakai v-model:collapsed untuk menyimpannya.
update:active(value: string | number) => void—Menu dipilih. Pakai v-model:active bila navigasinya tidak mengikuti route.
select(item: W4cAppShellNavItem) => void—Menu dipilih, dengan itemnya utuh. Menu nonaktif tidak mengirim event ini.

Komponen juga mengekspos toggle(), openMenu(), dan closeMenu() lewat template ref, untuk pintasan keyboard milik aplikasi.