Yesterday, I spoke about Editor.js. Today, I purpose an implementation for Symfony with EasyAdmin for a properties of type json
on a Doctrine entity.
If you think to an improvement, don't hesitate to comment!
First, create new Field (it's specific for EasyAdmin):
# src/admin/Field/Editorjs.php
<?php
namespace App\Admin\Field;
use App\Form\Type\EditorjsType;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;
class Editorjs implements FieldInterface
{
use FieldTrait;
public static function new(string $propertyName, ?string $label = null): self
{
return (new self())
->setProperty($propertyName)
->setLabel($label)
->setFormType(EditorjsType::class)
// required also the easyadmin entry in webpack.config.js
;
}
}
and the form type EditorJsType mentioned:
# src/Form/Type/EditorjsType.php
<?php
declare(strict_types=1);
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class EditorjsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->addModelTransformer(
new CallbackTransformer(
function ($value): string {
// transform the array to a json string
return json_encode($value);
},
function ($value): array {
// transform the json string to a php array
return json_decode($value, true);
}
)
);
}
public function getParent(): string
{
return TextType::class;
}
}
In order to have specific js and css load in EasyAdmin context, I create a specific entry in webpack.config.js:
// webpack.config.js
...
Encore
...
.addEntry('easyadmin', './assets/easyadmin.js')
...
;
Then, create the easyadmin.js file:
import './styles/easyadmin.css';
import EditorJS from '@editorjs/editorjs';
import Header from '@editorjs/header';
import Quote from '@editorjs/quote';
import RawTool from '@editorjs/raw';
import SimpleImage from "@editorjs/simple-image";
import EditorjsList from '@editorjs/list';
import Embed from '@editorjs/embed';
import Paragraph from '@editorjs/paragraph';
import Table from '@editorjs/table';
import CodeTool from '@editorjs/code';
import Underline from '@editorjs/underline';
import Delimiter from '@editorjs/delimiter';
import InlineCode from '@editorjs/inline-code';
const wrapper = document.getElementById('editorjs');
const input = document.getElementById(wrapper.dataset.fieldId);
const editor = new EditorJS({
holder: wrapper.id,
tools: {
header: {
class: Header,
shortcut: 'CMD+SHIFT+H',
config: {
levels: [2, 3, 4, 5, 6],
defaultLevel: 3
}
},
quote: {
class: Quote,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+O',
},
raw: RawTool,
image: SimpleImage,
list: {
class: EditorjsList,
inlineToolbar: true,
config: {
defaultStyle: 'unordered'
},
},
embed: Embed,
paragraph: {
class: Paragraph,
inlineToolbar: true,
},
table: Table,
code: CodeTool,
underline: Underline,
delimiter: Delimiter,
inlineCode: {
class: InlineCode,
shortcut: 'CMD+SHIFT+M',
},
},
onReady: () => {
editor.render(JSON.parse(input.value));
},
onChange: (api, event) => {
editor.save().then((outputData) => {
input.value = JSON.stringify(outputData);
});
}
});
and the easyadmin.css file:
/* Editor.js customization */
.editorjs {
background-color: var(--form-control-bg);
background-repeat: no-repeat;
border: 1px solid var(--form-input-border-color);
border-radius: var(--bs-border-radius);
box-shadow: var(--form-input-shadow);
color: var(--form-input-text-color);
margin: 1rem 0;
padding: 0.5rem 1rem 0.5rem 3rem;
transition: box-shadow .08s ease-in, color .08s ease-in;
white-space: nowrap;
word-break:keep-all;
.codex-editor__redactor {
padding-bottom: 2rem!important;
}
.ce-inline-toolbar {
.ce-popover__container, .ce-popover__items {
overflow-y: hidden!important;
}
}
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
/* Editor.js customization */
.editorjs {
.codex-editor__redactor {
.ce-block__content {
background-color: transparent;
}
}
.ce-toolbar {
.ce-toolbar__plus, .ce-toolbar__settings-btn {
color: var(--text-color)!important;
&:hover {
color: #1d202b!important;
}
}
}
}
}
Run this npm install command:
npm install @editorjs/editorjs @editorjs/header @editorjs/quote @editorjs/raw @editorjs/simple-image @editorjs/list @editorjs/embed @editorjs/paragraph @editorjs/table @editorjs/code @editorjs/underline @editorjs/delimiter @editorjs/inline-code
Configure EasyAdmin for use the new Webpack entry and a custom form template, in Dashboard Controller, add this:
# src/Controller/Admin/Dashboard.php
...
public function configureCrud(): Crud
{
return Crud::new()
->setFormThemes(['form/custom_types.html.twig', '@EasyAdmin/crud/form_theme.html.twig'])
;
}
public function configureAssets(): Assets
{
return Assets::new()
->addWebpackEncoreEntry('easyadmin')
;
}
...
Create the custom form template file:
{# templates/form/custom_types.html.twig #}
{% block editorjs_row %}
<div class="{{ ea_crud_form.ea_field.columns }}">
{{ form_label(form) }}
<div id="editorjs" class="editorjs" data-field-id="{{ id }}"></div>
<input type="hidden" name="{{ full_name }}" id="{{ id }}" value="{{ value }}" />
{{ form_errors(form) }}
</div>
<div class="flex-fill"></div>
{% endblock %}
Now you can use new Field type in a crud controller :
...
use App\Admin\Field\Editorjs;
...
public function configureFields(string $pageName): iterable
{
return [
...
Editorjs::new('content') // content is a json (array) properties of a doctrine entity
->hideOnIndex(),
...
];
}
...
}
Enjoy!
Top comments (0)