mirror of
https://github.com/vadimmelnicuk/meo.git
synced 2026-05-03 20:50:45 +00:00
feat: implement dynamic table insertion with customizable rows and columns
This commit is contained in:
@@ -379,7 +379,7 @@ export function createEditor({ parent, text, onApplyChanges }) {
|
||||
case 'hr':
|
||||
return insertHr(view, selection);
|
||||
case 'table':
|
||||
return insertTable(view, selection);
|
||||
return insertTable(view, selection, level?.cols, level?.rows);
|
||||
}
|
||||
|
||||
const newMarkerLen = insert.length;
|
||||
|
||||
@@ -1018,12 +1018,19 @@ export const sourceTableHeaderLineField = StateField.define({
|
||||
provide: (field) => EditorView.decorations.from(field)
|
||||
});
|
||||
|
||||
export function insertTable(view, selection) {
|
||||
export function insertTable(view, selection, cols = 3, rows = 2) {
|
||||
const line = view.state.doc.lineAt(selection.from);
|
||||
const lineText = view.state.doc.sliceString(line.from, line.to);
|
||||
const leadingWhitespace = /^(\s*)/.exec(lineText)?.[1] ?? '';
|
||||
|
||||
const table = `${leadingWhitespace}| Header 1 | Header 2 | Header 3 |\n${leadingWhitespace}| --- | --- | --- |\n${leadingWhitespace}| Cell 1 | Cell 2 | Cell 3 |`;
|
||||
const headerCells = Array.from({ length: cols }, () => ' ').join('|');
|
||||
const separatorCells = Array.from({ length: cols }, () => ' --- ').join('|');
|
||||
const bodyRows = Array.from({ length: rows }, () => {
|
||||
const cells = Array.from({ length: cols }, () => ' ').join('|');
|
||||
return `${leadingWhitespace}|${cells}|`;
|
||||
}).join('\n');
|
||||
|
||||
const table = `${leadingWhitespace}|${headerCells}|\n${leadingWhitespace}|${separatorCells}|\n${bodyRows}`;
|
||||
|
||||
view.dispatch({
|
||||
changes: { from: line.from, to: line.to, insert: table },
|
||||
|
||||
+69
-2
@@ -199,7 +199,75 @@ tableBtn.dataset.action = 'table';
|
||||
tableBtn.title = 'Table';
|
||||
tableBtn.appendChild(createElement(Table, { width: 18, height: 18 }));
|
||||
|
||||
formatGroup.append(headingWrapper, bulletListBtn, numberedListBtn, taskBtn, separator, codeBlockBtn, inlineCodeBtn, quoteBtn, hrBtn, tableBtn);
|
||||
const tableDropdown = document.createElement('div');
|
||||
tableDropdown.className = 'table-dropdown';
|
||||
|
||||
const tableDropdownWrapper = document.createElement('div');
|
||||
tableDropdownWrapper.className = 'table-dropdown-wrapper';
|
||||
|
||||
const tableGrid = document.createElement('div');
|
||||
tableGrid.className = 'table-grid';
|
||||
|
||||
const gridSize = 5;
|
||||
for (let row = 0; row < gridSize; row++) {
|
||||
for (let col = 0; col < gridSize; col++) {
|
||||
const cell = document.createElement('div');
|
||||
cell.className = 'table-grid-cell';
|
||||
cell.dataset.row = row + 1;
|
||||
cell.dataset.col = col + 1;
|
||||
if (row === 0 && col === 0) {
|
||||
cell.classList.add('is-highlighted');
|
||||
}
|
||||
tableGrid.appendChild(cell);
|
||||
}
|
||||
}
|
||||
|
||||
const tableSizeLabel = document.createElement('div');
|
||||
tableSizeLabel.className = 'table-size-label';
|
||||
tableSizeLabel.textContent = '1 x 1';
|
||||
|
||||
tableDropdown.append(tableGrid, tableSizeLabel);
|
||||
tableDropdownWrapper.appendChild(tableDropdown);
|
||||
|
||||
const tableWrapper = document.createElement('div');
|
||||
tableWrapper.className = 'table-wrapper';
|
||||
tableWrapper.append(tableBtn, tableDropdownWrapper);
|
||||
|
||||
let selectedTableCols = 1;
|
||||
let selectedTableRows = 1;
|
||||
|
||||
const updateTableGridHighlight = (hoveredCol, hoveredRow) => {
|
||||
const cells = tableGrid.querySelectorAll('.table-grid-cell');
|
||||
cells.forEach((cell) => {
|
||||
const cellCol = parseInt(cell.dataset.col, 10);
|
||||
const cellRow = parseInt(cell.dataset.row, 10);
|
||||
cell.classList.toggle('is-highlighted', cellCol <= hoveredCol && cellRow <= hoveredRow);
|
||||
});
|
||||
tableSizeLabel.textContent = `${hoveredCol} x ${hoveredRow}`;
|
||||
selectedTableCols = hoveredCol;
|
||||
selectedTableRows = hoveredRow;
|
||||
};
|
||||
|
||||
tableGrid.addEventListener('mouseover', (event) => {
|
||||
const cell = event.target.closest('.table-grid-cell');
|
||||
if (!cell) return;
|
||||
const col = parseInt(cell.dataset.col, 10);
|
||||
const row = parseInt(cell.dataset.row, 10);
|
||||
updateTableGridHighlight(col, row);
|
||||
});
|
||||
|
||||
tableGrid.addEventListener('mouseleave', () => {
|
||||
updateTableGridHighlight(1, 1);
|
||||
});
|
||||
|
||||
tableGrid.addEventListener('click', (event) => {
|
||||
const cell = event.target.closest('.table-grid-cell');
|
||||
if (!cell || !editor) return;
|
||||
editor.insertFormat('table', { cols: selectedTableCols, rows: selectedTableRows });
|
||||
editor.focus();
|
||||
});
|
||||
|
||||
formatGroup.append(headingWrapper, bulletListBtn, numberedListBtn, taskBtn, separator, codeBlockBtn, inlineCodeBtn, quoteBtn, hrBtn, tableWrapper);
|
||||
|
||||
const rightGroup = document.createElement('div');
|
||||
rightGroup.className = 'right-group';
|
||||
@@ -591,7 +659,6 @@ codeBlockBtn.addEventListener('click', () => handleFormatAction('codeBlock'));
|
||||
inlineCodeBtn.addEventListener('click', () => handleFormatAction('inlineCode'));
|
||||
quoteBtn.addEventListener('click', () => handleFormatAction('quote'));
|
||||
hrBtn.addEventListener('click', () => handleFormatAction('hr'));
|
||||
tableBtn.addEventListener('click', () => handleFormatAction('table'));
|
||||
autoSaveBtn.addEventListener('click', toggleAutoSave);
|
||||
outlineBtn.addEventListener('click', toggleOutline);
|
||||
|
||||
|
||||
@@ -101,6 +101,63 @@ body {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-dropdown-wrapper {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: calc(50% + 1px);
|
||||
transform: translateX(-50%);
|
||||
padding: 5px 25px;
|
||||
z-index: 100;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.table-dropdown {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px;
|
||||
background-color: var(--vscode-sideBar-background);
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper:hover .table-dropdown-wrapper {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.table-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.table-grid-cell {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table-grid-cell.is-highlighted {
|
||||
background-color: var(--vscode-toolbar-hoverBackground);
|
||||
}
|
||||
|
||||
.table-size-label {
|
||||
font-size: 11px;
|
||||
color: var(--vscode-editor-foreground);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.format-button,
|
||||
.heading-dropdown-option,
|
||||
.meo-mermaid-zoom-btn {
|
||||
|
||||
Reference in New Issue
Block a user