Files
SergObsidian/WORK & PROJECTS/Mol/Code Chunks/TipTap Plugin.md
2025-04-20 01:56:43 +05:00

5.7 KiB
Raw Blame History

Хорошо! Давайте создадим плагин для Tiptap во Vue 3 (Composition API), который добавляет колонтитул (footer) как отдельный узел (Node), сохраняемый и загружаемый вместе с документом.


1. Создаём плагин для колонтитула (FooterNode)

Сначала опишем сам узел (Node), который будет представлять колонтитул.

FooterExtension.ts

import { Node } from '@tiptap/core';

export const FooterExtension = Node.create({
  name: 'footer', // Уникальное имя узла
  group: 'block', // Группа (block, inline и т. д.)
  content: 'inline*', // Разрешённый контент внутри (текст, ссылки и т. п.)

  // Парсинг HTML → ProseMirror-узел
  parseHTML() {
    return [
      {
        tag: 'footer', // Будет парсить `<footer>` в документе
      },
    ];
  },

  // Как узел будет отрендерен в HTML
  renderHTML({ HTMLAttributes }) {
    return ['footer', HTMLAttributes, 0]; // <footer>...</footer>
  },

  // Добавляем команды для работы с колонтитулом
  addCommands() {
    return {
      setFooter:
        (content) =>
        ({ commands }) => {
          return commands.insertContent({
            type: this.name,
            content: [
              {
                type: 'text',
                text: content || 'Текст колонтитула',
              },
            ],
          });
        },
    };
  },
});

2. Подключаем плагин в редактор (Vue 3 Composition API)

Теперь создадим компонент редактора с использованием Vue 3 + Tiptap.

TiptapEditor.vue

<script setup lang="ts">
import { useEditor, EditorContent } from '@tiptap/vue-3';
import StarterKit from '@tiptap/starter-kit';
import { FooterExtension } from './FooterExtension';

const editor = useEditor({
  extensions: [
    StarterKit,
    FooterExtension, // Подключаем наш плагин
  ],
  content: `
    <p>Основной контент документа...</p>
    <footer>Колонтитул (подвал)</footer>
  `,
});

// Функция для добавления колонтитула
const insertFooter = () => {
  editor.value?.commands.setFooter('Новый текст колонтитула');
};

// Сохранение документа (например, в JSON)
const saveContent = () => {
  const json = editor.value?.getJSON();
  console.log(json);
  // Можно отправить на сервер или сохранить в localStorage
};
</script>

<template>
  <div class="editor">
    <div v-if="editor" class="menu-bar">
      <button @click="insertFooter">Добавить колонтитул</button>
      <button @click="saveContent">Сохранить</button>
    </div>
    <EditorContent :editor="editor" />
  </div>
</template>

<style>
.editor {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}
.menu-bar {
  margin-bottom: 10px;
}
footer {
  margin-top: 2rem;
  padding: 1rem;
  background: #f5f5f5;
  border-top: 1px solid #ddd;
}
</style>

3. Как это работает?

  • FooterExtension определяет новый тип узла <footer>, который:
    • Может содержать текст (inline*).
    • Сохраняется в JSON при вызове editor.getJSON().
    • Загружается обратно при установке editor.content.
  • Команда setFooter позволяет программно вставлять колонтитул.
  • Сохранение и загрузка работают автоматически благодаря Tiptap.

4. Дополнительные улучшения

Добавление атрибутов (например, даты в колонтитул)

Можно расширить плагин, чтобы он поддерживал атрибуты:

// В FooterExtension.ts
addAttributes() {
  return {
    date: {
      default: new Date().toLocaleDateString(),
      renderHTML: (attrs) => ({ 'data-date': attrs.date }),
    },
  };
},

Теперь при рендеринге будет:

<footer data-date="2024-04-20">Текст колонтитула</footer>

Валидация структуры документа

Если колонтитул должен быть только в конце документа, можно добавить проверку в addCommands:

setFooter:
  (content) =>
  ({ commands, state }) => {
    const { doc } = state;
    const lastNode = doc.lastChild;

    // Если последний элемент уже footer — заменяем его
    if (lastNode?.type.name === 'footer') {
      return commands.insertContentAt(
        doc.content.size - 1,
        {
          type: this.name,
          content: [{ type: 'text', text: content }],
        }
      );
    }

    // Иначе добавляем в конец
    return commands.insertContentAt(
      doc.content.size,
      {
        type: this.name,
        content: [{ type: 'text', text: content }],
      }
    );
  },

Итог

  • Кастомный узел <footer>, который сохраняется и загружается с документом.
  • Интеграция с Vue 3 (Composition API).
  • Готовые команды для управления колонтитулами.