From 4a65396e04a38fe7c4171f9b691a5c56cf1f9072 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Wed, 6 Mar 2024 12:34:56 +0000 Subject: [PATCH] feature: SPA mode URL exceptions --- packages/panels/docs/09-configuration.md | 38 +++++++++++++++++++ packages/panels/src/Panel.php | 1 + .../panels/src/Panel/Concerns/HasSpaMode.php | 23 +++++++++++ packages/support/src/Facades/FilamentView.php | 12 +++++- packages/support/src/View/ViewManager.php | 34 ++++++++++++++++- packages/support/src/helpers.php | 2 +- 6 files changed, 106 insertions(+), 4 deletions(-) diff --git a/packages/panels/docs/09-configuration.md b/packages/panels/docs/09-configuration.md index 574ebbeba6..64ae8e6599 100644 --- a/packages/panels/docs/09-configuration.md +++ b/packages/panels/docs/09-configuration.md @@ -147,6 +147,44 @@ public function panel(Panel $panel): Panel } ``` +### Disabling SPA navigation for specific URLs + +By default, when enabling SPA mode, any URL that lives on the same domain as the current request will be navigated to using Livewire's [`wire:navigate`](https://livewire.laravel.com/docs/navigate) feature. If you want to disable this for specific URLs, you can use the `spaUrlExceptions()` method: + +```php +use App\Filament\Resources\PostResource; +use Filament\Panel; + +public function panel(Panel $panel): Panel +{ + return $panel + // ... + ->spa() + ->spaUrlExceptions(fn (): array => [ + url('/admin'), + PostResource::getUrl(), + ]); +} +``` + +> In this example, we are using [`getUrl()`](/resources/getting-started#generating-urls-to-resource-pages) on a resource to get the URL to the resource's index page. This feature requires the panel to already be registered though, and the configuration is too early in the request lifecycle to do that. You can use a function to return the URLs instead, which will be resolved when the panel has been registered. + +These URLs need to exactly match the URL that the user is navigating to, including the domain and protocol. If you'd like to use a pattern to match multiple URLs, you can use an asterisk (`*`) as a wildcard character: + +```php +use Filament\Panel; + +public function panel(Panel $panel): Panel +{ + return $panel + // ... + ->spa() + ->spaUrlExceptions([ + '*/admin/posts/*', + ]); +} +``` + ## Unsaved changes alerts You may alert users if they attempt to navigate away from a page without saving their changes. This is applied on [Create](resources/creating-records) and [Edit](resources/editing-records) pages of a resource, as well as any open action modals. To enable this feature, you can use the `unsavedChangesAlerts()` method: diff --git a/packages/panels/src/Panel.php b/packages/panels/src/Panel.php index 1ea0f68516..4165cff255 100644 --- a/packages/panels/src/Panel.php +++ b/packages/panels/src/Panel.php @@ -77,6 +77,7 @@ class Panel extends Component FilamentIcon::register($this->getIcons()); FilamentView::spa($this->hasSpaMode()); + FilamentView::spaUrlExceptions($this->getSpaUrlExceptions()); $this->registerRenderHooks(); diff --git a/packages/panels/src/Panel/Concerns/HasSpaMode.php b/packages/panels/src/Panel/Concerns/HasSpaMode.php index eda55a0fbb..01bd89432c 100644 --- a/packages/panels/src/Panel/Concerns/HasSpaMode.php +++ b/packages/panels/src/Panel/Concerns/HasSpaMode.php @@ -8,6 +8,11 @@ trait HasSpaMode { protected bool | Closure $hasSpaMode = false; + /** + * @var array | Closure + */ + protected array | Closure $spaModeUrlExceptions = []; + public function spa(bool | Closure $condition = true): static { $this->hasSpaMode = $condition; @@ -15,8 +20,26 @@ trait HasSpaMode return $this; } + /** + * @param array| Closure $exceptions + */ + public function spaUrlExceptions(array | Closure $exceptions): static + { + $this->spaModeUrlExceptions = $exceptions; + + return $this; + } + public function hasSpaMode(): bool { return (bool) $this->evaluate($this->hasSpaMode); } + + /** + * @return array + */ + public function getSpaUrlExceptions(): array + { + return $this->evaluate($this->spaModeUrlExceptions); + } } diff --git a/packages/support/src/Facades/FilamentView.php b/packages/support/src/Facades/FilamentView.php index 21af3e4eca..d3309c63f6 100644 --- a/packages/support/src/Facades/FilamentView.php +++ b/packages/support/src/Facades/FilamentView.php @@ -8,7 +8,7 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Support\Facades\Facade; /** - * @method static bool hasSpaMode() + * @method static bool hasSpaMode(?string $url = null) * @method static Htmlable renderHook(string $name, string | array | null $scopes = null) * * @see ViewManager @@ -36,4 +36,14 @@ class FilamentView extends Facade $viewManager->spa($condition); }); } + + /** + * @param array $exceptions + */ + public static function spaUrlExceptions(array $exceptions): void + { + static::resolved(function (ViewManager $viewManager) use ($exceptions) { + $viewManager->spaUrlExceptions($exceptions); + }); + } } diff --git a/packages/support/src/View/ViewManager.php b/packages/support/src/View/ViewManager.php index cc791c4a93..7f589409a2 100644 --- a/packages/support/src/View/ViewManager.php +++ b/packages/support/src/View/ViewManager.php @@ -7,6 +7,8 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Support\Arr; use Illuminate\Support\HtmlString; +use function Filament\Support\is_app_url; + class ViewManager { /** @@ -16,6 +18,11 @@ class ViewManager protected bool $hasSpaMode = false; + /** + * @var array + */ + protected array $spaModeUrlExceptions = []; + /** * @param string | array | null $scopes */ @@ -74,8 +81,31 @@ class ViewManager $this->hasSpaMode = $condition; } - public function hasSpaMode(): bool + /** + * @param array $exceptions + */ + public function spaUrlExceptions(array $exceptions): void { - return $this->hasSpaMode; + $this->spaModeUrlExceptions = [ + ...$this->spaModeUrlExceptions, + ...$exceptions, + ]; + } + + public function hasSpaMode(?string $url = null): bool + { + if (! $this->hasSpaMode) { + return false; + } + + if (blank($url)) { + return true; + } + + if (count($this->spaModeUrlExceptions) && str($url)->is($this->spaModeUrlExceptions)) { + return false; + } + + return is_app_url($url); } } diff --git a/packages/support/src/helpers.php b/packages/support/src/helpers.php index cb3dcd010c..21c58d9bb8 100644 --- a/packages/support/src/helpers.php +++ b/packages/support/src/helpers.php @@ -147,7 +147,7 @@ if (! function_exists('Filament\Support\generate_href_html')) { if ($shouldOpenInNewTab) { $html .= ' target="_blank"'; - } elseif ($shouldOpenInSpaMode ?? (FilamentView::hasSpaMode() && is_app_url($url))) { + } elseif ($shouldOpenInSpaMode ?? (FilamentView::hasSpaMode($url))) { $html .= ' wire:navigate'; }