Menü
Panel menüsü tek bir JSON dosyasında durur; dikey menü, yatay menü, mobil çekmece ve ⌘K araması aynı dosyadan üretilir.
En kısa örnek
{
"menu": [
{ "section": "Genel" },
{ "title": "Pano", "icon": "home", "route": "dashboard" },
{ "title": "Müşteriler", "icon": "users", "route": "customers.index", "active": "customers.*" }
]
}
Temada bu dosya resources/menu/main.json yolundadır ve düzen onu her sayfada okur. Dosyaya eklediğiniz her sayfa menüde ve aramada görünür. Bağlantı adresi rota adından üretilir; seçili öğe de rota adına bakılarak bulunur.
JSON menü (acun:menu ve Acun\Ui\Menu\Menu) ücretsiz acunsoft/acun-ui-free paketindedir.
Menüye sayfa eklemek
-
Rotaları adıyla tanımlayın, ör.
projects.index(liste) veprojects.show(ayrıntı). -
main.jsondosyasındakimenudizisine bir satır ekleyin:{ "title": "Projeler", "icon": "folder", "route": "projects.index", "active": "projects.*" }
Sayfa menüde ve ⌘K aramasında görünür. "active": "projects.*" sayesinde proje ayrıntı sayfasında da "Projeler" seçili kalır.
Alanlar
| Alan | Ne yapar | Örnek |
|---|---|---|
section |
Bölüm başlığı; yalnızca en üst seviyede. | "section": "Kayıtlar" |
title |
Öğenin yazısı; çeviri anahtarıdır. | "title": "Müşteriler" |
icon |
acun:icon adı (Heroicons); bilinmeyen ad simge göstermez. |
"icon": "users" |
route |
Laravel rota adı; tanımlı değilse bağlantı # olur. |
"route": "customers.index" |
params |
Rota parametreleri. | "params": { "user": 1 } |
url |
Rota yerine adres; yalnızca güvenli adresler kabul edilir. | "url": "/reports/2025" |
active |
Öğenin seçili göründüğü sayfalar; * joker. |
"active": "customers.*" |
badge |
Sağdaki sayı ya da kısa etiket. | "badge": "Yeni" |
target |
_blank bağlantıyı yeni sekmede açar. |
"target": "_blank" |
children |
Alt öğeler; en fazla üç seviye. | "children": [ … ] |
Ayrıntılar:
- url: Yalnızca
http,https,mailto,telve uygulama içi adresler kabul edilir. Diğerleri#olur. - active: Rota adı ya da
/yoldeseni yazın; birden fazlası için dizi verin. Verilmezse öğe yalnızca kendi sayfasında seçili olur: aynı rota adı ya da aynı adres.activeverilmemiş dış adresler hiçbir sayfada seçili görünmez. - badge: Alt menülü öğede
true, alt öğe sayısını gösterir. - target: Herhangi bir değer verildiğinde bağlantı tam sayfa yüklemesiyle açılır.
"_self"aynı sekmede açar. - children: Üçüncü seviyedeki
childrenyok sayılır. Alt öğesi olan öğe bağlantı değil, açılıp kapanan bir gruptur. Alt öğelerinden biri seçiliyse grup da seçili ve açık görünür.
Altında öğe kalmayan bölüm başlığı ve alt öğesi kalmayan grup gösterilmez. Tanımdaki bilinmeyen alanlar yok sayılır.
Örnekler
{ "title": "Profilim", "icon": "user", "route": "users.show", "params": { "user": 1 } },
{ "title": "Raporlar", "icon": "chart-bar", "route": "reports.index", "active": ["reports.*", "/exports/*"] },
{ "title": "Talepler", "icon": "inbox", "route": "tickets.index", "badge": "Yeni" },
{ "title": "Dokümantasyon", "icon": "book-open", "url": "https://ornek.com/docs", "target": "_blank" },
{ "title": "Arşiv", "icon": "archive-box", "badge": true, "children": [
{ "title": "Satışlar", "route": "archive.sales" },
{ "title": "Yıllar", "children": [
{ "title": "2025", "url": "/archive/2025" }
] }
] }
Sırasıyla: parametreli rota; rota deseni ve yol deseni birlikte; sabit rozet; yeni sekmede açılan dış bağlantı; üç seviyeli iç içe menü. Sonuncuda "badge": true alt öğe sayısını (2) gösterir.
Menüyü çevirmek
Başlıklar ve bölüm adları çeviri anahtarıdır. "title": "Siparişler" için lang/en.json dosyasına "Siparişler": "Orders" yazmanız yeterli. Çevirisi olmayan başlık olduğu gibi görünür. Ayrıntılar Dil ve çeviri sayfasında.
Menünün düzende çizilmesi
Temada menü dosyası düzen dosyasının başında okunur, layouts/admin/sections/ altındaki bölümlerde çizilir:
{{-- layouts/admin/vertical.blade.php --}}
@php($menu = \Acun\Ui\Menu\Menu::fromFile('main'))
{{-- layouts/admin/sections/sidebar.blade.php --}}
<acun:slot:nav><acun:menu :menu="$menu" /></acun:slot:nav>
{{-- layouts/admin/sections/navbar.blade.php --}}
<acun:slot:search><acun:command-palette :items="$menu->searchItems()" :placeholder="__('Sayfa ara…')" /></acun:slot:search>
Yatay düzende aynı dosya üst çubukta horizontal ile, mobil çekmecede dikey hâliyle çizilir:
<acun:slot:nav><acun:menu :menu="$menu" horizontal /></acun:slot:nav>
<acun:slot:mobileNav><acun:menu :menu="$menu" /></acun:slot:mobileNav>
Servis sağlayıcı ya da View::share gerekmez; dosya yalnızca menüyü çizen düzende okunur. acun:menu şunlardan birini alır: dosya adı (menu="main"), dosya yolu, dizi ya da Menu nesnesi. Menüyü arama için de kullanacaksanız bir kez okuyup nesneyi iki yere verin.
Yatay menüde bölüm başlıkları yazılmaz. Bunun yerine sections prop'u her bölümü tek bir açılır listede toplayabilir: true her zaman toplar, false hiç toplamaz. Varsayılan auto, en üst seviyede altıdan fazla öğe varsa toplar.
Birden fazla menü
Her menü ayrı bir dosyadır; adını fromFile ile verirsiniz. Örneğin yönetim ve müşteri panelleri farklı menüler kullanabilir:
resources/menu/
├── main.json yönetim paneli
├── customer.json müşteri paneli
└── horizontal.json yatay düzen için ayrı menü (isteğe bağlı)
@php($menu = \Acun\Ui\Menu\Menu::fromFile('customer'))
Yatay menü dikeyden farklı olacaksa horizontal.json oluşturup yatay düzende Menu::fromFile('horizontal') kullanın. Gerekmiyorsa iki düzen aynı dosyayı paylaşır; menüyü tek yerde güncellersiniz.
Menüyü veritabanından üretmek
Menü tanımı bir dizi de olabilir. Örneğin her müşteri hesabına özel menüyü veritabanında tutabilirsiniz:
$menu = \Acun\Ui\Menu\Menu::resolve(['menu' => $hesap->menu_items]);
Veri kullanıcıdan gelse bile javascript: gibi tehlikeli adresler çizilmez; url ve href değerleri güvenli şemalarla sınırlıdır. Bilinmeyen bir simge adı da sayfayı bozmaz, yalnızca simge gösterilmez.
Yetki
Menü yetki denetimi yapmaz: tanımdaki her öğe çizilir. Role ya da izne göre farklı menü göstermek için tanımı PHP'de kendiniz süzün:
$items = json_decode(file_get_contents(resource_path('menu/main.json')), true)['menu'];
$items = array_values(array_filter($items, fn ($item) => ($item['title'] ?? '') !== 'Kullanıcılar' || auth()->user()->isAdmin()));
$menu = \Acun\Ui\Menu\Menu::resolve($items);
Elle yazılan menüde öğeyi Blade'in @can yönergesiyle sarın. Menüden gizlemek sayfayı korumaz; rotaları ayrıca kendi ara katmanınız ya da politikalarınızla koruyun.
Hatalar
- Menu file not found:
fromFile('main')dosyayıresources/menu/main.jsonyolunda arar. Adı ve konumu kontrol edin ya da tam dosya yolu verin. - JSON hatası: Dosya geçerli JSON değilse okuma bir hatayla durur, ör. son öğeden sonra fazladan virgül. Hata mesajı sorunun türünü söyler.
- Bağlantı
#oluyor:routealanındaki rota adı tanımlı değil ya daurlgüvenli olmayan bir şema kullanıyor. - Öğe seçili görünmüyor:
activealanına alt sayfaların rota desenini ekleyin, ör.customers.*.
Komut paleti
acun:command-palette üst çubuktaki arama kutusudur. ⌘K ya da Ctrl+K ile açılır, klavyeyle gezilir. Büyük/küçük harfi sayfanın diline göre eşler; Türkçe sayfada "İSTANBUL" ile "istanbul" eşleşir.
| Prop | Ne yapar |
|---|---|
items |
Liste; her öğe label, href, group ve hint alır. |
placeholder |
Kutudaki yazı; varsayılanı sayfanın dilinde "Ara…". |
empty |
Sonuç yokken görünen yazı; varsayılanı "Sonuç bulunamadı". |
shortcut |
false ⌘K kısayolunu kapatır. |
Menu::searchItems() listeyi menüden üretir. Menüde olmayan sayfaları eklemek için diziyi genişletin:
<acun:command-palette :items="[...$menu->searchItems(), ['label' => __('Yardım'), 'href' => route('help'), 'group' => __('Diğer')]]" />
Palet, pencereye command-palette-open olayı gönderildiğinde de açılır. Güvenli olmayan href değerleri # olur.
Elle yazılan menü
JSON yerine menü bileşenlerini doğrudan da kullanabilirsiniz: acun:nav.heading, acun:nav.item, acun:nav.group, acun:nav.sub-item, acun:nav.sub-group, acun:nav.coming-soon. Yatay menü için acun:nav.horizontal-item ve acun:nav.horizontal-group vardır. Örnekler bileşen sayfalarında.
İleri düzey: öğeleri dönüştürmek
Menu nesnesini doğrudan oluşturursanız iki isteğe bağlı fonksiyon verebilirsiniz. transform her öğeye, bölüm başlıkları ve alt öğeler dahil, çizilmeden önce uygulanır. isActive bir öğenin seçili olup olmadığına kendiniz karar vermek içindir; null dönerse varsayılan kural kullanılır.
use Acun\Ui\Menu\Menu;
$items = json_decode(file_get_contents(resource_path('menu/main.json')), true)['menu'];
$menu = new Menu(
$items,
transform: fn (array $item) => ($item['route'] ?? null) === 'tickets.index' ? [...$item, 'badge' => $acikTalepSayisi] : $item,
isActive: fn (array $item) => null,
);
Başlıklar zaten kendiliğinden çevrilir; transform içinde __() çağırmanız gerekmez.
Daha fazlası
- İlk sayfanız: sayfa, rota ve menü kaydı birlikte.
- Düzenler: menünün yerleştiği slot'lar.
- Dil ve çeviri: menü başlıklarını çevirmek.