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)