Skip to content

Latest commit

 

History

History
217 lines (163 loc) · 6.16 KB

File metadata and controls

217 lines (163 loc) · 6.16 KB

Filament Live Preview

Introduction

A Filament plugin to add a preview screen to your pages using websockets. The screen will render your website with the data from Filament, without saving it. This is heavily based on Filament Peek.

Installation

You can install the package via composer:

composer require wotz/filament-live-preview

You can publish the config file with:

php artisan vendor:publish --tag="filament-live-preview-config"

This is the contents of the published config file:

return [
];

Optionally, you can publish the views using

php artisan vendor:publish --tag="filament-live-preview-views"

Usage

Install Laravel Reverb

Follow the Laravel Reverb installation instructions here

Fix Livewire and Websockets

In bootstrap/app.php, add the following lines:

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            'livewire/*',
        ]);
    })

Since we reload Livewire via websockets, we need to exclude the Livewire routes from CSRF protection. Else you will get a 419 HTTP status code when Livewire tries to make requests.

Setup Filament Live Preview

Update Filament resource

use Filament\Schemas\Components\Section;public static function form(Schema $schema): Schema
{
    return $schema
        ->columns(3)
        ->components([
            Section::make()
                ->schema([
                    // Your fields here
                ])
                ->columnSpan(function (Component $livewire) {
                    return ['lg' => $livewire->isPreviewing() ? 1 : 3];
                })
                ->extraAttributes([
                    'data-live-preview-form' => '',
                ]),

            View::make('filament-live-preview::components.live-preview-frame')
                ->hidden(fn (Component $livewire) => ! $livewire->isPreviewing())
                ->columnSpan(['lg' => 2]),
        ]);
}

Update EditPage / CreatePage

Add the HasLivePreviewComponent trait and the bundled preview action group to your header actions. The group exposes two entries: Open in sidebar (toggles the inline preview pane) and Open in new tab (opens the preview in a separate browser tab so it can be placed side-by-side on a second monitor).

use Filament\Resources\Pages\EditRecord;
use Wotz\FilamentLivePreview\Filament\Traits\HasLivePreviewComponent;

class EditPage extends EditRecord
{
    use HasLivePreviewComponent;

    protected function getHeaderActions(): array
    {
        return [
            $this->getLivePreviewAction(),
            $this->getSaveFormAction()->submit(null)->action('save'),
            DeleteAction::make(),
        ];
    }

    protected function getPreviewModalView(): ?string
    {
        // This corresponds to resources/views/posts/preview.blade.php
        return 'page.show';
    }

    protected function getPreviewModalDataRecordKey(): ?string
    {
        return 'page';
    }

    protected function mutatePreviewModalData(array $data): array
    {
        $data['titleForLayout'] = $data['page']->title;

        return $data;
    }
}

If you only want one of the two entries, call toggleIsPreviewing() (sidebar) or openPreviewInNewTab() directly from a custom action. For the new-tab variant, add ->extraAttributes(['data-live-preview-open-tab' => true]) to your action — the package's JS listens for that attribute and pre-opens a blank tab during the click, which is required to bypass popup blockers.

See the Peek docs for more details on how to customize the preview data.

Install the plugin in your panel

use Filament\Support\Enums\Width;
use Pboivin\FilamentPeek\FilamentPeekPlugin;
use Wotz\FilamentLivePreview\LivePreviewPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            // Your other plugins...
            FilamentPeekPlugin::make() // Disable styles and scripts for peek, since we override them in our package
                ->disablePluginStyles()
                ->disablePluginScripts(),
            LivePreviewPlugin::make(),
        ])
        ->maxContentWidth(Width::Full) // This is optional, but makes that your panel uses the full width of the screen
        ;
}

Fix rendering of live preview component

Since we use a full page Livewire component to render the preview, and you use a Blade component for layout, you can make some changes to

In our projects we have this AppLayout component:

<?php

namespace App\View\Components;

use App\Models\Page;
use App\Models\StaticPage;
use Closure;
use Codedor\Seo\Facades\SeoBuilder;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class AppLayout extends Component
{
    public function __construct(
        public string $titleForLayout,
    ) {
        $this->renderDiv = request()->routeIs('live-preview-frame') || request()->is('livewire/update');
    }

    public function render(): View|Closure|string
    {
        if ($this->renderDiv) {
            return <<<'blade'
        <div>
            {{ $slot }}
        </div>
    blade;
        }

        return view('layouts.app');
    }
}

This make sure that the view defined in getPreviewModalView is wrapped in a <div> instead of the full layout, when rendering the live preview component.

Add broadcasting route

In routes/channels.php, add the following line:

Broadcast::channel('live-preview', function () {});

Blade files

If you load a blade file which loads a Livewire component, it wil break the live preview for some unknown reason.

This can be fixed by wrapping the Livewire component in a conditional, like this:

@if (! is_live_preview_request())
    <livewire:filters />
@else
    {{ __('filament-live-preview::alert.preview unavailable for :name', [
        'name' => "filters"
    ]) }}
@endif