Skip to content

Instantly share code, notes, and snippets.

@Gulivertx
Created July 16, 2024 16:27
Show Gist options
  • Save Gulivertx/0b9a594441396e0394f0879901127c94 to your computer and use it in GitHub Desktop.
Save Gulivertx/0b9a594441396e0394f0879901127c94 to your computer and use it in GitHub Desktop.
Quill.JS integration with Symfony project
{# File path in Symfony project : templates/form/layout_types.html.twig
Don't forget to add this file in symfony twig config inside section form_themes
#}
{%- block quill_textarea_row -%}
{%- set row_attr = row_attr|merge({ class: row_attr.class|default(row_class|default('flex-1')) }) -%}
{%- set widget_attr = {} -%}
{%- if help is not empty -%}
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
{%- endif -%}
<div
class="relative flex-1"
{% with {attr: row_attr} %}{{ block('attributes') }}{% endwith %}
{{ stimulus_controller('forms--quill-textarea-type') }}
>
{{- form_label(form) -}}
{{- form_widget(form, widget_attr) -}}
<div {{ stimulus_target('forms--quill-textarea-type', 'editor') }}></div>
{{- form_errors(form) -}}
{{- form_help(form) -}}
</div>
{% endblock %}
<?php
// File path in Symfony project : src/Form/NotesType.php
namespace App\Form;
use App\Form\Type\QuillTextareaType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class NotesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('personalNote', QuillTextareaType::class, [
'label' => false,
'required' => false,
])
;
}
...
}
// File path in Symfony project : assets/controllers/forms/quill-textarea-type_controller.ts
import {Controller} from '@hotwired/stimulus';
import Quill, {QuillOptions} from 'quill';
export default class extends Controller<HTMLDivElement> {
private editor: Quill;
static targets = [ "editor", "input" ]
declare readonly editorTarget: HTMLDivElement
declare readonly inputTarget: HTMLInputElement
connect() {
this.editor = new Quill(this.editorTarget, this.quillOption());
let value = this.inputTarget.value;
console.debug(value);
this.editor.setContents(this.editor.clipboard.convert({
html: value
}));
this.editor.on('text-change', this.onQuilTextChange);
}
disconnect() {
this.editor.off('text-change', this.onQuilTextChange);
}
private onQuilTextChange = () => {
this.inputTarget.value = this.editor.root.innerHTML;
}
private quillOption = (): QuillOptions => {
return {
modules: {
toolbar: [
[{ 'header': 1 }, { 'header': 2 }],
['bold', 'italic', 'underline', 'strike'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'color': [] }, { 'background': [] }],
['link'],
['clean']
]
},
placeholder: 'Ajouter des notes personelles ici. Ces notes ne sont visible que par vous.',
readOnly: false,
theme: 'snow',
}
}
}
<?php
// File path in Symfony project : src/Form/Type/QuillTextareaType.php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class QuillTextareaType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'attr' => [
'class' => 'input-hidden-accessible',
'data-forms--quill-textarea-type-target' => 'input',
],
]);
}
public function getParent(): string
{
return TextareaType::class;
}
public function getBlockPrefix(): string
{
return 'quill_textarea';
}
}
@choult
Copy link

choult commented Aug 10, 2025

Thanks for sharing, very helpful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment