I use CkEditor 5, it has great documentation and lots of features if you want.
This is my js:
Hooks.LoadEditor = {
mounted() {
ClassicEditor
.create(document.querySelector('#editor'), {
toolbar: { items: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'insertTable', 'undo', 'redo'] }, // 'imageUpload', 'mediaEmbed', 'insertTable', 'undo', 'redo')
language: 'nl',
table: { contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells'] },
link: {
defaultProtocol: 'http://',
addTargetToExternalLinks: true,
decorators: {
addLiveviewAttrs: {
mode: 'automatic',
callback: url => url.startsWith('/'),
attributes: {
"data-phx-link": 'redirect',
"data-phx-link-state": 'push'
}
}
},
}
})
.then(editor => {
console.log(editor);
editor.editing.view.change(writer => {
writer.setAttribute('spellcheck', 'false', editor.editing.view.document.getRoot());
});
// Add a click event listener to the editor to prevent the click event from opening the links with addLiveviewAttrs decorator
editor.editing.view.document.on('click', (evt, data) => {
data.stopPropagation();
});
// We need to add the text back to the textarea so changes can be tracked by LiveView,
// otherwise when we change anything in the rest of the form, the textarea will be reset.
editor.model.document.on('change:data', () => {
document.querySelector('#editor').value = editor.getData();
},
);
// Deactivate the stickyPanel, we set it manually in the css
editor.ui.view.stickyPanel.unbind('isActive');
editor.ui.view.stickyPanel.isActive = false;
})
.catch(err => {
console.error(err.stack);
});
},
// This function runs when the element's parent LiveView has reconnected to the server
reconnected() { },
updated() {
this.mounted();
}
};
And this is the form field, calling the Hook:
<.field
type="textarea"
field={@form[:text]}
label="Text field"
id="editor"
phx-hook="LoadEditor"
/>