[Vue.js] tiptap Editor ์—ฐ๋™

2022. 3. 23. 08:00ใ†Front-End ์ž‘์—…์‹ค/Vue.js

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
๋ฐ˜์‘ํ˜•

'Front-End ์ž‘์—…์‹ค > Vue.js' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Vue.js] Vue-Cookies  (0) 2022.04.02
[Vue.js] Template refs  (0) 2022.03.19
[Vue.js] Props  (0) 2022.03.19
[Vue.js] axios ์„ค์น˜  (0) 2022.03.18
[Vue.js] vue-router.esm-bundler.js?ec2d:72 [Vue Router warn]: No match found for location with path "/support/devInquryList"  (0) 2022.03.15