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

183 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Хорошо! Давайте создадим плагин для **Tiptap** во **Vue 3 (Composition API)**, который добавляет **колонтитул (footer)** как отдельный узел (`Node`), сохраняемый и загружаемый вместе с документом.
---
## **1. Создаём плагин для колонтитула (`FooterNode`)**
Сначала опишем сам узел (`Node`), который будет представлять колонтитул.
### **`FooterExtension.ts`**
```typescript
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`**
```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. Дополнительные улучшения**
### **Добавление атрибутов (например, даты в колонтитул)**
Можно расширить плагин, чтобы он поддерживал атрибуты:
```typescript
// В FooterExtension.ts
addAttributes() {
return {
date: {
default: new Date().toLocaleDateString(),
renderHTML: (attrs) => ({ 'data-date': attrs.date }),
},
};
},
```
Теперь при рендеринге будет:
```html
<footer data-date="2024-04-20">Текст колонтитула</footer>
```
### **Валидация структуры документа**
Если колонтитул должен быть **только в конце документа**, можно добавить проверку в `addCommands`:
```typescript
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).
- **Готовые команды** для управления колонтитулами.