vault backup: 2025-05-12 17:12:01
This commit is contained in:
27
.obsidian/workspace.json
vendored
27
.obsidian/workspace.json
vendored
@@ -14,12 +14,12 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"state": {
|
"state": {
|
||||||
"file": "WORK & PROJECTS/Mol/Серверы/mail.mol-soft.ru.md",
|
"file": "WORK & PROJECTS/Mol/Серверы/Jira - Service - Confluence - Crm.md",
|
||||||
"mode": "source",
|
"mode": "source",
|
||||||
"source": false
|
"source": false
|
||||||
},
|
},
|
||||||
"icon": "lucide-file",
|
"icon": "lucide-file",
|
||||||
"title": "mail.mol-soft.ru"
|
"title": "Jira - Service - Confluence - Crm"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -196,16 +196,23 @@
|
|||||||
},
|
},
|
||||||
"active": "343836aed0394d03",
|
"active": "343836aed0394d03",
|
||||||
"lastOpenFiles": [
|
"lastOpenFiles": [
|
||||||
"WORK & PROJECTS/Mol/Серверы/Jira - Service - Confluence - Crm.md",
|
|
||||||
"WORK & PROJECTS/Mol/Серверы/Схема инфраструктуры.canvas",
|
|
||||||
"WORK & PROJECTS/Mol/Серверы/1С Бухгалтерия.md",
|
"WORK & PROJECTS/Mol/Серверы/1С Бухгалтерия.md",
|
||||||
"WORK & PROJECTS/Mol/Серверы/Alfa prod.md",
|
"WORK & PROJECTS/Mol/Серверы/Jira - Service - Confluence - Crm.md",
|
||||||
|
"WORK & PROJECTS/Mol/Ideas/Все идеи для Моли.md",
|
||||||
|
"WORK & PROJECTS/Mol/Code Chunks/Tiptap resizeTableColumnWidth.md",
|
||||||
|
"WORK & PROJECTS/Mol/Code Chunks/TipTap Plugin.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/Alfa cloud prod_canvas2doc-data/newdoc-node_72b797b472986e84_fromCanvas.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/Alfa cloud prod_canvas2doc-data/newdoc-node_16f08ef6b7358e0b_fromCanvas.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/Alfa cloud prod_canvas2doc-data/newdoc-node_7bb34343b485f669_fromCanvas.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/Alfa cloud prod_canvas2doc-data/newdoc-node_1e42b8aa516bc15d_fromCanvas.md",
|
||||||
"WORK & PROJECTS/Mol/Серверы/git.moldev.ru.md",
|
"WORK & PROJECTS/Mol/Серверы/git.moldev.ru.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/Alfa prod.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/Схема инфраструктуры.canvas",
|
||||||
"WORK & PROJECTS/Mol/Серверы/00_Список серверов.md",
|
"WORK & PROJECTS/Mol/Серверы/00_Список серверов.md",
|
||||||
"WORK & PROJECTS/Mol/Серверы/VPN-FIREWALL-GATE (Cerberus).md",
|
"WORK & PROJECTS/Mol/Серверы/VPN-FIREWALL-GATE (Cerberus).md",
|
||||||
"WORK & PROJECTS/Mol/Серверы/mail.mol-soft.ru.md",
|
|
||||||
"WORK & PROJECTS/Mol/Серверы/mol-desk.mol-soft.PRODs.md",
|
|
||||||
"WORK & PROJECTS/Mol/Серверы/moldev.ru.md",
|
"WORK & PROJECTS/Mol/Серверы/moldev.ru.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/mol-desk.mol-soft.PRODs.md",
|
||||||
|
"WORK & PROJECTS/Mol/Серверы/mail.mol-soft.ru.md",
|
||||||
"WORK & PROJECTS/Mol/Планы и диаграммы/Разработка - ЭПИКИ.md",
|
"WORK & PROJECTS/Mol/Планы и диаграммы/Разработка - ЭПИКИ.md",
|
||||||
"WORK & PROJECTS/Mol/Планы и диаграммы/Alfa Cloud/Alfa cloud prod.canvas",
|
"WORK & PROJECTS/Mol/Планы и диаграммы/Alfa Cloud/Alfa cloud prod.canvas",
|
||||||
"WORK & PROJECTS/Mol/Планы и диаграммы/Alfa Cloud/Alfa cloud readme.md",
|
"WORK & PROJECTS/Mol/Планы и диаграммы/Alfa Cloud/Alfa cloud readme.md",
|
||||||
@@ -220,18 +227,12 @@
|
|||||||
"SKILLS DOCS/JAPANESE частицы.md",
|
"SKILLS DOCS/JAPANESE частицы.md",
|
||||||
"WORK & PROJECTS/Mol/Планы и диаграммы/Заказы",
|
"WORK & PROJECTS/Mol/Планы и диаграммы/Заказы",
|
||||||
"WORK & PROJECTS/Mol/Планы и диаграммы/План СИЛА.md",
|
"WORK & PROJECTS/Mol/Планы и диаграммы/План СИЛА.md",
|
||||||
"WORK & PROJECTS/Mol/Ideas/Все идеи для Моли.md",
|
|
||||||
"WORK & PROJECTS/Mol/Планы и диаграммы/MOL _ План разработки.md",
|
"WORK & PROJECTS/Mol/Планы и диаграммы/MOL _ План разработки.md",
|
||||||
"WORK & PROJECTS/Mol/Code Chunks/Tiptap resizeTableColumnWidth.md",
|
|
||||||
"PERSONAL PROJECTS/P2EP/cdRead.canvas",
|
"PERSONAL PROJECTS/P2EP/cdRead.canvas",
|
||||||
"conflict-files-obsidian-git.md",
|
"conflict-files-obsidian-git.md",
|
||||||
"My/Diet 2.md",
|
"My/Diet 2.md",
|
||||||
"PERSONAL PROJECTS/P2EP/pseudoCode/MakeSmallChar.md",
|
"PERSONAL PROJECTS/P2EP/pseudoCode/MakeSmallChar.md",
|
||||||
"PERSONAL PROJECTS/P2EP/pseudoCode/FUN_80015674 Update Entity Stats.md",
|
"PERSONAL PROJECTS/P2EP/pseudoCode/FUN_80015674 Update Entity Stats.md",
|
||||||
"PERSONAL PROJECTS/P2EP/pseudoCode/FUN_800453e0 SystemEventManager.md",
|
|
||||||
"PERSONAL PROJECTS/P2EP/pseudoCode/FUN_80012df4 RenderInterface.md",
|
|
||||||
"PERSONAL PROJECTS/P2EP/pseudoCode/FUN_80011f2c InitInterfaceManager.md",
|
|
||||||
"PERSONAL PROJECTS/PS1 DOCS/PSX code - Texture show from file.md",
|
|
||||||
"WORK & PROJECTS/Mol/Code Chunks",
|
"WORK & PROJECTS/Mol/Code Chunks",
|
||||||
"PERSONAL PROJECTS/PS1 DOCS",
|
"PERSONAL PROJECTS/PS1 DOCS",
|
||||||
"PERSONAL PROJECTS/P2EP/pseudoCode",
|
"PERSONAL PROJECTS/P2EP/pseudoCode",
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
Хорошо! Давайте создадим плагин для **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).
|
|
||||||
- **Готовые команды** для управления колонтитулами.
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
|
|
||||||
# Creating a Tiptap Plugin for Resizable Table Columns in Vue 3
|
|
||||||
|
|
||||||
To create a Tiptap plugin that allows users to resize table columns and persists the width information in the node's JSON, you'll need to:
|
|
||||||
|
|
||||||
1. Extend the Table extension
|
|
||||||
2. Add resize handles
|
|
||||||
3. Handle mouse events for resizing
|
|
||||||
4. Store column widths in node attributes
|
|
||||||
|
|
||||||
Here's a comprehensive solution:
|
|
||||||
|
|
||||||
## 1. First, install required dependencies (if not already installed)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @tiptap/vue-3 @tiptap/pm @tiptap/starter-kit @tiptap/extension-table @tiptap/extension-table-row @tiptap/extension-table-header @tiptap/extension-table-cell
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. Create a custom Table extension with resizable columns
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// ResizableTableExtension.js
|
|
||||||
import { Table } from '@tiptap/extension-table'
|
|
||||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
|
||||||
|
|
||||||
export const ResizableTableExtension = Table.extend({
|
|
||||||
addAttributes() {
|
|
||||||
return {
|
|
||||||
...this.parent?.(),
|
|
||||||
colWidths: {
|
|
||||||
default: null,
|
|
||||||
parseHTML: element => element.getAttribute('data-col-widths'),
|
|
||||||
renderHTML: attributes => {
|
|
||||||
if (!attributes.colWidths) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
'data-col-widths': attributes.colWidths.join(',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
addProseMirrorPlugins() {
|
|
||||||
const parentPlugins = this.parent?.() || []
|
|
||||||
|
|
||||||
return [
|
|
||||||
...parentPlugins,
|
|
||||||
resizableColumnsPlugin({
|
|
||||||
handleWidth: 5,
|
|
||||||
cellMinWidth: 25,
|
|
||||||
onColumnResize: (colIndex, width, colWidths, node) => {
|
|
||||||
const transaction = this.editor.state.tr.setNodeMarkup(
|
|
||||||
this.editor.state.selection.$anchor.posAtIndex(0),
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
...node.attrs,
|
|
||||||
colWidths: colWidths
|
|
||||||
}
|
|
||||||
)
|
|
||||||
this.editor.view.dispatch(transaction)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function resizableColumnsPlugin(options) {
|
|
||||||
const pluginKey = new PluginKey('resizableColumns')
|
|
||||||
let resizing = false
|
|
||||||
let startX, startWidth, colIndex, table, colWidths
|
|
||||||
|
|
||||||
return new Plugin({
|
|
||||||
key: pluginKey,
|
|
||||||
props: {
|
|
||||||
handleDOMEvents: {
|
|
||||||
mousedown: (view, event) => {
|
|
||||||
const target = event.target
|
|
||||||
if (target.classList.contains('column-resize-handle')) {
|
|
||||||
event.preventDefault()
|
|
||||||
startResizing(view, target, event.clientX)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mousemove: (view, event) => {
|
|
||||||
if (!resizing) return
|
|
||||||
const tableRect = table.getBoundingClientRect()
|
|
||||||
const totalWidth = tableRect.width
|
|
||||||
const newWidth = startWidth + (event.clientX - startX)
|
|
||||||
const percentageWidth = (newWidth / totalWidth) * 100
|
|
||||||
|
|
||||||
// Update the column widths array
|
|
||||||
const newColWidths = [...colWidths]
|
|
||||||
newColWidths[colIndex] = Math.max(options.cellMinWidth, percentageWidth)
|
|
||||||
|
|
||||||
// Apply the new widths to the table columns
|
|
||||||
applyColumnWidths(table, newColWidths)
|
|
||||||
|
|
||||||
if (options.onColumnResize) {
|
|
||||||
const node = view.state.doc.nodeAt(view.state.selection.$anchor.posAtIndex(0))
|
|
||||||
options.onColumnResize(colIndex, percentageWidth, newColWidths, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
mouseup: () => {
|
|
||||||
if (resizing) {
|
|
||||||
resizing = false
|
|
||||||
document.body.style.cursor = ''
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
view: (view) => {
|
|
||||||
return {
|
|
||||||
update: (view) => {
|
|
||||||
// Add resize handles to columns
|
|
||||||
const tables = view.dom.querySelectorAll('table')
|
|
||||||
tables.forEach(table => {
|
|
||||||
const rows = table.querySelectorAll('tr')
|
|
||||||
if (rows.length === 0) return
|
|
||||||
|
|
||||||
// Remove existing handles
|
|
||||||
const existingHandles = table.querySelectorAll('.column-resize-handle')
|
|
||||||
existingHandles.forEach(handle => handle.remove())
|
|
||||||
|
|
||||||
// Get column widths from node attributes or calculate equal distribution
|
|
||||||
const node = view.state.doc.nodeAt(view.state.selection.$anchor.posAtIndex(0))
|
|
||||||
const attrs = node?.attrs || {}
|
|
||||||
colWidths = attrs.colWidths
|
|
||||||
? attrs.colWidths.split(',').map(Number)
|
|
||||||
: Array(rows[0].cells.length).fill(100 / rows[0].cells.length)
|
|
||||||
|
|
||||||
// Apply initial column widths
|
|
||||||
applyColumnWidths(table, colWidths)
|
|
||||||
|
|
||||||
// Add resize handles
|
|
||||||
const firstRow = rows[0]
|
|
||||||
Array.from(firstRow.cells).forEach((cell, index) => {
|
|
||||||
const handle = document.createElement('div')
|
|
||||||
handle.className = 'column-resize-handle'
|
|
||||||
handle.style.position = 'absolute'
|
|
||||||
handle.style.top = '0'
|
|
||||||
handle.style.right = '0'
|
|
||||||
handle.style.width = `${options.handleWidth}px`
|
|
||||||
handle.style.height = '100%'
|
|
||||||
handle.style.cursor = 'col-resize'
|
|
||||||
handle.style.backgroundColor = 'transparent'
|
|
||||||
handle.style.zIndex = '10'
|
|
||||||
|
|
||||||
cell.style.position = 'relative'
|
|
||||||
cell.appendChild(handle)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function startResizing(view, handle, clientX) {
|
|
||||||
resizing = true
|
|
||||||
startX = clientX
|
|
||||||
const cell = handle.parentElement
|
|
||||||
const row = cell.parentElement
|
|
||||||
table = row.closest('table')
|
|
||||||
colIndex = Array.from(row.cells).indexOf(cell)
|
|
||||||
|
|
||||||
// Get current column widths
|
|
||||||
const node = view.state.doc.nodeAt(view.state.selection.$anchor.posAtIndex(0))
|
|
||||||
const attrs = node?.attrs || {}
|
|
||||||
colWidths = attrs.colWidths
|
|
||||||
? attrs.colWidths.split(',').map(Number)
|
|
||||||
: Array(row.cells.length).fill(100 / row.cells.length)
|
|
||||||
|
|
||||||
const tableRect = table.getBoundingClientRect()
|
|
||||||
const totalWidth = tableRect.width
|
|
||||||
startWidth = (colWidths[colIndex] / 100) * totalWidth
|
|
||||||
|
|
||||||
document.body.style.cursor = 'col-resize'
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyColumnWidths(table, widths) {
|
|
||||||
const rows = table.querySelectorAll('tr')
|
|
||||||
if (rows.length === 0) return
|
|
||||||
|
|
||||||
rows.forEach(row => {
|
|
||||||
Array.from(row.cells).forEach((cell, index) => {
|
|
||||||
cell.style.width = `${widths[index]}%`
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Use the extension in your Vue 3 component
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<editor-content :editor="editor" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { Editor, EditorContent } from '@tiptap/vue-3'
|
|
||||||
import StarterKit from '@tiptap/starter-kit'
|
|
||||||
import Table from '@tiptap/extension-table'
|
|
||||||
import TableRow from '@tiptap/extension-table-row'
|
|
||||||
import TableHeader from '@tiptap/extension-table-header'
|
|
||||||
import TableCell from '@tiptap/extension-table-cell'
|
|
||||||
import { ResizableTableExtension } from './ResizableTableExtension'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
EditorContent,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
editor: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.editor = new Editor({
|
|
||||||
extensions: [
|
|
||||||
StarterKit,
|
|
||||||
ResizableTableExtension,
|
|
||||||
TableRow,
|
|
||||||
TableHeader,
|
|
||||||
TableCell.configure({
|
|
||||||
HTMLAttributes: {
|
|
||||||
style: 'position: relative;',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
content: `
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Header 1</th>
|
|
||||||
<th>Header 2</th>
|
|
||||||
<th>Header 3</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Cell 1</td>
|
|
||||||
<td>Cell 2</td>
|
|
||||||
<td>Cell 3</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
`,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeUnmount() {
|
|
||||||
this.editor.destroy()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.column-resize-handle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 5px;
|
|
||||||
height: 100%;
|
|
||||||
cursor: col-resize;
|
|
||||||
background-color: transparent;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-resize-handle:hover {
|
|
||||||
background-color: #adf;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 1rem 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 8px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Features of This Implementation:
|
|
||||||
|
|
||||||
1. **Column Width Storage**: Column widths are stored in the table node's attributes as a comma-separated string of percentages.
|
|
||||||
|
|
||||||
2. **Resize Handles**: Adds invisible resize handles to each column that become active on hover.
|
|
||||||
|
|
||||||
3. **Persistence**: When you save the editor content to JSON, the column widths are preserved in the node attributes.
|
|
||||||
|
|
||||||
4. **Initialization**: If no widths are specified, columns are initially equally distributed.
|
|
||||||
|
|
||||||
5. **Minimum Width**: Enforces a minimum column width to prevent columns from becoming too narrow.
|
|
||||||
|
|
||||||
## Usage Notes:
|
|
||||||
|
|
||||||
- The plugin adds resize handles to the right side of each column header
|
|
||||||
- Users can drag these handles to resize columns
|
|
||||||
- The widths are stored as percentages to maintain responsiveness
|
|
||||||
- The data is saved in the table node's attributes and will be included when you call `editor.getJSON()`
|
|
||||||
|
|
||||||
## Customization Options:
|
|
||||||
|
|
||||||
You can adjust these parameters in the plugin options:
|
|
||||||
- `handleWidth`: Width of the resize handle in pixels
|
|
||||||
- `cellMinWidth`: Minimum column width in percentage
|
|
||||||
- `onColumnResize`: Callback function when a column is resized
|
|
||||||
|
|
||||||
This implementation provides a complete solution for resizable table columns in Tiptap with Vue 3, with proper persistence of column widths in the document JSON.
|
|
||||||
@@ -14,7 +14,7 @@ https://config.fsa.gov.ru/api/dictionary/property_measured/elements?limit=1000
|
|||||||
### Импорт аккредитации из PDF документов
|
### Импорт аккредитации из PDF документов
|
||||||
|
|
||||||
|
|
||||||
### СИЛА Моль - Сообщество испытательных лабораторий
|
### СИЛА Моль - Совет испытательных лабораторий
|
||||||
|
|
||||||
### Массовая привязка различных параметров в журнале
|
### Массовая привязка различных параметров в журнале
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
root@212.67.13.241
|
||||||
|
```
|
||||||
|
uz5m9Mf1*Alviola16tyu@uz5m9Mf1*Alviola16tyu@
|
||||||
|
```
|
||||||
|
|
||||||
|
narooto
|
||||||
|
ons2So8KGworzwkdDuUzBEoP
|
||||||
|
|
||||||
|
1С
|
||||||
|
molsoft
|
||||||
|
FSfF1qLgHaXnxq1SK7Cikfik
|
||||||
|
me@gufranov.ru
|
||||||
|
|
||||||
|
vnc
|
||||||
|
—act:
|
||||||
|
rrOjiEPzSi79
|
||||||
|
—spectate:
|
||||||
|
ylJLWLSK3gpr
|
||||||
Reference in New Issue
Block a user