삼분공부/Vue

[Nuxt] nuxt.js 에서 텍스트 에디터 추가하기 - @tiptap

케이쓰리 2024. 12. 30. 15:04

갑자기 다시 넉스트를 하고 있는 

시키는 거 걍 다 해야 하는 노예 등장. 

 

넉스트에 이미지 추가도 가능한 텍스트 에디터가 필요했다. 

마치 이 티스토리 글쓰기 페이지 같은.... 이런 것 만드는 것이다. 

 

공식 문서는 아래.

환경에 맞게 필요한 패키지들 설치하면 된다. 

https://tiptap.dev/docs/editor/getting-started/overview

 

 

 

나는 특별한건 필요 없고, 글씨 정렬이나 볼드체, 글씨 크기, 순번 리스트.. 이 정도만 구현했다.

<template>
  <div>
    <!-- 툴바 -->
    <div class="toolbar" v-if="editor">
      <!-- Bold -->
      <button @click="editor.chain().focus().toggleBold().run()" :class="{ active: editor.isActive('bold') }">
        <i class="mdi mdi-format-bold"></i>
      </button>
      <!-- Italic -->
      <button @click="editor.chain().focus().toggleItalic().run()" :class="{ active: editor.isActive('italic') }">
        <i class="mdi mdi-format-italic"></i>
      </button>
      <!-- Underline -->
      <button @click="editor.chain().focus().toggleUnderline().run()" :class="{ active: editor.isActive('underline') }">
        <i class="mdi mdi-format-underline"></i>
      </button>
      <!-- Clear Marks (마크 제거) 버튼 -->
      <!-- Clear Marks는 텍스트에 적용된 스타일, 즉 마크를 초기화하는 기능입니다. "마크"는 굵게(bold), 기울임(italic), 밑줄(underline) 등과 같은 텍스트 스타일을 의미합니다. 이 기능을 사용하면 선택된 텍스트나 전체 텍스트에서 모든 스타일을 제거할 수 있습니다.-->
      <button @click="editor.chain().focus().unsetAllMarks().run()" title="Clear Marks">
        <i class="mdi mdi-eraser"></i>
      </button>
      <!-- Clear Nodes -->
      <!-- Clear Nodes는 노드를 초기화하는 기능입니다. TipTap에서 노드는 텍스트 블록, 헤딩, 리스트, 이미지 등과 같은 구조적인 요소를 말합니다. 예를 들어, 텍스트가 **헤딩(h1, h2, h3)**으로 설정되어 있을 때, 이 노드를 초기화하면 해당 텍스트는 기본적인 단락(paragraph)으로 되돌아갑니다.-->
      <button @click="editor.chain().focus().clearNodes().run()" title="Clear Nodes">
        <i class="mdi mdi-delete-forever"></i>
      </button>
      <!-- Paragraph -->
      <button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }" title="Paragraph">
        <i class="mdi mdi-format-paragraph"></i>
      </button>
      <!-- Heading 1 -->
      <button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }" title="H1">
        <i class="mdi mdi-format-header-1"></i>
      </button>
      <!-- Heading 2 -->
      <button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }" title="H2">
        <i class="mdi mdi-format-header-2"></i>
      </button>
      <!-- Heading 3 -->
      <button @click="editor.chain().focus().toggleHeading({ level: 3 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }" title="H3">
        <i class="mdi mdi-format-header-3"></i>
      </button>
      <!-- Text Alignment -->
      <button @click="editor.chain().focus().setTextAlign('left').run()">
        <i class="mdi mdi-format-align-left"></i>
      </button>
      <button @click="editor.chain().focus().setTextAlign('center').run()">
        <i class="mdi mdi-format-align-center"></i>
      </button>
      <button @click="editor.chain().focus().setTextAlign('right').run()">
        <i class="mdi mdi-format-align-right"></i>
      </button>
      <!-- Bullet List -->
      <button @click="editor.chain().focus().toggleBulletList().run()" :class="{ active: editor.isActive('bulletList') }">
        <i class="mdi mdi-format-list-bulleted"></i>
      </button>
      <!-- Ordered List -->
      <button @click="editor.chain().focus().toggleOrderedList().run()" :class="{ active: editor.isActive('orderedList') }">
        <i class="mdi mdi-format-list-numbered"></i>
      </button>
      <!-- Insert Image Button -->
      <button @click="addImage" >
        <i class="mdi mdi-image"></i>
      </button>
      <!-- Hidden File Input -->
      <input
          type="file"
          accept="image/*"
          ref="imageInput"
          style="display: none;"
          @change="handleImageUpload"

      />
      <!-- Undo -->
      <button @click="editor.chain().focus().undo().run()">
        <i class="mdi mdi-undo"></i>
      </button>
      <!-- Redo -->
      <button @click="editor.chain().focus().redo().run()">
        <i class="mdi mdi-redo"></i>
      </button>
      <button class="color-picker">
        <input
            type="color"
            @input="changeTextColor($event.target.value)"
            :value="editor.getAttributes('textStyle').color || '#000000'"
        />
      </button>
    </div>

    <!-- 에디터 본문 -->
    <EditorContent v-if="editor" :editor="editor" class="editor-content" />

    <!-- 문자 수 표시 -->
    <div class="character-count">
      {{ characterCountMessage }}
    </div>

  </div>
</template>

 

<i class="mdi mdi-format-bold"></i>

 

위 클래스 같은 경우는 사용하려면 

npm install @mdi/font

 

mid/font 를 설치해줘야 함!

 

 

 

 

지금 회사에 퍼블리셔도 없어서... 대충 챗지피티한테 이쁘게 만들어달라 함.

내가 사용한 코드는 아래. 

<style scoped>
.toolbar {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
  position: relative;
  align-items: center;
}

.editor-content {
  min-height: 100%;
  font-size: 16px;
  line-height: 1.5; 
  outline: none; 
  white-space: pre-wrap; 
}

button {
  padding: 8px 12px;
  border: none;
  border-radius: 4px;
  background-color: #f5f5f5;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  transition: background-color 0.2s;
}

button:hover {
  background-color: #e6e6e6;
}

button i {
  font-size: 20px;
}

.character-count {
  margin-top: 8px;
  font-size: 14px;
  color: #666;
}

.upload-btn i {
  font-size: 18px;
}


</style>

 

 

 

 

 

 

이러면 짠!

 

위와 같은 간단한 에디터가 생성된다. 

이미지 업로드는 따로 구현해야 함!