awcodes / scribble
A Rich Text Editor plugin for Filament Forms.
Fund package maintenance!
awcodes
Installs: 2 283
Dependents: 0
Suggesters: 0
Security: 0
Stars: 47
Watchers: 5
Forks: 8
Open Issues: 3
Requires
- php: ^8.1
- filament/forms: ^3.0
- league/html-to-markdown: ^5.1
- spatie/laravel-package-tools: ^1.15.0
- ueberdosis/tiptap-php: ^1.3
Requires (Dev)
- laravel/pint: ^1.0
- nunomaduro/collision: ^7.9|^8.1
- orchestra/testbench: ^8.0|^9.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-arch: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
- spatie/laravel-ray: ^1.26
- dev-main
- v0.8.1-alpha
- v0.8.0-alpha
- v0.7.0-alpha
- v0.6.4-alpha
- v0.6.3-alpha
- v0.6.2-alpha
- v0.6.1-alpha
- v0.6.0-alpha
- v0.5.0-alpha
- v0.4.1-alpha
- v0.4.0-alpha
- v0.3.11-alpha
- v0.3.10-alpha
- v0.3.9-alpha
- v0.3.8-alpha
- v0.3.7-alpha
- v0.3.6-alpha
- v0.3.5-alpha
- v0.3.4-alpha
- v0.3.3-alpha
- v0.3.2-alpha
- v0.3.1-alpha
- v0.3.0-alpha.1
- v0.2.0-alpha.4
- v0.2.0-alpha.3
- v0.2.0-alpha.2
- v0.2.0-alpha.1
- v0.1.0-alpha.3
- v0.1.0-alpha.2
- v0.1.0-alpha.1
This package is auto-updated.
Last update: 2024-10-14 13:41:38 UTC
README
A Rich Text Editor plugin for Filament Forms.
Installation
Install the package via composer
composer require awcodes/scribble
Publishing the config
php artisan vendor:publish --tag="scribble-config"
Publishing the translations
php artisan vendor:publish --tag="scribble-translations"
Setting up the plugin's styling
Important
If you have not set up a custom theme and are using Filament Panels follow the instructions in the Filament Docs first. The following applies to both the Panels Package and the standalone Forms package.
Import the plugin's stylesheet (if not already included) into your theme's css file.
@import '/vendor/awcodes/scribble/resources/css/editor.css'; @import '/vendor/awcodes/scribble/resources/css/entry.css';
Add the plugin's views to your tailwind.config.js
file.
content: [ './vendor/awcodes/scribble/resources/**/*{.blade.php,.svelte}', ]
Install the dependency.
npm install @simonwep/pickr
Rebuild your custom theme.
npm run build
Preparing your model
Scribble stores its content as JSON data in a single column on your model. So, it is vital that you cast the column to an array or json object in your model.
protected $casts = [ 'content' => 'array', // or 'json' ];
It is also recommended to make the column alongText
type in your migration. However, this is not required and if you know you will not need a large amount of data you can use a text
or mediumText
type as well. Just be aware that the content can grow rather quickly.
$table->longText('content')->nullable();
Usage
Form Component
use Awcodes\Scribble\ScribbleEditor; public function form(Form $form): Form { return $form ->schema([ ScribbleEditor::make('content') ]) }
Infolist Entry
use Awcodes\Scribble\ScribbleEntry; public function infolist(Infolist $infolist): Infolist { return $infolist ->schema([ ScribbleEntry::make('content') ]); }
Global Configuration
In the boot
method of a ServiceProvider you can set the default configuration for all instances of the editor with the configureUsing
method.
use Awcodes\Scribble\ScribbleEditor; use Awcodes\Scribble\Profiles\MinimalProfile; ScribbleEditor::configureUsing(function (ScribbleEditor $scribble) { $scribble ->renderToolbar() ->profile(MinimalProfile::class); });
Editor Profiles
Manually, creating menu configurations for each instance of the editor can be cumbersome. To alleviate this, you can create a profile class that defines the tools for the bubble, suggestion, and toolbar menus. You can then apply the profile to the editor using the profile
method. You may use either the tool identifier or the tool's class name.
namespace App\ScribbleProfiles; use Awcodes\Scribble\ScribbleProfile;use Awcodes\Scribble\Tools; class Minimal extends ScribbleProfile { public static function bubbleTools(): array { return [ Tools\Paragraph::class, Tools\Bold::class, Tools\Italic::class, Tools\Link::class, Tools\BulletList::class, Tools\OrderedList::class, ]; } public static function suggestionTools(): array { return []; } public static function toolbarTools(): array { return [ 'paragraph', 'bold', 'italic', 'link', 'bullet-list', 'ordered-list', ]; } }
use App\ScribbleProfiles\Minimal; Scribble::configureUsing('content') ->profile(Mimimal::class)
Generating a Profile
You can scaffold out a new profile class using the make:scribble-profile
command and following the prompts.
php artisan make:scribble-profile
Custom Editor Styles
Should you need to provide styles to the editor for custom blocks or tools, you can use the customStyles
method to provide a path to a CSS file.
Scribble::make('content') ->customStyles('path/to/custom.css')
Extending the Editor
Custom Tools
Commands
Command tools are used to insert content into the editor using Tiptap commands. The Bold
and Italic
tools are examples of this. This is also the default tool type.
use Awcodes\Scribble\ScribbleTool; class Bold extends ScribbleTool { protected function setUp(): void { $this ->icon('scribble-bold') ->label('Bold') ->extension('bold') ->active(extension: 'bold') ->commands([ $this->makeCommand(command: 'toggleBold'), // or ['command' => 'toggleBold', 'arguments' => null], ]); } }
Static Blocks
Static Blocks are a tool type that can be used to insert a static blade view into the editor. These are useful for inserting placeholder content that can be rendered out to a different view in your HTML. For instance, a block that represents a list of FAQs that when rendered on the front-end will display a list of FAQs from the database.
$editorView
is optional but can be useful in the case that you need to provide a custom editor view for the block. And a different rendering view for the output.
use Awcodes\Scribble\ScribbleTool; use Awcodes\Scribble\Enums\ToolType; class FaqsList extends ScribbleTool { protected function setUp(): void { $this ->icon('heroicon-o-question-mark-circle') ->label('FAQs List') ->type(ToolType::StaticBlock) ->editorView('scribble-tools.faqs-list-editor') ->renderedView('scribble-tools.faqs-list'); } }
{{-- scribble.static-block-editor --}} <div class="p-4 bg-gray-800 rounded-lg"> <p>This is a placeholder. FAQ list will be rendered on output.</p> </div> {{-- scribble.static-block --}} <div class="p-4 bg-gray-800 rounded-lg"> @foreach ($faqs as $faq) <div class="mb-4"> <h3 class="text-lg font-bold">{{ $faq->question }}</h3> <p>{{ $faq->answer }}</p> </div> @endforeach </div>
Blocks
Blocks are a tool type that interact with the editor's content through a modal form and a blade view. They can be used to insert custom content into the editor.
$editorView
is optional but can be useful in the case that you need to provide a custom editor view for the block. And a different rendering view for the output.
Tool class
use Awcodes\Scribble\ScribbleTool; use Awcodes\Scribble\Enums\Alignment; use Awcodes\Scribble\Enums\SlideDirection; use Awcodes\Scribble\Enums\ToolType; use Filament\Support\Enums\MaxWidth; class Notice extends ScribbleTool { protected function setUp(): void { $this ->icon('heroicon-o-exclamation-triangle') ->label('Notice') ->type(ToolType::Block) ->optionsModal(NoticeForm::class) ->renderedView('scribble-tools.notice'); } }
Modal Form
use Awcodes\Scribble\Livewire\ScribbleModal; use Awcodes\Scribble\Profiles\MinimalProfile; use Awcodes\Scribble\ScribbleEditor; use Filament\Forms\Components\Radio; class NoticeForm extends ScribbleModal { public ?string $header = 'Notice'; // this should match the identifier in the tool class public ?string $identifier = 'notice'; public function mount(): void { $this->form->fill([ 'color' => $this->data['color'] ?? 'info', 'body' => $this->data['body'] ?? null, ]); } public function getFormFields(): array { return [ Radio::make('color') ->inline() ->inlineLabel(false) ->options([ 'info' => 'Info', 'success' => 'Success', 'warning' => 'Warning', 'danger' => 'Danger', ]), ScribbleEditor::make('body') ->profile(MinimalProfile::class) ->columnSpanFull(), ]; } }
Blade View
<div @class([ 'border-l-4 p-4 flex items-center gap-3 not-prose', match($color) { 'success' => 'bg-success-200 text-success-900 border-success-600', 'danger' => 'bg-danger-200 text-danger-900 border-danger-600', 'warning' => 'bg-warning-200 text-warning-900 border-warning-600', default => 'bg-info-200 text-info-900 border-info-600', } ]) > @php $icon = match($color) { 'success' => 'heroicon-o-check-circle', 'danger' => 'heroicon-o-exclamation-circle', 'warning' => 'heroicon-o-exclamation-triangle', default => 'heroicon-o-information-circle', }; @endphp @svg($icon, 'h-6 w-6') {!! scribble($body)->toHtml() !!} </div>
Modals
Modals are a tool type that interact with the editor's content through a modal form and use Tiptap commands to insert content into the editor. The Media
and Grid
tools are examples of this.
Tool class
use Awcodes\Scribble\ScribbleTool; use Awcodes\Scribble\Enums\ToolType; use App\Path\To\MediaForm; class Media extends ScribbleTool { protected function setUp(): void { $this ->icon('heroicon-o-photograph') ->label('Media') ->type(ToolType::Modal) ->commands([ $this->makeCommand(command: 'setMedia'), ]) ->optionsModal(MediaForm::class); } }
Modal Form
use Awcodes\Scribble\Livewire\ScribbleModal; use Awcodes\Scribble\Profiles\MinimalProfile; use Awcodes\Scribble\ScribbleEditor; use Filament\Forms\Components\Radio; class MediaForm extends ScribbleModal { public ?string $header = 'Media'; // this should match the identifier in the tool class public ?string $identifier = 'media'; public function mount(): void { $this->form->fill([ // ]); } public function getFormFields(): array { return [ // ]; } }
Events
You may also create tools that emit events when they are clicked. This can be useful for triggering actions in your application when a tool is clicked.
Tool class
use Awcodes\Scribble\Enums\ToolType; use Awcodes\Scribble\ScribbleTool; class OpenRandomModal extends ScribbleTool { protected function setUp(): void { $this ->icon('scribble-open') ->label('Open Random Modal') ->type(ToolType::Event) ->commands([ $this->makeCommand(command: 'setDataFromEvent'), ]) ->event( name: 'open-modal', data: [ 'id' => 'random-modal', 'title' => 'Random Modal', ], ); } }
Generating a Tool
You can scaffold out a new tool class using the make:scribble-tool
command and following the prompts.
php artisan make:scribble-tool
Custom Tiptap Extensions
You can also provide custom Tiptap extensions or other Tiptap native extensions to the editor. This can be useful for adding custom marks, nodes, or other extensions to the editor.
Javascript
import {Highlight} from "@tiptap/extension-highlight"; import MyCustomExtension from "./MyCustomExtension"; window.scribbleExtensions = { highlight: Highlight, myCustomExtension: MyCustomExtension, };
Next you will need to load your js file in your layout or view before Filament's scripts. This can be done in a way you see fit for you application.
For example, with a Filament Panel you could do something like the following:
use Filament\View\PanelsRenderHook; public function panel(Panel $panel): Panel { return $panel ->renderHook( name: PanelsRenderHook::STYLES_AFTER, hook: fn (): string => Blade::render('@vite("resources/js/scribble/extensions.js")') ); }
PHP Parser
In order for the content to be able to be converted to HTML, you will need to provide a PHP parser for the extension. See the Tiptap PHP package for more information on how to create a parser for a Tiptap extension or using an included one in their package.
Tool
Next you will need a make a tool for the extension.
use Awcodes\Scribble\ScribbleTool; use Tiptap\Marks\Highlight as TiptapHighlight; class Highlight extends ScribbleTool { protected function setUp(): void { $this ->icon('icon-highlight') ->label('Highlight') ->commands([ $this->makeCommand(command: 'toggleHighlight'), ]) ->converterExtensions(new TiptapHighlight()); } }
Now you can register the tool and PHP parser with the plugin in a ServiceProvider's register
method.
use Awcodes\Scribble\ScribbleManager; use App\ScribbleTools\Highlight; use Tiptap\Marks\Highlight as TiptapHighlight; public function register(): void { app(ScribbleManager::class) ->registerTools([ Highlight::make(), ]); }
Converting output
With the Converter Utility class
use Awcodes\Scribble\Utils\Converter; Converter::from($content)->toHtml(); Converter::from($content)->toJson(); Converter::from($content)->toText(); Converter::from($content)->toMarkdown(); Converter::from($content)->toTOC(); // Table of Contents
With the helper function
{!! scribble($content)->toHtml() !!} {!! scribble($content)->toJson() !!} {!! scribble($content)->toText() !!} {!! scribble($content)->toMarkdown() !!} {!! scribble($content)->toTOC() !!}
Table of Contents
use Awcodes\Scribble\Utils\Converter; // HTML output with headings linked and wrapped in anchor tags Converter::from($content) ->toHtml( toc: true, maxDepth: 3, wrapHeadings: true ); // Structured list of heading links Converter::from($content)->toTOC();
MergeTags Replacement
If you are using Merge tags and outputting the content as HTML you can use the mergeTagsMap
method to replace the merge tags with the appropriate values.
{!! scribble($content)->mergeTagsMap([ 'brand_phone' => '1-800-555-1234', 'brand_email' => 'test@example.com', ])->toHtml() !!}
Faker Utility
use Awcodes\Scribble\Utils\Faker; Faker::make() ->heading(int | string | null $level = 2) ->emptyParagraph() ->paragraphs(int $count = 1, bool $withRandomLinks = false) ->unorderedList(int $count = 1) ->orderedList(int $count = 1) ->image(?int $width = 640, ?int $height = 480) ->link() ->details(bool $open = false) ->code(?string $className = null) ->blockquote() ->hr() ->br() ->grid(array $cols = [1, 1, 1]) ->toJson();
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.