I implemented wysiwyg editor with a library named tiptap.
Although I am almost a beginner in Nuxt.js, it was really easy to create the component.
FYI, the sample code uses buefy
though I skip about installing and the detail of it.
Add tiptap to a project
The official website shows how to add it.
In my case, using yarn.
$ yarn add @tiptap/vue-2 @tiptap/starter-kit
Implement a wysiwyg component
Create Tiptap.vue
and write the following code as a component.
<template>
<div class="editor">
<div v-if="editor" class="menu">
<b-tooltip label="bold" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('bold') }"
icon-left="format-bold"
type="is-text"
size="small"
@click="editor.chain().focus().toggleBold().run()"
/>
</b-tooltip>
<b-tooltip label="italic" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('italic') }"
icon-left="format-italic"
type="is-text"
size="small"
@click="editor.chain().focus().toggleItalic().run()"
/>
</b-tooltip>
<b-tooltip label="strike" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('strike') }"
icon-left="format-strikethrough"
type="is-text"
size="small"
@click="editor.chain().focus().toggleStrike().run()"
/>
</b-tooltip>
<b-tooltip label="code" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('code') }"
icon-left="code-tags"
type="is-text"
size="small"
@click="editor.chain().focus().toggleCode().run()"
/>
</b-tooltip>
<b-tooltip label="paragraph" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('paragraph') }"
icon-left="format-paragraph"
type="is-text"
size="small"
@click="editor.chain().focus().setParagraph().run()"
/>
</b-tooltip>
<b-tooltip label="header1" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
icon-left="format-header-1"
type="is-text"
size="small"
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
/>
</b-tooltip>
<b-tooltip label="header2" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
icon-left="format-header-2"
type="is-text"
size="small"
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
/>
</b-tooltip>
<b-tooltip label="header3" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
icon-left="format-header-3"
type="is-text"
size="small"
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
/>
</b-tooltip>
<b-tooltip label="bulleted list" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('bulletList') }"
icon-left="format-list-bulleted"
type="is-text"
size="small"
@click="editor.chain().focus().toggleBulletList().run()"
/>
</b-tooltip>
<b-tooltip label="ordered list" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('orderedList') }"
icon-left="format-list-numbered"
type="is-text"
size="small"
@click="editor.chain().focus().toggleOrderedList().run()"
/>
</b-tooltip>
<b-tooltip label="undo" type="is-dark">
<b-button
icon-left="undo"
type="is-text"
size="small"
@click="editor.chain().focus().undo().run()"
/>
</b-tooltip>
<b-tooltip label="redo" type="is-dark">
<b-button
icon-left="redo"
type="is-text"
size="small"
@click="editor.chain().focus().redo().run()"
/>
</b-tooltip>
</div>
<div class="input-field">
<editor-content :editor="editor" />
</div>
</div>
</template>
<script>
import { Editor, EditorContent } from '@tiptap/vue-2';
import StarterKit from '@tiptap/starter-kit';
export default {
components: {
EditorContent,
},
props: {
value: {
type: String,
default: '',
},
},
data() {
return {
editor: null,
};
},
watch: {
value(value) {
// HTML
const isSame = this.editor.getHTML() === value;
// JSON
// const isSame = JSON.stringify(this.editor.getJSON()) === JSON.stringify(value)
if (isSame) {
return;
}
this.editor.commands.setContent(value, false);
},
},
mounted() {
this.editor = new Editor({
content: this.value,
extensions: [StarterKit],
onUpdate: () => {
// HTML
this.$emit('input', this.editor.getHTML());
// JSON
// this.$emit('input', this.editor.getJSON())
},
});
},
beforeDestroy() {
this.editor.destroy();
},
};
</script>
<style lang="scss" scoped>
.editor {
border: 1px solid lightgray;
border-radius: 5px;
margin-top: 10px;
margin-bottom: 10px;
}
.menu {
border-bottom: 1px solid lightgray;
padding: 10px;
}
.input-field {
padding: 10px;
}
</style>
The block below represents one of the menu button.
I use Material icons
so you can find the icons like format-bold
on the website.
<b-tooltip label="bold" type="is-dark">
<b-button
:class="{ 'is-active': editor.isActive('bold') }"
icon-left="format-bold"
type="is-text"
size="small"
@click="editor.chain().focus().toggleBold().run()"
/>
</b-tooltip>
When you call the component on any other component, you should pass a value like this.
<tiptap value="something">
something
must belong to like data()
.
And when you modify the text on the wysiwyg editor, something
property is going to be changed.
Top comments (0)