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)