aymanalhattami/filament-context-menu
Add right‑click context menus to Filament pages and table cells using Filament Actions. Works on resource and custom pages, supports dividers, dark/light mode, and LTR/RTL. Includes Refresh, Go Back, and Go Forward actions. Filament v3–v5.
Installation:
composer require aymanalhattami/filament-context-menu
Ensure compatibility with your Filament version (3.x, 4.x, or 5.x).
First Use Case:
Add the PageHasContextMenu trait to a Filament page (e.g., ListRecords or ViewRecord):
use AymanAlhattami\FilamentContextMenu\Traits\PageHasContextMenu;
class ListUsers extends ListRecords
{
use PageHasContextMenu;
public function getContextMenuActions(): array
{
return [
Action::make('Refresh Page')
->action(fn () => $this->refresh())
];
}
}
Right-click anywhere on the page to test the context menu.
Dynamic Actions:
Use getContextMenuActions() to return an array of Filament Action objects. Leverage Filament’s built-in actions (e.g., CreateAction, EditAction) or custom actions:
public function getContextMenuActions(): array
{
return [
CreateAction::make()->model(User::class),
Action::make('Custom Action')
->icon('heroicon-o-pencil')
->action(fn () => $this->customLogic())
];
}
Conditional Logic: Dynamically filter actions based on user roles or record state:
public function getContextMenuActions(): array
{
$actions = [/* default actions */];
if ($this->auth()->user()->can('delete')) {
$actions[] = DeleteAction::make()->record($this->record);
}
return $actions;
}
Dividers:
Use ContextMenuDivider to group actions:
return [
Action::make('Group 1'),
ContextMenuDivider::make(),
Action::make('Group 2'),
];
Column Integration:
Replace standard columns with context-menu-enabled variants (e.g., ContextMenuTextColumn):
Table::make(User::class)
->columns([
ContextMenuTextColumn::make('name')
->getContextMenuActions(fn ($record) => [
Action::make('Edit Name')
->action(fn () => $record->update(['name' => 'Updated']))
]),
]);
Record-Specific Actions:
Pass the record to getContextMenuActions for dynamic actions:
ContextMenuTextColumn::make('status')
->getContextMenuActions(fn ($record) => [
Action::make('Toggle Active')
->icon($record->active ? 'heroicon-o-x-circle' : 'heroicon-o-check-circle')
->action(fn () => $record->update(['active' => !$record->active])),
]);
Bulk Actions: Combine with table bulk actions for consistency:
Table::make(User::class)
->columns([
ContextMenuCheckboxColumn::make('select'),
// ...
])
->actions([
Action::make('Bulk Delete')
->requiresConfirmation()
->action(fn () => User::whereIn('id', $this->getSelectedRecords())->delete()),
]);
Dashboard Integration: Add context menus to custom dashboard widgets or pages:
class CustomDashboard extends Page
{
use PageHasContextMenu;
public function getContextMenuActions(): array
{
return [
Action::make('Reset Dashboard')
->action(fn () => $this->resetLayout()),
];
}
}
Modal Actions: Use modal-based actions for complex operations:
Action::make('Advanced Edit')
->modalContent(fn ($record) => view('filament.modals.advanced-edit', ['record' => $record]))
->action(fn () => $this->handleAdvancedEdit($record)),
Nested Resources:
Extend context menus for nested resource pages (e.g., EditPost with EditComment sub-actions):
class EditPost extends EditRecord
{
use PageHasContextMenu;
public function getContextMenuActions(): array
{
return [
Action::make('Edit Comments')
->url(fn () => route('filament.admin.resources.comments.edit', $this->record->comments->first())),
];
}
}
Action Registration Timing:
getContextMenuActions() is defined as static but relies on instance properties (e.g., $this->record).public function getContextMenuActions(): array
{
return $this->resolveActions($this->record); // Custom method
}
Duplicate Actions:
getContextMenuActions().Dark Mode Conflicts:
dark:bg-gray-800) or override via CSS:
.filament-context-menu {
background: var(--color-gray-100);
}
.filament-context-menu.dark {
background: var(--color-gray-800);
}
Inspect Menu Content: Use browser dev tools to check if actions are rendered:
console.log(document.querySelectorAll('.filament-context-menu li'));
Disable Globally:
Temporarily disable the menu via .env:
CONTEXT_MENU_ENABLED=false
Or override in the page:
public static bool $contextMenuEnabled = false;
Action Logging:
Add debug logs to getContextMenuActions() to verify action resolution:
public function getContextMenuActions(): array
{
$actions = [
Action::make('Debug')
->action(fn () => Log::debug('Context menu triggered')),
];
Log::debug('Context menu actions:', $actions);
return $actions;
}
Custom Styling:
Override the menu’s Blade template (resources/views/vendor/filament-context-menu/menu.blade.php) or use CSS variables:
.filament-context-menu {
--context-menu-width: 200px;
--context-menu-padding: 0.5rem;
}
Event Listeners:
Listen for context menu events (e.g., context-menu:opening):
public function register()
{
FilamentContextMenu::listen('context-menu:opening', fn ($event) => {
Log::info('Menu opened for page:', $event->page);
});
}
Localization: Translate action labels using Filament’s translation system:
Action::make(__('filament-context-menu::actions.refresh'))
->action(fn () => $this->refresh()),
Keyboard Shortcuts: Combine with Filament’s keyboard shortcuts for power users:
public static function getNavigationShortcuts(): array
{
return [
shortcut('r', fn () => redirect()->refresh()),
];
}
Lazy-Load Actions: Defer action resolution until the menu is opened:
public function getContextMenuActions(): array
{
return [
Action::make('Lazy Action')
->action(fn () => $this->resolveLazyAction()),
];
}
Memoization: Cache resolved actions if they’re computationally expensive:
protected ?array $contextMenuActions = null;
public function getContextMenuActions(): array
{
return $this->contextMenuActions ??= $this->resolveActions();
}
Avoid Heavy Dependencies:
Keep getContextMenuActions() lightweight. Offload complex logic to separate methods or services.
How can I help you explore Laravel packages today?