Mason for Filament

repository·3.x·Indexed 42 days ago

https://github.com/awcodes/mason

A block-based, drag-and-drop page and document builder for Filament. Mason allows users to construct complex layouts using reusable 'bricks' stored as JSON in the database, featuring a custom editor UI, BrickCollections for organization, and a dedicated renderer for Blade templates.

Tokens
15.2K
Snippets
72
Records
82
Agent score
51%

What's inside Mason

  1. What is Mason?

    3.x

    Mason is a block-based, drag-and-drop page builder designed for Filament forms and infolists. It allows authors to assemble documents using bricks—custom classes that define their own settings (via a form) and their visual output (via a Blade view). The assembled document is stored as a JSON object in your database.

    To ensure visual consistency, the editor renders inside an iframe using your application's own stylesheets, meaning the building experience matches the final front-end output.

  2. Anatomy of a Mason brick class

    3.x

    A brick is a class extending Awcodes\Mason\Brick. It defines how the brick is identified, how it appears in the sidebar, how it is configured via a form, and how it renders to HTML.

    Core Methods

    MethodPurpose
    getId()Required. The unique identifier stored in the JSON. Changing this will orphan existing content.
    getLabel()The label shown in the sidebar. Defaults to the class name if not overridden.
    getIcon()The sidebar icon. Accepts a Heroicon case, an icon name string, or an Htmlable (for inline SVGs).
    getTags()An array of search terms used to improve discoverability in the sidebar search.
    toHtml(array $config, ?array $data = null)Renders the brick's HTML using the saved $config and optional $data provided by the renderer.
    configureBrickAction(Action $action)Configures the Filament Action that builds the form used for inserting or editing the brick.

    Example Brick Implementation

    use Awcodes\Mason\Brick;
    use Filament\Actions\Action;
    use Filament\Forms\Components\FileUpload;
    use Filament\Forms\Components\Radio;
    use Filament\Forms\Components\RichEditor;
    use Filament\Support\Icons\Heroicon;
    use Illuminate\Contracts\Support\Htmlable;
    
    class Section extends Brick
    {
        public static function getId(): string
        {
            return 'section';
        }
    
        public static function getIcon(): string | Heroicon | Htmlable | null
        {
            return Heroicon::OutlinedSquares2x2;
        }
    
        public static function getTags(): array
        {
            return ['section', 'content', 'layout'];
        }
    
        public static function toHtml(array $config, ?array $data = null): ?string
        {
            return view('mason.section', [
                'background_color' => $config['background_color'] ?? 'white',
                'image' => $config['image'] ?? null,
                'text' => $config['text'] ?? null,
            ])->render();
        }
    
        public static function configureBrickAction(Action $action): Action
        {
            return $action
                ->slideOver()
                ->schema([
                    Radio::make('background_color'),
                    FileUpload::make('image'),
                    RichEditor::make('text'),
                ]);
        }
    }
  3. Organize bricks using BrickCollections

    3.x

    To avoid repeating brick definitions across different parts of your application (e.g., in both the editor and the entry), create a static collection class. This keeps your code DRY and ensures consistency.

    class BrickCollection
    {
        public static function make(): array
        {
            return [
                NewsletterSignup::class,
                Section::class,
                Cards::class,
                SupportCenter::class,
            ];
        }
    }
    
    Mason::make('content')
        ->bricks(BrickCollection::make())
    
    MasonEntry::make('content')
        ->bricks(BrickCollection::make())
  4. Pass data to bricks during rendering

    3.x

    Bricks can access external data (like the Eloquent model they belong to) by passing a third argument to the helper or directive. This data is passed to the brick's toHtml() method via the $data parameter. While record is a common convention, the array key can be anything.

    Example Brick Implementation:

    public static function toHtml(array $config, ?array $data = null): ?string
    {
        $record = $data['record'] ?? null;
        return view('mason.byline', ['author' => $record?->author?->name])->render();
    }

    Usage in Blade:

    @mason($post->content, \App\Mason\BrickCollection::make(), ['record' => $post])
    @mason($post->content, \App\Mason\BrickCollection::make(), ['record' => $post])
  5. Upgrade Mason database schema

    3.x

    Mason 1.x uses a different JSON schema for stored bricks. The identifier key is replaced by id, and the values key is replaced by config.

    Old Schema (0.x):

    {
        "type":"masonBrick",
        "attrs":{
            "identifier": "newsletter_signup",
            "values": { ... }
        }
    }

    New Schema (1.x):

    {
        "type": "masonBrick",
        "attrs": {
            "id": "newsletter_signup",
            "config": { ... }
        }
    }
  6. Core components of Mason

    3.x

    Mason is composed of four primary abstractions:

    • Form field (Mason): Provides the interactive editor interface, including a sidebar of available bricks for drag-and-drop assembly.
    • Infolist entry (MasonEntry): A read-only component used to display stored Mason content within Filament Infolists.
    • Bricks: Custom classes that extend Brick. Each brick defines its own configuration form and its Blade view for rendering. You can scaffold these using the make:mason-brick command.
    • Renderer (MasonRenderer or mason() helper): The engine that converts the stored JSON data into HTML for display on your front end.
  7. Create a custom layout for MasonEntry

    3.x

    If you are defining a custom layout for a MasonEntry, ensure your Blade template includes the @masonEntryStyles directive in the <head> and uses the mason::iframe-entry-content partial to render the blocks.

    Example layout structure:

    {{-- resources/views/layouts/mason-entry.blade.php --}}
    
    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
    
            <title>{{ config('app.name') }}</title>
    
            @vite(['resources/css/app.css', 'resources/js/app.js'])
    
            @masonEntryStyles
        </head>
        <body>
            <main>
                @include('mason::iframe-entry-content', ['blocks' => $blocks])
            </main>
        </body>
    </html>
  8. Add the Mason editor to a Filament form

    3.x

    To use Mason in a Filament form, use the Mason::make() method. The argument passed to make() should be the attribute name on your model, which must be cast to array or json in your model definition.

    You must define which bricks are available for the author to insert using the bricks() method. If you omit bricks(), Mason defaults to using its built-in Section brick.

    use Awcodes\
    Mason\Bricks\Section;
    use Awcodes\Mason\Mason;
    
    Mason::make('content')
        ->bricks([
            Section::class,
        ]);
  9. Mason 1.x System Requirements and Dependencies

    3.x

    Upgrading to Mason 1.x introduces the following requirements and dependency changes:

    • PHP: 8.2+
    • Filament: v4 (Follow the Filament v4 Upgrade Guide first)
    • ueberdosis/tiptap-php: Upgraded to v2
    • Removed: Awcodes\Mason\Support\Helpers, Awcodes\Mason\Support\Converter, and the mason.renderer Livewire component.
  10. Configure Mason CSS and Theming

    3.x

    Mason requires a custom Filament theme. After setting up a custom theme in Filament, you must import the Mason plugin CSS and include its Blade templates in your theme's CSS file:

    @import '../../../../vendor/awcodes/mason/resources/css/plugin.css';
    
    @source '../../../../vendor/awcodes/mason/resources/**/*.blade.php';
  11. Create reusable brick collections

    3.x

    To prevent drift between your Mason field and MasonEntry configuration, define your brick list in a single static method. This ensures that the field (used in the admin), the entry (used for data structure), and the renderer (used on the frontend) all use the exact same set of bricks.

    Create a class with a static method (e.g., make()) that returns an array of brick class names.

    class BrickCollection
    {
        public static function make(): array
        {
            return [
                NewsletterSignup::class,
                Section::class,
                Cards::class,
                SupportCenter::class,
            ];
        }
    }
    
    // Use the collection in both the field and the entry
    Mason::make('content')->bricks(BrickCollection::make());
    MasonEntry::make('content')->bricks(BrickCollection::make());
  12. Customize Mason editor and entry height

    3.x

    You can adjust the default height of the Mason field or the MasonEntry component using Filament's extraInputAttributes() method. This is useful if the default height is insufficient for your layout.

    Mason::make('content')
        ->extraInputAttributes(['style' => 'min-height: 30rem;'])
        ->bricks([...])
    
    MasonEntry::make('content')
        ->extraInputAttributes(['style' => 'min-height: 40rem;'])
        ->bricks([...])