DEV Community

mugichaba
mugichaba

Posted on

Wysiwyg editor with Nuxt.js

I implemented wysiwyg editor with a library named tiptap.

Image description

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


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

When you call the component on any other component, you should pass a value like this.



<tiptap value="something">


Enter fullscreen mode Exit fullscreen mode

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)