TR
Başlangıç

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.

  1. Rotaları adıyla tanımlayın, ör. projects.index (liste) ve projects.show (ayrıntı).

  2. main.json dosyasındaki menu dizisine 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, tel ve uygulama içi adresler kabul edilir. Diğerleri # olur.
  • active: Rota adı ya da /yol deseni 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. active verilmemiş 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 children yok 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.

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.

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ü 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.json yolunda 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: route alanındaki rota adı tanımlı değil ya da url güvenli olmayan bir şema kullanıyor.
  • Öğe seçili görünmüyor: active alanı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ı

Acun UIİnsanlar için tasarlandı.