From 2cb89efc88e7ba1f541384204913e5e58d3c0454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jefferson=20Gon=C3=A7alves?= Date: Sat, 2 Aug 2025 22:31:40 -0300 Subject: [PATCH 1/9] feat(panels): add support for customizable topbar Livewire component Introduce the ability to set a customizable Livewire component for the topbar via `topbarLivewireComponent`. Updated `HasTopbar` trait to handle dynamic registration of the topbar component, providing greater flexibility in panel customization. --- .../src/Panel/Concerns/HasComponents.php | 2 +- .../panels/src/Panel/Concerns/HasTopbar.php | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/panels/src/Panel/Concerns/HasComponents.php b/packages/panels/src/Panel/Concerns/HasComponents.php index cff4409358..4d0f22b743 100644 --- a/packages/panels/src/Panel/Concerns/HasComponents.php +++ b/packages/panels/src/Panel/Concerns/HasComponents.php @@ -493,7 +493,7 @@ trait HasComponents $this->queueLivewireComponentForRegistration(Notifications::class); $this->queueLivewireComponentForRegistration(Sidebar::class); $this->queueLivewireComponentForRegistration(SimpleUserMenu::class); - $this->queueLivewireComponentForRegistration(Topbar::class); + $this->queueLivewireComponentForRegistration($this->getTopbarLivewireComponent()); if ($this->hasEmailVerification() && is_subclass_of($emailVerificationPromptRouteAction = $this->getEmailVerificationPromptRouteAction(), Component::class)) { $this->queueLivewireComponentForRegistration($emailVerificationPromptRouteAction); diff --git a/packages/panels/src/Panel/Concerns/HasTopbar.php b/packages/panels/src/Panel/Concerns/HasTopbar.php index b7d8428db2..c44b26a3ac 100644 --- a/packages/panels/src/Panel/Concerns/HasTopbar.php +++ b/packages/panels/src/Panel/Concerns/HasTopbar.php @@ -3,14 +3,29 @@ namespace Filament\Panel\Concerns; use Closure; +use Filament\Livewire\Topbar; +use Livewire\Component; trait HasTopbar { protected bool | Closure $hasTopbar = true; - public function topbar(bool | Closure $condition = true): static + protected string | Closure | null $topbarLivewireComponent = null; + + public function topbar(bool | Closure $condition = true, string | Closure | null $livewireComponent = null): static { $this->hasTopbar = $condition; + $this->topbarLivewireComponent($livewireComponent); + + return $this; + } + + /** + * @param class-string | Closure | null $component + */ + public function topbarLivewireComponent(string | Closure | null $component): static + { + $this->topbarLivewireComponent = $component; return $this; } @@ -19,4 +34,12 @@ trait HasTopbar { return (bool) $this->evaluate($this->hasTopbar); } + + /** + * @return class-string + */ + public function getTopbarLivewireComponent(): string + { + return $this->evaluate($this->topbarLivewireComponent) ?? Topbar::class; + } } From 354dede23b76e3a02435bfbb03a6ef1c8d176ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jefferson=20Gon=C3=A7alves?= Date: Sat, 2 Aug 2025 22:33:42 -0300 Subject: [PATCH 2/9] refactor(panels): remove unused Topbar Livewire component import Clean up unused `Topbar` import in `HasComponents` to streamline codebase. --- packages/panels/src/Panel/Concerns/HasComponents.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/panels/src/Panel/Concerns/HasComponents.php b/packages/panels/src/Panel/Concerns/HasComponents.php index 4d0f22b743..27880ff2d5 100644 --- a/packages/panels/src/Panel/Concerns/HasComponents.php +++ b/packages/panels/src/Panel/Concerns/HasComponents.php @@ -9,7 +9,6 @@ use Filament\Livewire\GlobalSearch; use Filament\Livewire\Notifications; use Filament\Livewire\Sidebar; use Filament\Livewire\SimpleUserMenu; -use Filament\Livewire\Topbar; use Filament\Pages\Page; use Filament\Resources\Pages\Page as ResourcePage; use Filament\Resources\RelationManagers\RelationGroup; From 13e9c5fc051106421e364bf27ec86fa543f9f26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jefferson=20Gon=C3=A7alves?= Date: Sat, 2 Aug 2025 23:25:11 -0300 Subject: [PATCH 3/9] refactor(panels): use dynamic topbar livewire component Update to dynamically fetch the topbar Livewire component via `FilamentManager::getTopbarLivewireComponent()` instead of hardcoding the class reference, improving flexibility for topbar customization. --- .../panels/resources/views/components/layout/index.blade.php | 2 +- packages/panels/src/FilamentManager.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/panels/resources/views/components/layout/index.blade.php b/packages/panels/resources/views/components/layout/index.blade.php index 7887dab856..f957e416ab 100644 --- a/packages/panels/resources/views/components/layout/index.blade.php +++ b/packages/panels/resources/views/components/layout/index.blade.php @@ -28,7 +28,7 @@ @if ($hasTopbar) {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_BEFORE, scopes: $renderHookScopes) }} - @livewire(\Filament\Livewire\Topbar::class) + @livewire(filament()->getTopbarLivewireComponent()) {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::TOPBAR_AFTER, scopes: $renderHookScopes) }} @endif diff --git a/packages/panels/src/FilamentManager.php b/packages/panels/src/FilamentManager.php index e4fd7c622a..1a0edd1c88 100644 --- a/packages/panels/src/FilamentManager.php +++ b/packages/panels/src/FilamentManager.php @@ -488,6 +488,11 @@ class FilamentManager return $this->getCurrentOrDefaultPanel()->getProfilePage(); } + public function getTopbarLivewireComponent(): string + { + return $this->getCurrentOrDefaultPanel()->getTopbarLivewireComponent(); + } + public function getTenantProfilePage(): ?string { return $this->getCurrentOrDefaultPanel()->getTenantProfilePage(); From f3b2599693436d828f6240b7888448cd81537a94 Mon Sep 17 00:00:00 2001 From: Ali Salehi <111766206+alisalehi1380@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:14:26 +0330 Subject: [PATCH 4/9] Update register.php --- packages/panels/resources/lang/fa/pages/auth/register.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/panels/resources/lang/fa/pages/auth/register.php b/packages/panels/resources/lang/fa/pages/auth/register.php index 1693f76a7b..162f29c5cc 100644 --- a/packages/panels/resources/lang/fa/pages/auth/register.php +++ b/packages/panels/resources/lang/fa/pages/auth/register.php @@ -10,7 +10,7 @@ return [ 'login' => [ 'before' => 'یا', - 'label' => 'وارد حساب خود شوید.', + 'label' => 'وارد حساب خود شوید', ], ], From c1f122b80e9986c60b113b97768f617753e8f36a Mon Sep 17 00:00:00 2001 From: Ali Salehi <111766206+alisalehi1380@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:30:31 +0330 Subject: [PATCH 5/9] Update register.php --- packages/panels/resources/lang/fa/pages/auth/register.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/panels/resources/lang/fa/pages/auth/register.php b/packages/panels/resources/lang/fa/pages/auth/register.php index 162f29c5cc..303090ead9 100644 --- a/packages/panels/resources/lang/fa/pages/auth/register.php +++ b/packages/panels/resources/lang/fa/pages/auth/register.php @@ -10,7 +10,7 @@ return [ 'login' => [ 'before' => 'یا', - 'label' => 'وارد حساب خود شوید', + 'label' => 'ورود به حساب کاربری', ], ], From 8d62f95b66cd8c4093b877c41031ff0314c9551f Mon Sep 17 00:00:00 2001 From: Ali Salehi <111766206+alisalehi1380@users.noreply.github.com> Date: Sun, 3 Aug 2025 17:16:47 +0330 Subject: [PATCH 6/9] add return type in wrapInDatabaseTransaction to Register page --- packages/panels/src/Pages/Auth/Register.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/panels/src/Pages/Auth/Register.php b/packages/panels/src/Pages/Auth/Register.php index 1c52d44af6..e9aca6afc2 100644 --- a/packages/panels/src/Pages/Auth/Register.php +++ b/packages/panels/src/Pages/Auth/Register.php @@ -25,6 +25,7 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules\Password; +use Illuminate\Database\Eloquent\Model; /** * @property Form $form @@ -70,7 +71,7 @@ class Register extends SimplePage return null; } - $user = $this->wrapInDatabaseTransaction(function () { + $user = $this->wrapInDatabaseTransaction(function (): Model { $this->callHook('beforeValidate'); $data = $this->form->getState(); From 11b9e7c5d9634330c50ffbacc13b909aefc031d5 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Mon, 4 Aug 2025 08:52:18 +0100 Subject: [PATCH 7/9] feature: Reorder tables in descending order --- packages/tables/docs/01-overview.md | 12 ++++++++++++ packages/tables/src/Concerns/CanReorderRecords.php | 2 ++ packages/tables/src/Concerns/CanSortRecords.php | 2 +- .../tables/src/Table/Concerns/CanReorderRecords.php | 11 ++++++++++- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/tables/docs/01-overview.md b/packages/tables/docs/01-overview.md index e9c158d5d2..bae7386edd 100644 --- a/packages/tables/docs/01-overview.md +++ b/packages/tables/docs/01-overview.md @@ -387,6 +387,18 @@ public function table(Table $table): Table } ``` +You can pass a `direction` parameter as `desc` to reorder the records in descending order instead of ascending: + +```php +use Filament\Tables\Table; + +public function table(Table $table): Table +{ + return $table + ->reorderable('sort', direction: 'desc'); +} +``` + ### Enabling pagination while reordering Pagination will be disabled in reorder mode to allow you to move records between pages. It is generally a bad experience to have pagination while reordering, but if would like to override this use `$table->paginatedWhileReordering()`: diff --git a/packages/tables/src/Concerns/CanReorderRecords.php b/packages/tables/src/Concerns/CanReorderRecords.php index 3e472f9fef..4c53f062b9 100644 --- a/packages/tables/src/Concerns/CanReorderRecords.php +++ b/packages/tables/src/Concerns/CanReorderRecords.php @@ -4,6 +4,7 @@ namespace Filament\Tables\Concerns; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Query\Expression; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; trait CanReorderRecords @@ -45,6 +46,7 @@ trait CanReorderRecords ->update([ $orderColumn => new Expression( 'case ' . collect($order) + ->when($this->getTable()->getReorderDirection() === 'desc', fn (Collection $order) => $order->reverse()->values()) ->map(fn ($recordKey, int $recordIndex): string => 'when ' . $wrappedModelKeyName . ' = ' . DB::getPdo()->quote($recordKey) . ' then ' . ($recordIndex + 1)) ->implode(' ') . ' end' ), diff --git a/packages/tables/src/Concerns/CanSortRecords.php b/packages/tables/src/Concerns/CanSortRecords.php index c53e7dd1fe..851c7789ab 100644 --- a/packages/tables/src/Concerns/CanSortRecords.php +++ b/packages/tables/src/Concerns/CanSortRecords.php @@ -74,7 +74,7 @@ trait CanSortRecords } if ($this->isTableReordering()) { - return $query->orderBy($this->getTable()->getReorderColumn()); + return $query->orderBy($this->getTable()->getReorderColumn(), $this->getTable()->getReorderDirection()); } if ( diff --git a/packages/tables/src/Table/Concerns/CanReorderRecords.php b/packages/tables/src/Table/Concerns/CanReorderRecords.php index 24e2fafaa5..0d6732e94b 100644 --- a/packages/tables/src/Table/Concerns/CanReorderRecords.php +++ b/packages/tables/src/Table/Concerns/CanReorderRecords.php @@ -19,6 +19,8 @@ trait CanReorderRecords protected string | Closure | null $reorderColumn = null; + protected string | Closure | null $reorderDirection = null; + protected ?Closure $modifyReorderRecordsTriggerActionUsing = null; public function reorderRecordsTriggerAction(?Closure $callback): static @@ -28,7 +30,7 @@ trait CanReorderRecords return $this; } - public function reorderable(string | Closure | null $column = null, bool | Closure | null $condition = null): static + public function reorderable(string | Closure | null $column = null, bool | Closure | null $condition = null, string | Closure | null $direction = null): static { $this->reorderColumn = $column; @@ -36,6 +38,8 @@ trait CanReorderRecords $this->isReorderable = $condition; } + $this->reorderDirection = $direction; + return $this; } @@ -72,6 +76,11 @@ trait CanReorderRecords return $this->evaluate($this->reorderColumn); } + public function getReorderDirection(): string + { + return $this->evaluate($this->reorderDirection) ?? 'asc'; + } + public function isReorderable(): bool { return filled($this->getReorderColumn()) && $this->evaluate($this->isReorderable) && $this->isReorderAuthorized(); From ac0a8c5881662c713925f4df15452d0809c90638 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Mon, 4 Aug 2025 08:54:28 +0100 Subject: [PATCH 8/9] Update Register.php --- packages/panels/src/Pages/Auth/Register.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/panels/src/Pages/Auth/Register.php b/packages/panels/src/Pages/Auth/Register.php index e9aca6afc2..f78dc814e7 100644 --- a/packages/panels/src/Pages/Auth/Register.php +++ b/packages/panels/src/Pages/Auth/Register.php @@ -25,7 +25,6 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules\Password; -use Illuminate\Database\Eloquent\Model; /** * @property Form $form From 713a6ec9c1f7c63c8d8d190723162bc3f1d16359 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Mon, 4 Aug 2025 09:05:01 +0100 Subject: [PATCH 9/9] clean up & docs --- docs/06-navigation/01-overview.md | 18 ++++++++++++++++++ packages/panels/src/Facades/Filament.php | 2 ++ packages/panels/src/FilamentManager.php | 4 ++++ .../panels/src/Panel/Concerns/HasTopbar.php | 3 +-- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/06-navigation/01-overview.md b/docs/06-navigation/01-overview.md index 4edd174154..b21a9f59d0 100644 --- a/docs/06-navigation/01-overview.md +++ b/docs/06-navigation/01-overview.md @@ -552,6 +552,24 @@ public function panel(Panel $panel): Panel } ``` +### Replacing the sidebar and topbar Livewire components + +You may completely replace the Livewire components that are used to render the sidebar and topbar, passing your own Livewire component class name into the `sidebarLivewireComponent()` or `topbarLivewireComponent()` method: + +```php +use App\Livewire\Sidebar; +use App\Livewire\Topbar; +use Filament\Panel; + +public function panel(Panel $panel): Panel +{ + return $panel + // ... + ->sidebarLivewireComponent(Sidebar::class) + ->topbarLivewireComponent(Topbar::class); +} +``` + ## Disabling breadcrumbs The default layout will show breadcrumbs to indicate the location of the current page within the hierarchy of the app. diff --git a/packages/panels/src/Facades/Filament.php b/packages/panels/src/Facades/Filament.php index 9b25df2b87..a21ab7d1d3 100644 --- a/packages/panels/src/Facades/Filament.php +++ b/packages/panels/src/Facades/Filament.php @@ -26,6 +26,7 @@ use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Facade; +use Livewire\Component; /** * @method static bool arePasswordsRevealable() @@ -105,6 +106,7 @@ use Illuminate\Support\Facades\Facade; * @method static string | null getTenantProfileUrl(array $parameters = []) * @method static string | null getTenantRegistrationUrl(array $parameters = []) * @method static Theme getTheme() + * @method static class-string getTopbarLivewireComponent() * @method static ThemeMode getDefaultThemeMode() * @method static string | null getUserAvatarUrl(Model | Authenticatable $user) * @method static Model | null getUserDefaultTenant(HasTenants | Model | Authenticatable $user) diff --git a/packages/panels/src/FilamentManager.php b/packages/panels/src/FilamentManager.php index 1a0edd1c88..7f6d712b74 100644 --- a/packages/panels/src/FilamentManager.php +++ b/packages/panels/src/FilamentManager.php @@ -34,6 +34,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Event; +use Livewire\Component; class FilamentManager { @@ -488,6 +489,9 @@ class FilamentManager return $this->getCurrentOrDefaultPanel()->getProfilePage(); } + /** + * @return class-string + */ public function getTopbarLivewireComponent(): string { return $this->getCurrentOrDefaultPanel()->getTopbarLivewireComponent(); diff --git a/packages/panels/src/Panel/Concerns/HasTopbar.php b/packages/panels/src/Panel/Concerns/HasTopbar.php index c44b26a3ac..ee5936daa8 100644 --- a/packages/panels/src/Panel/Concerns/HasTopbar.php +++ b/packages/panels/src/Panel/Concerns/HasTopbar.php @@ -12,10 +12,9 @@ trait HasTopbar protected string | Closure | null $topbarLivewireComponent = null; - public function topbar(bool | Closure $condition = true, string | Closure | null $livewireComponent = null): static + public function topbar(bool | Closure $condition = true): static { $this->hasTopbar = $condition; - $this->topbarLivewireComponent($livewireComponent); return $this; }