Replacing Filament's RichEditor hyperlink modal

Overview

When using Filament's RichEditor with the link toolbar button enabled, the action modal's form that collects the hyperlink details includes a checkbox for "Open in new tab".

This article outlines how to create a RichContentPlugin implementation (extension for Filament's RichEditor) that behaves similarly to the default link functionality, but without the aforementioned checkbox.

We'll accomplish this by

  1. Implementing a RichEditor Filament plugin
  2. Configuring our Filament form's RichEditor to use the plugin

Use case

For when you'd prefer to not show the "Open in new tab" checkbox in the RichEditor link modal and default all created links to always open in a new tab.

Assumptions

You have a working Laravel project with:

  • Filament 4 installed and configured
  • a form schema using Filament's RichEditor

RichEditor Filament plugin

Filament's RichEditor was built with extensibility in mind. Anything you can do with TipTap, you can likely find a way to do within the RichEditor.

Creating a plugin for Filament's RichEditor is done by creating a class that implements the RichContentPlugin interface. For a more detailed breakdown of what each method in this interface is responsible for, see Filament's documentation.

Implementing the plugin

Create the following class in a location that makes sense for your application's structure. This article uses app/Filament/Forms/RichEditorPlugins/LinkNewTabPlugin.php.

📁
app/Filament/Forms/RichEditorPlugins/LinkNewTabPlugin.php
1<?php
2 
3namespace App\Support\Filament\Plugins;
4 
5use Filament\Forms\Components\RichEditor;
6use Filament\Forms\Components\RichEditor\Actions\LinkAction;
7use Filament\Forms\Components\RichEditor\Plugins\Contracts\RichContentPlugin;
8use Filament\Forms\Components\RichEditor\RichEditorTool;
9use Filament\Schemas\Schema;
10use Filament\Support\Icons\Heroicon;
11 
12class LinkNewTabPlugin implements RichContentPlugin
13{
14 public static function make(): static
15 {
16 return app(static::class);
17 }
18 
19 public function getTipTapPhpExtensions(): array
20 {
21 return []; // PHP extension for links is already included by Filament's extensions
22 }
23 
24 public function getTipTapJsExtensions(): array
25 {
26 return []; // JavaScript for links is already included by Filament's scripts
27 }
28 
29 public function getEditorTools(): array
30 {
31 return [
32 RichEditorTool::make('linkNewTab')
33 ->label(__('filament-forms::components.rich_editor.tools.link'))
34 ->action(arguments: '{ url: $getEditor().getAttributes(\'link\')?.href }')
35 ->icon(Heroicon::Link)
36 ->iconAlias('forms:components.rich-editor.toolbar.link'),
37 ];
38 }
39 
40 public function getEditorActions(): array
41 {
42 $linkAction = LinkAction::make();
43 
44 $schema = $linkAction->getSchema(Schema::make());
45 $components = $schema->getComponents();
46 $componentsWithoutShouldOpenInNewTabCheckbox = collect($components)
47 ->reject(fn ($component) => $component->getName() === 'shouldOpenInNewTab')
48 ->all();
49 
50 $linkActionFunction = $linkAction->getActionFunction();
51 
52 return [
53 $linkAction
54 ->name('linkNewTab')
55 ->schema($componentsWithoutShouldOpenInNewTabCheckbox)
56 ->action(function (array $arguments, array $data, RichEditor $component) use ($linkActionFunction) {
57 $data['shouldOpenInNewTab'] = true;
58 $linkActionFunction($arguments, $data, $component);
59 }),
60 ];
61 }
62}

The implementation above is based mostly off of Filament's preexisting implementation for the link tool. You can find that implementation within this class.

Some changes made to Filament's original implementation include:

  • in getEditorTools, we are returning a single instantiated RichEditorTool as a sole array element
    • the tool instance is named linkNewTab, which we will reference when including the toolbar button in a RichEditor instance.
    • the tool instance also uses an action based off of Filament's implementation, but without shouldOpenInNewTab in the arguments object (because our action's form won't use it).
  • in getEditorActions, we are returning an instance of Filament's LinkAction action, but overriding the name, schema, and action configurations
    • we use linkNewTab as the action name
    • we filter the components in the action's schema to exclude the shouldOpenInNewTab checkbox component
    • in the action function, we explicitly set the 'shouldOpenInNewTab' key within the $data array to true and then pass the data along to the original LinkAction's action Closure

Using the plugin

Now we can use our plugin anywhere we configure a RichEditor (e.g. within a Filament Schema configuration).

Within the chain of methods on your RichEditor instance and within the array passed to the plugins method, include an instance of the LinkNewTabPlugin class.

Then in the same method chain, within the array passed to the toolbarButtons method, place an element 'linkNewTab' wherever you'd like the link button icon to appear in the editor's toolbar.

Don't forget to remove the 'link' entry in the array passed to the toolbarButtons method. Otherwise, a button for the original link tool provided by Filament will be included in addition to your new plugin's button.
1use App\Filament\Forms\RichEditorPlugins\LinkNewTabPlugin;
2 
3// within your schema configuration
4$schema
5 ->components([
6 // ... other schema components
7 RichEditor::make('content')
8 ->plugins([
9 LinkNewTabPlugin::make(),
10 ])
11 ->toolbarButtons([
12 // ... other toolbar buttons
13 'linkNewTab',
14 ]),
15 ]);

Conclusion

By following the above steps, you can replace the link tool button provided out-of-the-box by Filament's RichEditor with a custom plugin that slightly modifies the user's experience. The modal to specify details about the relevant hyperlink will exclude the checkbox which denotes if the hyperlink should open a new tab. Instead, new hyperlinks created with our plugin's interface will always open in a new tab.

🤖
Did you spot a mistake in this article? Have a suggestion for how something can be improved? Even if you'd just like to comment or chat about something else, I'd love to hear from you! Contact me.

Syntax highlighting by Torchlight.dev

End of article