diff --git a/packages/ui/src/RichEditor/TableCellMenu.tsx b/packages/ui/src/RichEditor/TableCellMenu.tsx index aeb542616..f02da49f4 100644 --- a/packages/ui/src/RichEditor/TableCellMenu.tsx +++ b/packages/ui/src/RichEditor/TableCellMenu.tsx @@ -235,70 +235,33 @@ export function TableCellMenu({ editor }: TableCellMenuProps) { setMenuOpen(false); }; - const hasSpannedCell = (): boolean => { - const { selection } = editor.state; - - if (selection instanceof CellSelection) { - let found = false; - selection.forEachCell((node) => { - if (node.attrs.colspan > 1 || node.attrs.rowspan > 1) { - found = true; - } - }); - return found; - } - - const $pos = editor.state.doc.resolve(selection.from); - const cell = cellAround($pos); - if (!cell) return false; - const cellNode = editor.state.doc.nodeAt(cell.pos); - if (!cellNode) return false; - return cellNode.attrs.colspan > 1 || cellNode.attrs.rowspan > 1; - }; - const handleSplitCell = () => { editor.chain().focus().splitCell().run(); setMenuOpen(false); }; const handleClearContents = () => { - const { state, dispatch } = editor.view; - const { selection, schema } = state; - const { tr } = state; - let cursorTarget: number | null = null; + const { state } = editor.view; - if (selection instanceof CellSelection) { - selection.forEachCell((node, pos) => { - const start = pos + 1; - const end = pos + node.nodeSize - 1; - if (cursorTarget === null) { - cursorTarget = tr.mapping.map(start) + 1; - } - tr.replaceWith( - tr.mapping.map(start), - tr.mapping.map(end), - schema.nodes.paragraph.create(), - ); - }); + if (state.selection instanceof CellSelection) { + editor.commands.deleteSelection(); } else { - const $pos = state.doc.resolve(selection.from); + const { dispatch } = editor.view; + const { tr, schema } = state; + const $pos = state.doc.resolve(state.selection.from); const cell = cellAround($pos); if (cell) { const cellNode = state.doc.nodeAt(cell.pos); if (cellNode) { const start = cell.pos + 1; const end = cell.pos + cellNode.nodeSize - 1; - cursorTarget = start + 1; tr.replaceWith(start, end, schema.nodes.paragraph.create()); + tr.setSelection(TextSelection.create(tr.doc, start + 1)); + dispatch(tr); } } } - if (cursorTarget !== null) { - tr.setSelection(TextSelection.create(tr.doc, cursorTarget)); - } - - dispatch(tr); setMenuOpen(false); }; @@ -340,11 +303,13 @@ export function TableCellMenu({ editor }: TableCellMenuProps) { onMouseDown={e => e.preventDefault()} className={menu()} > - - - Merge cells - - {hasSpannedCell() && ( + {editor.can().mergeCells() && ( + + + Merge cells + + )} + {editor.can().splitCell() && ( Split cells diff --git a/packages/ui/src/RichEditor/TableColumnMenu.tsx b/packages/ui/src/RichEditor/TableColumnMenu.tsx index 6112a46a5..ad79d816d 100644 --- a/packages/ui/src/RichEditor/TableColumnMenu.tsx +++ b/packages/ui/src/RichEditor/TableColumnMenu.tsx @@ -450,19 +450,17 @@ export function TableColumnMenu({ editor }: TableColumnMenuProps) { if (!table) return; const map = TableMap.get(table); - const { tr, schema } = editor.state; - const targetType = isHeaderColumn() - ? schema.nodes.tableCell - : schema.nodes.tableHeader; + const cellPos = map.positionAt(0, 0, table) + tableStart; - for (let row = 0; row < map.height; row++) { - const cellPos = map.map[row * map.width] + tableStart; - const cellNode = editor.state.doc.nodeAt(cellPos); - if (!cellNode) continue; - tr.setNodeMarkup(cellPos, targetType, cellNode.attrs); - } - - editor.view.dispatch(tr); + editor + .chain() + .focus() + .command(({ tr }) => { + tr.setSelection(TextSelection.create(tr.doc, cellPos + 1)); + return true; + }) + .toggleHeaderColumn() + .run(); } catch { // table may have changed } @@ -503,26 +501,26 @@ export function TableColumnMenu({ editor }: TableColumnMenuProps) { const { colIndex, tableStart } = currentCol; try { - const tableNodePos = tableStart - 1; - const table = editor.state.doc.nodeAt(tableNodePos); + const table = editor.state.doc.nodeAt(tableStart - 1); if (!table) return; - const rows: PMNode[] = []; - table.forEach((row) => { - const cells: PMNode[] = []; - row.forEach((cell, _offset, i) => { - cells.push(cell); - if (i === colIndex) { - cells.push(cell.copy(cell.content)); - } - }); - rows.push(row.type.create(row.attrs, cells)); - }); + const map = TableMap.get(table); - const newTable = table.type.create(table.attrs, rows); - const { tr } = editor.state; - tr.replaceWith(tableNodePos, tableNodePos + table.nodeSize, newTable); - editor.view.dispatch(tr); + editor + .chain() + .focus() + .command(({ tr }) => { + for (let row = map.height - 1; row >= 0; row--) { + const cellOffset = map.map[row * map.width + colIndex]; + const cell = table.nodeAt(cellOffset); + if (!cell) continue; + + const insertPos = cellOffset + tableStart + cell.nodeSize; + tr.insert(insertPos, cell); + } + return true; + }) + .run(); } catch { // table may have changed } @@ -593,25 +591,17 @@ export function TableColumnMenu({ editor }: TableColumnMenuProps) { if (!table) return; const map = TableMap.get(table); - const { tr } = editor.state; - const { schema } = editor.state; + const firstCellPos = map.map[colIndex] + tableStart; + const lastCellPos + = map.map[(map.height - 1) * map.width + colIndex] + tableStart; - for (let row = 0; row < map.height; row++) { - const cellPos = map.map[row * map.width + colIndex] + tableStart; - const cellNode = editor.state.doc.nodeAt(cellPos); - if (!cellNode) continue; + const $anchor = editor.state.doc.resolve(firstCellPos); + const $head = editor.state.doc.resolve(lastCellPos); - const start = cellPos + 1; - const end = cellPos + cellNode.nodeSize - 1; - - tr.replaceWith( - tr.mapping.map(start), - tr.mapping.map(end), - schema.nodes.paragraph.create(), - ); - } - - editor.view.dispatch(tr); + editor.view.dispatch( + editor.state.tr.setSelection(new CellSelection($anchor, $head)), + ); + editor.commands.deleteSelection(); } catch { // table may have changed } diff --git a/packages/ui/src/RichEditor/TableRowMenu.tsx b/packages/ui/src/RichEditor/TableRowMenu.tsx index d61e9e3a5..357abd235 100644 --- a/packages/ui/src/RichEditor/TableRowMenu.tsx +++ b/packages/ui/src/RichEditor/TableRowMenu.tsx @@ -446,19 +446,17 @@ export function TableRowMenu({ editor }: TableRowMenuProps) { if (!table) return; const map = TableMap.get(table); - const { tr, schema } = editor.state; - const targetType = isHeaderRow() - ? schema.nodes.tableCell - : schema.nodes.tableHeader; + const cellPos = map.positionAt(0, 0, table) + tableStart; - for (let col = 0; col < map.width; col++) { - const cellPos = map.map[col] + tableStart; - const cellNode = editor.state.doc.nodeAt(cellPos); - if (!cellNode) continue; - tr.setNodeMarkup(cellPos, targetType, cellNode.attrs); - } - - editor.view.dispatch(tr); + editor + .chain() + .focus() + .command(({ tr }) => { + tr.setSelection(TextSelection.create(tr.doc, cellPos + 1)); + return true; + }) + .toggleHeaderRow() + .run(); } catch { // table may have changed } @@ -499,22 +497,24 @@ export function TableRowMenu({ editor }: TableRowMenuProps) { const { rowIndex, tableStart } = currentRow; try { - const tableNodePos = tableStart - 1; - const table = editor.state.doc.nodeAt(tableNodePos); + const table = editor.state.doc.nodeAt(tableStart - 1); if (!table) return; - const rows: PMNode[] = []; - table.forEach((row, _offset, i) => { - rows.push(row); - if (i === rowIndex) { - rows.push(row.copy(row.content)); - } - }); + const rowNode = table.child(rowIndex); - const newTable = table.type.create(table.attrs, rows); - const { tr } = editor.state; - tr.replaceWith(tableNodePos, tableNodePos + table.nodeSize, newTable); - editor.view.dispatch(tr); + let insertPos = tableStart; + for (let i = 0; i <= rowIndex; i++) { + insertPos += table.child(i).nodeSize; + } + + editor + .chain() + .focus() + .command(({ tr }) => { + tr.insert(insertPos, rowNode); + return true; + }) + .run(); } catch { // table may have changed } @@ -585,25 +585,17 @@ export function TableRowMenu({ editor }: TableRowMenuProps) { if (!table) return; const map = TableMap.get(table); - const { tr } = editor.state; - const { schema } = editor.state; + const firstCellPos = map.map[rowIndex * map.width] + tableStart; + const lastCellPos + = map.map[rowIndex * map.width + (map.width - 1)] + tableStart; - for (let col = 0; col < map.width; col++) { - const cellPos = map.map[rowIndex * map.width + col] + tableStart; - const cellNode = editor.state.doc.nodeAt(cellPos); - if (!cellNode) continue; + const $anchor = editor.state.doc.resolve(firstCellPos); + const $head = editor.state.doc.resolve(lastCellPos); - const start = cellPos + 1; - const end = cellPos + cellNode.nodeSize - 1; - - tr.replaceWith( - tr.mapping.map(start), - tr.mapping.map(end), - schema.nodes.paragraph.create(), - ); - } - - editor.view.dispatch(tr); + editor.view.dispatch( + editor.state.tr.setSelection(new CellSelection($anchor, $head)), + ); + editor.commands.deleteSelection(); } catch { // table may have changed }