diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 64f18a0..0191c9c 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -4,21 +4,17 @@ "type": "split", "children": [ { - "id": "b1afd552ee0aa86f", + "id": "30ead2b90dc646d4", "type": "tabs", "children": [ { - "id": "343836aed0394d03", + "id": "e53cb40c752bba12", "type": "leaf", "state": { - "type": "markdown", - "state": { - "file": "WORK & PROJECTS/Mol/Планы и диаграммы/Разработка - ЭПИКИ.md", - "mode": "source", - "source": false - }, + "type": "empty", + "state": {}, "icon": "lucide-file", - "title": "Разработка - ЭПИКИ" + "title": "New tab" } } ] @@ -78,7 +74,7 @@ } ], "direction": "horizontal", - "width": 469.5 + "width": 314.5 }, "right": { "id": "5a12b65cf742d665", @@ -140,17 +136,24 @@ "state": { "type": "outline", "state": { - "file": "WORK & PROJECTS/Mol/Планы и диаграммы/Разработка - ЭПИКИ.md", - "followCursor": false, - "showSearch": false, - "searchQuery": "" + "file": "WORK & PROJECTS/Ulab/Доступы к точкам.md" }, "icon": "lucide-list", - "title": "Outline of Разработка - ЭПИКИ" + "title": "Структура Доступы к точкам" + } + }, + { + "id": "c463341da3a7d6da", + "type": "leaf", + "state": { + "type": "git-view", + "state": {}, + "icon": "git-pull-request", + "title": "Source Control" } } ], - "currentTab": 3 + "currentTab": 4 } ], "direction": "horizontal", @@ -168,12 +171,10 @@ "obsidian-git:Open Git source control": false } }, - "active": "343836aed0394d03", + "active": "e53cb40c752bba12", "lastOpenFiles": [ - "WORK & PROJECTS/Mol/Планы и диаграммы/План СИЛА.md", + "WORK & PROJECTS/Mol/Code Chunks/Tiptap resizeTableColumnWidth.md", "PERSONAL PROJECTS/P2EP/cdRead.canvas", - "conflict-files-obsidian-git.md", - "My/Diet 2.md", "PERSONAL PROJECTS/P2EP/pseudoCode/MakeSmallChar.md", "PERSONAL PROJECTS/P2EP/pseudoCode/FUN_80015674 Update Entity Stats.md", "PERSONAL PROJECTS/P2EP/pseudoCode/FUN_800453e0 SystemEventManager.md", @@ -184,6 +185,7 @@ "PERSONAL PROJECTS/PS1 DOCS/PSX code - Load texture from SECTOR!.md", "PERSONAL PROJECTS/PS1 DOCS/PSX code texture load unpack show and fade.md", "PERSONAL PROJECTS/Persona 1/Script Format.md", + "My/Diet 2.md", "WORK & PROJECTS/Mol/Серверы/git.moldev.ru.md", "PERSONAL PROJECTS/PS1 DOCS/CD_Execute_Reading.md", "PERSONAL PROJECTS/PS1 DOCS/PSX code logo show.md", diff --git a/WORK & PROJECTS/Mol/Code Chunks/Tiptap resizeTableColumnWidth.md b/WORK & PROJECTS/Mol/Code Chunks/Tiptap resizeTableColumnWidth.md new file mode 100644 index 0000000..fd53b69 --- /dev/null +++ b/WORK & PROJECTS/Mol/Code Chunks/Tiptap resizeTableColumnWidth.md @@ -0,0 +1,318 @@ + +# 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 + + + + + +``` + +## 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. \ No newline at end of file