Lompat ke konten
dokumentasi.id
Commit 6ea47ab
v2
Star3,1 rb

6

Data Navigasi Sidebar dan Menu Horizontal

Sidebar dan menu horizontal Mazer tidak ditulis di dalam template. Keduanya adalah dua berkas data JSON yang diimpor vite.config.js (vite.config.js:7-8), lalu disuntikkan sebagai variabel sidebarItems dan horizontalMenuItems ke setiap halaman oleh getVariables (vite.config.js:28-40). Template hanya membaca variabel itu: src/layouts/master.html meng-include src/layouts/sidebar.html (src/layouts/master.html:19-21), sedangkan halaman berlayout horizontal meng-extends src/layouts/horizontal.html (src/layout-horizontal.html:1-8).

Penentu status aktif datang dari halaman, bukan dari data. Setiap halaman menetapkan filename sendiri di awal berkasnya (src/component-alert.html:1-4). Kedua layout membandingkan nilai itu dengan url dan key milik setiap simpul menu.

Simpul menu dan relasinya

Bentuk simpulnya seragam di dua berkas: name wajib; url, key, icon, dan submenu opsional. Karena submenu berisi array simpul sejenis, kedalaman struktur tidak dibatasi skema — lihat Multi-level Menu di sidebar (src/sidebar-items.json:340-371) dan Extra Components yang bersarang di berkas horizontal (src/horizontal-menu-items.json:72-76). Template tetap hanya merender paling dalam tiga tingkat (src/layouts/sidebar.html:55-62, src/layouts/horizontal.html:77-87).

erDiagram
    SIDEBAR_FILE ||--|{ SIDEBAR_ITEM : "sidebarItems"
    HORIZONTAL_FILE ||--|{ HORIZONTAL_ITEM : "horizontalMenuItems"
    SIDEBAR_ITEM ||--o{ SIDEBAR_ITEM : "submenu rekursif"
    HORIZONTAL_ITEM ||--o{ HORIZONTAL_ITEM : "submenu rekursif"
    SIDEBAR_FILE {
        string path "src/sidebar-items.json"
    }
    HORIZONTAL_FILE {
        string path "src/horizontal-menu-items.json"
    }
    SIDEBAR_ITEM {
        string name
        string url "opsional, fallback tanda pagar"
        string key "prefix untuk startsWith"
        string icon "nama Bootstrap Icons"
        bool isTitle
        array submenu
    }
    HORIZONTAL_ITEM {
        string name
        string url "opsional, fallback tanda pagar"
        string key "prefix untuk startsWith"
        string icon "nama Bootstrap Icons"
        array submenu
    }
%% fig: Bentuk simpul menu pada dua berkas data
%% desc: Kedua berkas berisi daftar simpul dengan submenu rekursif; field isTitle hanya ada di berkas sidebar.
Fig. 1: Bentuk simpul menu pada dua berkas dataKedua berkas berisi daftar simpul dengan submenu rekursif; field isTitle hanya ada di berkas sidebar.

Tabel berikut memetakan setiap field ke pembacaannya di template.

FieldTipePerilaku di template
namestringLabel judul seksi maupun tautan menu (src/layouts/sidebar.html:41-45)
urlstring, opsionalDibandingkan dengan filename; bila undefined, tautan memakai '#' (src/layouts/sidebar.html:46-49)
keystring, opsionalPrefix yang diumpankan ke filter startsWith untuk kelas active (src/layouts/sidebar.html:41-45)
iconstringNama Bootstrap Icons, dirender sebagai bi-{{ icon }} (src/layouts/horizontal.html:56-63)
isTitlebooleanHanya di berkas sidebar; simpul ini menjadi <li class="sidebar-title"> (src/layouts/sidebar.html:41-45)
submenuarrayRekursif; submenu.length > 0 menambah kelas has-sub dan memicu render <ul class="submenu"> (src/layouts/sidebar.html:50-54)

Perbedaan kedua berkas

sidebar-items.json menyisipkan judul seksi yang tidak bisa diklik. isTitle muncul lima kali: Menu (src/sidebar-items.json:1-10), Forms & Tables (src/sidebar-items.json:160-163), Extra UI (src/sidebar-items.json:254-257), Pages (src/sidebar-items.json:372-375), dan Raise Support (src/sidebar-items.json:453-456). Berkas horizontal tidak memuat field ini, dan layout-nya memang menjaganya dengan {% if not menuItem.isTitle %} (src/layouts/horizontal.html:58).

Penempatan grup juga berbeda: Extra Components berdiri sebagai simpul top-level di sidebar (src/sidebar-items.json:90-94), tetapi bersarang di dalam Components di berkas horizontal (src/horizontal-menu-items.json:72-76). Submenu Components versi horizontal berisi 16 entri (src/horizontal-menu-items.json:12-106).

Aspeksrc/sidebar-items.jsonsrc/horizontal-menu-items.json
Indentasi2 spasi (src/sidebar-items.json:1-10)tab (src/horizontal-menu-items.json:1-11)
Judul seksi isTitle5 entritidak ada
Extra Componentssimpul top-level (src/sidebar-items.json:90-94)anak dari Components (src/horizontal-menu-items.json:72-76)
Tabelsatu tautan Table tanpa submenu (src/sidebar-items.json:234-238)grup Table dengan tiga anak (src/horizontal-menu-items.json:213-233)
Kelas tingkat 2submenusubmenu dibungkus submenu-group
Kelas tingkat 3submenu-level-2subsubmenu

Layout horizontal memecah submenu menjadi kolom: maxSubmenuItemY bernilai 8, satu <ul class="submenu-group"> dibuka saat loop.index % maxSubmenuItemY == 1 dan ditutup saat == 0 atau loop.last (src/layouts/horizontal.html:56-71). Dengan 16 entri, submenu Components terbagi menjadi dua kolom berisi 8 item. Sidebar tidak mengelompokkan apa pun; ia merender satu <ul class="submenu"> per simpul induk (src/layouts/sidebar.html:50-54).

Status aktif dari filename

Kelas active dihasilkan satu ekspresi: 'active' if (url == filename or filename|startsWith(key)) (src/layouts/sidebar.html:41-45, src/layouts/horizontal.html:56-63). Filter startsWith didefinisikan bersama containString di vite.config.js; keduanya mengembalikan false bila argumen pertama kosong, lalu startsWith meneruskan ke String.prototype.startsWith (vite.config.js:130-139).

graph LR
    F["filename halaman"] --> S["filter startsWith"]
    K["item.key"] --> S
    U["item.url"] --> E{"sama dengan filename"}
    S --> A["kelas active"]
    E --> A
    T["filter containString"] -.-> X["tanpa pemanggil"]
    style F fill:#336791,color:#fff
    style K fill:#f59e0b,color:#000
    style U fill:#f59e0b,color:#000
    style S fill:#8b5cf6,color:#fff
    style E fill:#8b5cf6,color:#fff
    style A fill:#2d7d46,color:#fff
    style T fill:#dc2626,color:#fff
    style X fill:#dc2626,color:#fff
%% fig: Dua jalur penentuan kelas active
%% desc: Template membandingkan filename dengan url, atau mengumpankannya ke filter startsWith bersama item.key; containString terdaftar tetapi tidak dipanggil.
Fig. 2: Dua jalur penentuan kelas activeTemplate membandingkan filename dengan url, atau mengumpankannya ke filter startsWith bersama item.key; containString terdaftar tetapi tidak dipanggil.

key milik induk dipakai dua kali di sidebar: pada <li class="sidebar-item"> dan pada seluruh <ul class="submenu"> (src/layouts/sidebar.html:41-45, src/layouts/sidebar.html:50-54). Simpul anak memakai key miliknya sendiri, dan simpul paling dalam hanya dibandingkan lewat url (src/layouts/sidebar.html:50-54, src/layouts/sidebar.html:55-62). Di layout horizontal, submenu-item hanya memakai kesamaan url (src/layouts/horizontal.html:72-76), sedangkan subsubmenu-item tidak diberi kelas active sama sekali (src/layouts/horizontal.html:77-87).

filename halamanEntri yang diujiHasil
component-modal.htmlComponents dengan key component (src/sidebar-items.json:12-15)aktif lewat filter startsWith
ui-map-leaflet.htmlMaps dengan key ui-map (src/sidebar-items.json:317-321)aktif lewat filter startsWith
index.htmlDashboard tanpa key (src/sidebar-items.json:6-10)hanya cocok lewat kesamaan url
error-404.htmlDocumentation dengan key error (src/sidebar-items.json:457-474)ikut aktif karena prefix error

Jebakan yang perlu dijaga

⚠️ Tiga entri sidebar memakai key yang sama, yaitu error, untuk Documentation, Contribute, dan Donate (src/sidebar-items.json:457-474). Berkas horizontal mengulang pola itu pada Support beserta ketiga anaknya (src/horizontal-menu-items.json:385-408). Akibatnya error-403.html, error-404.html, dan error-500.html ikut menandai entri dukungan tersebut sebagai active bersama menu Errors.

⚠️ src/layouts/horizontal.html menguji sub.url di luar loop {% for sub %} dan memakai sidebarItem.key, variabel yang tidak ada di layout horizontal. Ekspresi itu tidak pernah menghasilkan active, sehingga <div class="submenu"> tidak pernah mendapat kelas tersebut (src/layouts/horizontal.html:63-71).

⚠️ Daftar menu terduplikasi. Menambah satu halaman berarti menyunting src/sidebar-items.json dan src/horizontal-menu-items.json, lalu memastikan filename halaman itu cocok dengan url yang baru (src/component-alert.html:1-4).

⚠️ containString terdaftar sebagai filter (vite.config.js:130-139) tetapi tidak dipanggil template mana pun; satu-satunya filter yang dipakai untuk status aktif adalah startsWith.

Berkas horizontal juga memuat beberapa grup tanpa key, misalnya Table (src/horizontal-menu-items.json:213-233). Untuk entri semacam itu cabang startsWith tidak pernah bernilai benar, sehingga status aktif sepenuhnya bergantung pada kesamaan url.