Front-End μž‘μ—…μ‹€/Vue.js

[Vue.js] tiptap Editor 연동

μ£Όλ‹ˆμ“°πŸ§‘‍πŸ’» 2022. 3. 23. 08:00
728x90
λ°˜μ‘ν˜•

πŸš€ tiptap Editor

이 μ—λ””ν„°λ‘œ μž‘μ„±λ˜λŠ” λ‚΄μš©μ€ Form의 textarea에 μ¨μ§€λŠ” 것이 μ•„λ‹ˆκ³  div에 HTML둜 써지고 미리 μ •μ˜λ˜μ–΄ μžˆλŠ” CSS Style둜 ν˜•νƒœλ₯Ό λ°”λ‘œλ°”λ‘œ λ³΄μ—¬μ£ΌλŠ” 식인것 κ°™μ•„ λ³΄μ΄λŠ” κ²ƒμ΄μ—μš”. λ Œλ”λ¦¬μŠ€λΌλŠ”κ²Œ 이런 방식을 λ§ν•˜λŠ”κ²ƒμ΄ μ•„λ‹Œκ°€ 생각이 λ“œλŠ” κ²ƒμ΄μ—μš”.
Tiptapμ—μ„œλŠ” 기본적으둜 CSSμŠ€νƒ€μΌμ΄ μ •μ˜ λ˜μ–΄ μžˆμ§€ μ•Šλ‹€κ³  ν•©λ‹ˆλ‹€. 본인에 맞게 섀정을 ν•΄μ•Ό ν•˜λŠ” κ²ƒμ΄μ—μš”.
λ‹€λ§Œ μƒ˜ν”Œλ‘œ μ œκ³΅ν•˜κ³  μžˆλŠ” cssλ₯Ό κ°€μ Έλ‹€ μ‚¬μš©ν•  μˆ˜λŠ” μžˆμ„ 것 같은 κ²ƒμ΄μ—μš”.

 

    πŸ”½  μ„€μΉ˜

● npm 이용 μ„€μΉ˜

npm install @tiptap/vue-3 @tiptap/starter-kit

 

● yarn 이용 μ„€μΉ˜

yarn add @tiptap/vue-3 @tiptap/starter-kit

 

➜  junyharang_dev_commnunity_client git:(master) βœ— npm uninstall @tiptap/vue-3
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: junyharang_dev_commnunity_client@0.1.0
npm ERR! Found: vue@3.2.31
npm ERR! node_modules/vue
npm ERR!   vue@"^3.2.13" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer vue@"^2.5.17" from tiptap@1.32.2
npm ERR! node_modules/tiptap
npm ERR!   tiptap@"^1.32.2" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /Users/juny/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/juny/.npm/_logs/2022-03-22T21_59_03_868Z-debug-0.log
➜  junyharang_dev_commnunity_client git:(master) βœ— npm uninstall @tiptap/vue-3 --force
npm WARN using --force Recommended protections disabled.
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: tiptap@1.32.2
npm WARN Found: vue@3.2.31
npm WARN node_modules/vue
npm WARN   peerOptional vue@"^2 || ^3.2.13" from @vue/babel-preset-app@5.0.1
npm WARN   node_modules/@vue/babel-preset-app
npm WARN     @vue/babel-preset-app@"^5.0.1" from @vue/cli-plugin-babel@5.0.1
npm WARN     node_modules/@vue/cli-plugin-babel
npm WARN   5 more (@vue/server-renderer, vue-cookie-next, vue-router, vuex, the root project)
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peer vue@"^2.5.17" from tiptap@1.32.2
npm WARN node_modules/tiptap
npm WARN   tiptap@"^1.32.2" from the root project
npm WARN   1 more (tiptap-extensions)
npm WARN 
npm WARN Conflicting peer dependency: vue@2.6.14
npm WARN node_modules/vue
npm WARN   peer vue@"^2.5.17" from tiptap@1.32.2
npm WARN   node_modules/tiptap
npm WARN     tiptap@"^1.32.2" from the root project
npm WARN     1 more (tiptap-extensions)
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: tiptap-extensions@1.35.2
npm WARN Found: vue@3.2.31
npm WARN node_modules/vue
npm WARN   peerOptional vue@"^2 || ^3.2.13" from @vue/babel-preset-app@5.0.1
npm WARN   node_modules/@vue/babel-preset-app
npm WARN     @vue/babel-preset-app@"^5.0.1" from @vue/cli-plugin-babel@5.0.1
npm WARN     node_modules/@vue/cli-plugin-babel
npm WARN   5 more (@vue/server-renderer, vue-cookie-next, vue-router, vuex, the root project)
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peer vue@"^2.5.17" from tiptap-extensions@1.35.2
npm WARN node_modules/tiptap-extensions
npm WARN   tiptap-extensions@"^1.35.2" from the root project
npm WARN 
npm WARN Conflicting peer dependency: vue@2.6.14
npm WARN node_modules/vue
npm WARN   peer vue@"^2.5.17" from tiptap-extensions@1.35.2
npm WARN   node_modules/tiptap-extensions
npm WARN     tiptap-extensions@"^1.35.2" from the root project

added 113 packages, removed 54 packages, changed 23 packages, and audited 988 packages in 7s

104 packages are looking for funding
  run `npm fund` for details

9 vulnerabilities (8 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
➜  junyharang_dev_commnunity_client git:(master) βœ— npm install @tiptap/vue-3 @tiptap/starter-kit

added 36 packages, and audited 1024 packages in 17s

128 packages are looking for funding
  run `npm fund` for details

9 vulnerabilities (8 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force


➜  junyharang_dev_commnunity_client git:(master) βœ—

 

μ£Όλ‹ˆν•˜λž‘μ€ npm을 μ΄μš©ν•΄μ„œ μ„€μΉ˜λ₯Ό ν•΄ μ€€ κ²ƒμ΄μ—μš”.

 

   πŸ”½  μ‚¬μš© 방법

<template>
  <editor-content :editor="editor" />
</template>

<script>
import { Editor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

export default {
  components: {
    EditorContent,
  },

  data() {
    return {
      editor: null,
    }
  },

  mounted() {
    this.editor = new Editor({
      content: '<p>I’m running Tiptap with Vue.js. πŸŽ‰</p>',
      extensions: [
        StarterKit,
      ],
    })
  },

  beforeUnmount() {
    this.editor.destroy()
  },
}
</script>

 

기본적인 CodeλŠ” μœ„μ™€ 같이 μ΄μš©ν•  수 μžˆλŠ”λ°, component/Tiptap.vue와 같은 Componentλ₯Ό λ§Œλ“€μ–΄μ„œ μœ„μ˜ 예제 μ½”λ“œλ₯Ό μž…λ ₯ν•΄μ„œ μ‚¬μš©ν•  수 μžˆλŠ” κ²ƒμ΄μ—μš”.

 

또 λ‹€λ₯Έ 방법은 Compositon APIλ₯Ό μ΄μš©ν•˜μ—¬ `useEditor`λ₯Ό μ΄μš©ν•  수 μžˆλŠ” κ²ƒμ΄μ—μš”.

<template>
  <editor-content :editor="editor" />
</template>

<script>
import { useEditor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

export default {
  components: {
    EditorContent,
  },

  setup() {
    const editor = useEditor({
      content: '<p>I’m running Tiptap with Vue.js. πŸŽ‰</p>',
      extensions: [
        StarterKit,
      ],
    })

    return { editor }
  },
}
</script>

 

μ£Όλ‹ˆν•˜λž‘μ€ μ•„λž˜μ™€ 같이 μ΄μš©ν•  κ²ƒμ΄μ—μš”. 이에 λ§žμΆ”μ–΄μ„œ λ‚΄μš©μ„ μž‘μ„±ν•΄ λ³Ό κ²ƒμ΄μ—μš”.

<template>
  <div class="editor" v-if="editor">
    <menu-bar class="editor__header" :editor="editor" />
    <!-- 색상선택 -->
    <input
      type="color"
      @input="
        editor
          .chain()
          .focus()
          .setColor($event.target.value)
          .run()
      "
      :value="editor.getAttributes('textStyle').color"
    />
    <!-- 폰트 크기 -->
    <floating-menu
      class="floating-menu"
      :tippy-options="{ duration: 100 }"
      :editor="editor"
      v-if="editor" 
    >
      <button
        @click="
          editor
            .chain()
            .focus()
            .toggleHeading({ level: 1 })
            .run()
        "
        :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
      >
        H1
      </button>
      <button
        @click="
          editor
            .chain()
            .focus()
            .toggleHeading({ level: 2 })
            .run()
        "
        :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
      >
        H2
      </button>
      <button
        @click="
          editor
            .chain()
            .focus()
            .toggleBulletList()
            .run()
        "
        :class="{ 'is-active': editor.isActive('bulletList') }"
      >
        Bullet List
      </button>
    </floating-menu>
    <!-- 폰트 선택 -->

    <editor-content class="editor__content" :editor="editor" />
  </div>
</template>

<script>
import { Editor, EditorContent, FloatingMenu } from "@tiptap/vue-3";
import StarterKit from "@tiptap/starter-kit";
import TaskList from "@tiptap/extension-task-list";
import TaskItem from "@tiptap/extension-task-item";
import Highlight from "@tiptap/extension-highlight";
import CharacterCount from "@tiptap/extension-character-count";
import TextStyle from "@tiptap/extension-text-style";
import MenuBar from "@/components/tiptap/MenuBar.vue";

import { Color } from "@tiptap/extension-color";
export default {
  components: {
    EditorContent,
    FloatingMenu,
    MenuBar
  },

  data() {
    return {
      editor: null
    };
  },
  mounted() {
    this.editor = new Editor({
      extensions: [
        StarterKit,
        Highlight,
        TaskList,
        TaskItem,
        CharacterCount.configure({
          limit: 10000
        }),
        TextStyle,
        Color
      ],
      content: `
        <h2>
          Hi there,
        </h2>
        <p>
          this is a <em>basic</em> example of <strong>tiptap</strong>. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists:
        </p>
        <p>
          Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block:
        </p>

        <p>
          I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too.
        </p>
        <blockquote>
          Wow, that’s amazing. Good work, boy! πŸ‘
          <br />
          — Mom
        </blockquote>
      `
    });
  },

  methods: {
    beforeUnmount() {
      this.editor.destroy();
    }
  }
};
</script>

 

아직 Extention Module을 μ„€μΉ˜ν•˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— Errorκ°€ 있게 λ©λ‹ˆλ‹€.

 

πŸš€ ν™•μž₯ Module μ„€μΉ˜ν•˜κΈ°

Prosemirror Editor의 κ°•μ μ΄μž tiptap의 강점은 ν•„μš”ν•œ κΈ°λŠ₯만 λΆˆλŸ¬μ™€ μ΄μš©ν•  수 μžˆλ‹€λŠ” κ²ƒμ΄μ—μš”.
ν™•μž₯ κΈ°λŠ₯을 μ΄μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” ν™•μž₯ Module을 μΆ”κ°€λ‘œ μ„€μΉ˜ν•΄μ•Ό ν•˜λŠ” κ²ƒμ΄μ—μš”.

 

    πŸ”½  μ„€μΉ˜

● npm 이용 μ„€μΉ˜

npm install tiptap-extensions --save



● yarn 이용 μ„€μΉ˜



μ΄λ²ˆμ—λ„ μ£Όλ‹ˆν•˜λž‘μ€ yarn을 μ΄μš©ν•΄μ„œ μ„€μΉ˜ ν•΄ λ³Ό κ²ƒμ΄μ—μš”.

 

 

    πŸ”½  μ‚¬μš© 방법

졜초 μ„€μΉ˜ν•œ tiptap에 μΆ”κ°€λ‘œ extensionsλ₯Ό μ μš©ν•˜λ©΄ λ˜λŠ” κ²ƒμ΄μ—μš”.

 

import { Editor, EditorContent } from 'tiptap' 
import { Heading } from 'tiptap-extensions' 

const editor = new Editor({ 
	extensions: [ new Heading(), ], 
})

 

`extensions: [ new Heading() ]`처럼 ν•„μš”ν•œ κΈ°λŠ₯의 객체(μΈμŠ€ν„΄μŠ€)λ₯Ό μΆ”κ°€ν•΄μ„œ λΆˆλŸ¬μ™€μ„œ μ΄μš©ν•˜λ©΄ λ˜λŠ” κ²ƒμ΄μ—μš”.

λ˜ν•œ 각 κΈ°λŠ₯의 μΈμŠ€ν„΄μŠ€λŠ” μžμ‹ λ§Œμ˜ 섀정을 μΆ”κ°€ / λ³€κ²½ν•  수 μžˆλŠ” κ²ƒμ΄μ—μš”. κ·Έλž˜μ„œ 뢈러 올 κΈ°λŠ₯의 섀정값도 잘 μ‚΄νŽ΄λ΄μ•Ό Errorλ₯Ό μ΅œμ†Œν™” ν•  수 μžˆλŠ” κ²ƒμ΄μ—μš”. `Heaing()`의 경우 μ•„λž˜μ™€ 같이 `level` Option`도 μžˆλŠ” κ²ƒμ΄μ—μš”.

 

const editor = new Editor({
	new Heading({
    	leve: [1, 2, 3],
    }),
})

 

`tiptap-extensions`μ—λŠ” λ‹€μ–‘ν•œ κΈ°λŠ₯듀이 μ€€λΉ„ λ˜μ–΄ μžˆλŠ” κ²ƒμ΄μ—μš”.

import { Editor, EditorContent } from 'tiptap'
import { Bold, Italic, Link, HardBreak, Heading } from 'tiptap-extensions'

const editor = new Editor({
	extensions: [
    	new Bold(),
        new Italic(),
        new Link(),
        new HardBread(),
        new Haading()
    ],
})

 

 

    πŸ”½  ν™”λ©΄

        πŸ“¦ DevInquryReplyVO.java

μ•ˆλ…•ν•˜μ„Έμš”.

 

 

 

 

 

 

μ£Όλ‹ˆν•˜λž‘μ΄ μž‘μ„±ν•œ λ‚΄μš©μ΄ 도움이 λ˜μ…¨μœΌλ©΄ μ’‹κ² μŠ΅λ‹ˆλ‹€!
ν˜Ήμ‹œλΌλ„ 잘 λͺ»λœ 정보가 μžˆλ‹€λ©΄ λŒ“κΈ€μ— μ†Œμ€‘ν•œ ν”Όλ“œλ°± 뢀탁 λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€!

'μ£Όλ‹ˆν•˜λž‘'의 Blog 글은 λ”°λ‘œ λ³΄κ΄€ν•˜μ‹œμ§€ λ§ˆμ‹œκ³ , 'μ£Όλ‹ˆν•˜λž‘' Blogμ—μ„œ μ—¬λŸ¬λΆ„μ„ 항상 기닀리고 μžˆμœΌλ‹ˆ 이 κ³³μ—μ„œ 이용 ν•΄ μ£Όμ‹œλ©΄ κ³ λ§™κ² μŠ΅λ‹ˆλ‹€.

일뢀 정보λ₯Ό νΌκ°€μ…”μ„œ 정리λ₯Ό ν•˜κ³ μž ν•˜μ‹€ λ•ŒλŠ” λ°˜λ“œμ‹œ μ•„λž˜μ™€ 같이 좜처λ₯Ό 남겨 μ£Όμ‹œκΈ° λ°”λΌκ² μŠ΅λ‹ˆλ‹€.

좜처 : μ£Όλ‹ˆν•˜λž‘ 개발 Blog - '[Blog URL]'

β“’ 2022. 'μ£Όλ‹ˆν•˜λž‘' all rights reserved.
junyharang8592@gmail.com

 

 

 

 

728x90
λ°˜μ‘ν˜•