novius / laravel-filament-menu
A Filament package to manage menus in your application.
Installs: 28
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 1
Open Issues: 0
pkg:composer/novius/laravel-filament-menu
Requires
- php: ^8.2
- codewithdennis/filament-select-tree: ^4.0
- filament/filament: ^4.0 | ^5.0
- kalnoy/nestedset: ^6.0.6
- laravel/framework: ^11.0 | ^12.0
- novius/filament-relation-nested: ^1.0.0
- novius/laravel-filament-slug: ^1.0.0
- novius/laravel-filament-translatable: ^1.0.0
- novius/laravel-json-casted: ^1.2
- novius/laravel-linkable: ^2.0.0
- spatie/laravel-sluggable: ^3.4.0
Requires (Dev)
- larastan/larastan: ^2.0 | ^3.0
- laravel/pint: ^1.7
- orchestra/testbench: ^10.3
- pestphp/pest: ^3.7 || ^4.4
- pestphp/pest-plugin-laravel: ^3.0 || ^4.0
- roave/security-advisories: dev-latest
- symfony/dom-crawler: ^7.4
README
Introduction
This Laravel Filament package allows you to manage menus in your Laravel Filament admin panel.
Requirements
- PHP >= 8.2
- Laravel Filament >= 4
- Laravel Framework >= 11.0
Installation
composer require novius/laravel-filament-menu
Publish the Filament assets:
php artisan filament:assets
Then run the migrations:
php artisan migrate
In your AdminFilamentPanelProvider, add the MenuManagerPlugin:
use Novius\LaravelFilamentMenu\Filament\MenuManagerPlugin; class AdminFilamentPanelProvider extends PanelProvider { public function panel(Panel $panel): Panel { return $panel // ... ->plugins([ MenuManagerPlugin::make(), ]) // ... ; } }
Configuration
Several options are available for you to override.
php artisan vendor:publish --provider="Novius\LaravelFilamentMenu\LaravelFilamentMenuServiceProvider" --tag="config"
Usage
Blade directive
You have two ways to manage the styles of your menus. Either you use the CSS classes defined in the package, or you fill in all the attributes you need.
Classes CSS
You can display a menu with:
<x-laravel-filament-menu::menu
menu-slug="slug-of-menu"
locale="fr"
title-tag="h3"
item-empty-tag="span"
/>
menu-slug: required, the slug of your menu.locale: optional, defaults to the current locale.title-tag: optional,spanby default. Use it to change the title HTML tag if needed (useful for website footers).item-empty-tag: optional,spanby default. Use it to change the HTML tag of menu items that are neither links nor HTML blocks.
Here is a sample HTML structure with the CSS classes applied (with a menu slug of slug-of-menu):
<nav role="navigation" class="lfm-slug-of-menu"
id="lfm-slug-of-menu"
aria-label="Title of the menu">
<span class="menu-title">Title of the menu</span>
<ul class="lfm-items-container lfm--is-root" data-depth="0">
<li class="lfm-item-li">
<a href="https://example.com" class="lfm-item">First item</a>
</li>
<li class="lfm-item-li lfm--has-children" data-has-children="true">
<a href="https://example.com/2" class="lfm-item">Second item</a>
<ul class="lfm-items-container lfm--has-active-item" data-depth="1" data-active-items="true">
<li class="lfm-item-li">
<a href="https://example.com/3" class="lfm-item">First sub item</a>
</li>
<li class="lfm-item-li">
<a href="https://example.com/4" class="lfm-item lfm--active" data-active="true">Second sub item</a>
</li>
<li class="lfm-item-li">
<a href="https://example.com/5" class="lfm-item">Third sub item</a>
</li>
</ul>
</li>
</ul>
</nav>
Attributes CSS
<x-laravel-filament-menu::menu
menu-slug="slug-of-menu"
locale="fr"
container-classes="p6"
title-classes="font-bold"
list-classes="flex flex-col gap-x-6"
item-container-classes="p6"
item-classes="p6"
item-active-classes="active"
item-contains-active-classes="open"
/>
locale: optional, will use the current locale by defaultcontainer-classes: optional,'lfm-'.$menuSlugby default. Css classes for the menu container (<nav>), can be a string, an array or a Closure taking the menu as single paramatertitle-classes: optional,lfm-titleby default. Css classes for the menu title (<div>), can be a string, an array or a Closure taking the menu as single paramaterlist-classes: optional,lfm-items-containerby default. Css classes for the menu container of a list of items (<ul>), can be a string, an array or a Closure taking the item menu as single paramateritem-container-classes:lfm-item-li, null by default. Css classes for the item menu container (<li>), can be a string, an array or a Closure taking the item menu as single paramateritem-classes: optional,lfm-itemby default. Css classes for the item menu (<a>or<div>), can be a string, an array or a Closure taking the item menu as single paramateritem-active-classes: optional, null by default. Css classes for the active item menu (<a>), must be a string.data-active="true"attribute will be added to the item menu if the item is active.item-contains-active-classes: optional, null by default. Css classes for item menu containers (<ul>) containing the active item (<a>), must be a string.data-active-items="true"attribute will be added to the item menu if the item is active.
Here the sample of the css classes implemenations in HTML :
<nav role="navigation"
aria-label="Title of the menu"
class="{-- container-classes --}"
>
<span class="{-- title-classes --}">Title of the menu</span>
<ul class="{-- list-classes --}">
<li class="{-- item-container-classes --}">
<a href="https://example.com/" class="{-- item-classes --}" >First item</a>
</li>
<li class="{-- container-item-classes --}">
<a href="https://example.com/" class="{-- item-classes --}" >Second item</a>
<ul class="{-- container-items-classes & item-contains-active-classes --}" data-open="true">
<li class="{-- container-item-classes --}">
<a href="https://example.com/" class="{-- item-classes --}" >First sub item</a>
</li>
<li class="{-- container-item-classes --}">
<a href="https://example.com/"
data-active="true"
class="{-- item-classes & item-active-classes --}"
>
Second sub item
</a>
</li>
<li class="{-- container-item-classes --}">
<a href="https://example.com/" class="{-- item-classes --}" >Third sub item</a>
</li>
</ul>
</li>
</ul>
</nav>
Write your own template
Template class
namespace App\Menus\Templates; use Novius\LaravelFilamentMenu\Concerns\IsMenuTemplate; use Novius\LaravelFilamentMenu\Contracts\MenuTemplate; class MyMenuTemplate implements MenuTemplate // Must implement the MenuTemplate interface { use IsMenuTemplate; // This trait defines the required methods with default implementations public function key(): string { return 'my-template'; } public function name(): string { return 'My template'; } public function hasTitle(): bool { return true; // Indicates whether the menu needs a title displayed on the front end } public function maxDepth(): int { return 1; // Defines the maximum menu depth } public function fields(): array { return [ \Filament\Forms\Components\DatePicker::make('extras.date'), // You can add additional fields to items; prefix names with `extras.` to store them in the extras field ]; } public function casts(): array { return [ 'date' => 'date:Y-m-d', // Define casts for any additional item fields ]; } public function view(): string { return 'menus.my-template'; // View used to render the menu } public function viewItem(): string { return 'menus.my-template-item'; // View used to render individual menu items } }
Template views
First, the view to display the menu:
@php
use Novius\LaravelFilamentMenu\Models\Menu;
/** @var Menu $menu */
@endphp
<nav role="navigation"
id="menu-{{ $menu->slug }}"
aria-label="{{ $menu->aria_label ?? $menu->title ?? $menu->name }}"
@class($containerClasses)
>
@if ($menu->template->hasTitle())
<x-laravel-filament-menu::dynamic-tag :tag="$titleTag" @class($titleClasses)>
{{ $menu->title ?? $menu->name }}
</x-laravel-filament-menu::dynamic-tag>
@endif
<ul @class([...$listClasses(), ...$listRootClasses()]) data-depth="0">
@foreach($items as $item)
{!! $menu->template->renderItem(
$menu,
$item,
$listClasses,
$itemContainerClasses,
$itemClasses,
$itemEmptyTag,
$itemActiveClasses,
$itemContainsActiveClasses
) !!}
@endforeach
</ul>
</nav>
Then, the view to display an item of the menu:
@php
use Novius\LaravelFilamentMenu\Enums\LinkType;
use Novius\LaravelFilamentMenu\Models\Menu;
use Novius\LaravelFilamentMenu\Models\MenuItem;
/** @var Menu $menu */
/** @var MenuItem $item */
@endphp
<li @class($itemContainerClasses) @if ($item->children->isNotEmpty()) data-has-children="true" @endif>
@if ($item->link_type === LinkType::html)
{!! $item->html !!}
@elseif ($item->link_type !== LinkType::empty)
<a href="{{ $item->href() }}"
@class([
...$itemClasses,
$menu->template->isActiveItem($item) ? $itemActiveClasses : '',
$item->html_classes
])
@if ($item->target_blank) target="_blank" rel="noopener noreferrer" @endif
@if ($menu->template->isActiveItem($item)) data-active="true" @endif
>
{{ $item->title }}
</a>
@else
<x-laravel-filament-menu::dynamic-tag :tag="$itemEmptyTag"
@class([
...$itemClasses,
$item->html_classes
])
>
{{ $item->title }}
</x-laravel-filament-menu::dynamic-tag>
@endif
@if ($item->children->isNotEmpty())
<ul
data-depth="{{ $item->depth + 1 }}"
@if ($menu->template->containsActiveItem($item)) data-active-items="true" @endif
@class([
...$listClasses,
$menu->template->containsActiveItem($item) ? $itemContainsActiveClasses : '',
])
>
@foreach($item->children as $item)
{!! $menu->template->renderItem(
$menu,
$item
$listClasses,
$itemContainerClasses,
$itemClasses,
$itemEmptyTag,
$itemActiveClasses,
$itemContainsActiveClasses,
) !!}
@endforeach
</ul>
@endif
</li>
Manage internal links
Laravel Filament Menu uses Laravel Linkable to manage linkable routes and models. Refer to its documentation for detailed usage instructions.
Seeder
You can use the \Novius\LaravelFilamentMenu\Database\Seeders\MenuSeeder to create menus.
Create a new seeder, extend the class, and define the menus() method. You can also override the postCreate() method to add custom logic.
namespace Database\Seeders; use Novius\LaravelFilamentMenu\Templates\MenuTemplateWithoutTitle; use Novius\LaravelFilamentMenu\Templates\MenuTemplateWithTitle; class MenuSeeder extends \Novius\LaravelFilamentMenu\Database\Seeders\MenuSeeder { protected function menus(): array { return [ 'header' => [ 'name' => 'Header', 'template' => MenuTemplateWithoutTitle::class, ], 'footer' => [ 'name' => 'Footer', 'template' => MenuTemplateWithTitle::class, ], ]; } protected function postCreate(array $config, LocaleData $locale, Menu $menu): void { // Add custom logic here } }
Lint
Run PHP-CS Fixer with:
composer run-script lint
Contributing
Contributions are welcome! Leave an issue on GitHub, or create a Pull Request.
License
This package is under GNU Affero General Public License v3 or (at your option) any later version.