[WIKI-277] fix: unable to delete last table column/row in editor (#7389)

* fix: delete column not working

* refactor: delete column commad

* fix: delete last row logic

* refactor: row count logic

* refactor: isCellSelection utility function
This commit is contained in:
Aaryan Khandelwal 2025-07-14 17:27:09 +05:30 committed by GitHub
parent c067eaa1ed
commit a427367720
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 89 additions and 19 deletions

View file

@ -21,10 +21,11 @@ import {
import { COLORS_LIST } from "@/constants/common";
import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
import { isCellSelection } from "@/extensions/table/table/utilities/is-cell-selection";
import { isCellSelection } from "@/extensions/table/table/utilities/helpers";
// types
import { TEditorCommands } from "@/types";
// local components
import { TextAlignmentSelector } from "./alignment-selector";
import { TEditorCommands } from "@/types";
type EditorBubbleMenuProps = Omit<BubbleMenuProps, "children">;

View file

@ -7,8 +7,6 @@ import {
addRowBefore,
CellSelection,
columnResizing,
deleteColumn,
deleteRow,
deleteTable,
fixTables,
goToNextCell,
@ -27,6 +25,8 @@ import { TableInsertPlugin } from "../plugins/insert-handlers/plugin";
import { tableControls } from "./table-controls";
import { TableView } from "./table-view";
import { createTable } from "./utilities/create-table";
import { deleteColumnOrTable } from "./utilities/delete-column";
import { deleteRowOrTable } from "./utilities/delete-row";
import { deleteTableWhenAllCellsSelected } from "./utilities/delete-table-when-all-cells-selected";
import { insertLineAboveTableAction } from "./utilities/insert-line-above-table-action";
import { insertLineBelowTableAction } from "./utilities/insert-line-below-table-action";
@ -140,10 +140,7 @@ export const Table = Node.create<TableOptions>({
() =>
({ state, dispatch }) =>
addColumnAfter(state, dispatch),
deleteColumn:
() =>
({ state, dispatch }) =>
deleteColumn(state, dispatch),
deleteColumn: deleteColumnOrTable,
addRowBefore:
() =>
({ state, dispatch }) =>
@ -152,10 +149,7 @@ export const Table = Node.create<TableOptions>({
() =>
({ state, dispatch }) =>
addRowAfter(state, dispatch),
deleteRow:
() =>
({ state, dispatch }) =>
deleteRow(state, dispatch),
deleteRow: deleteRowOrTable,
deleteTable:
() =>
({ state, dispatch }) =>

View file

@ -0,0 +1,39 @@
import type { Command } from "@tiptap/core";
import { deleteColumn, deleteTable } from "@tiptap/pm/tables";
// local imports
import { isCellSelection } from "./helpers";
export const deleteColumnOrTable: () => Command =
() =>
({ state, dispatch }) => {
const { selection } = state;
// Check if we're in a table and have a cell selection
if (!isCellSelection(selection)) {
return false;
}
// Get the ProseMirrorTable and calculate total columns
const tableStart = selection.$anchorCell.start(-1);
const selectedTable = state.doc.nodeAt(tableStart - 1);
if (!selectedTable) return false;
// Count total columns by examining the first row
const firstRow = selectedTable.firstChild;
if (!firstRow) return false;
let totalColumns = 0;
for (let i = 0; i < firstRow.childCount; i++) {
const cell = firstRow.child(i);
totalColumns += cell.attrs.colspan || 1;
}
// If only one column exists, delete the entire ProseMirrorTable
if (totalColumns === 1) {
return deleteTable(state, dispatch);
}
// Otherwise, proceed with normal column deletion
return deleteColumn(state, dispatch);
};

View file

@ -0,0 +1,32 @@
import type { Command } from "@tiptap/core";
import { deleteRow, deleteTable } from "@tiptap/pm/tables";
// local imports
import { isCellSelection } from "./helpers";
export const deleteRowOrTable: () => Command =
() =>
({ state, dispatch }) => {
const { selection } = state;
// Check if we're in a ProseMirrorTable and have a cell selection
if (!isCellSelection(selection)) {
return false;
}
// Get the ProseMirrorTable and calculate total rows
const tableStart = selection.$anchorCell.start(-1);
const selectedTable = state.doc.nodeAt(tableStart - 1);
if (!selectedTable) return false;
// Count total rows by examining the table's children
const totalRows = selectedTable.childCount;
// If only one row exists, delete the entire ProseMirrorTable
if (totalRows === 1) {
return deleteTable(state, dispatch);
}
// Otherwise, proceed with normal row deletion
return deleteRow(state, dispatch);
};

View file

@ -2,7 +2,7 @@ import { findParentNodeClosestToPos, KeyboardShortcutCommand } from "@tiptap/cor
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
import { isCellSelection } from "@/extensions/table/table/utilities/is-cell-selection";
import { isCellSelection } from "@/extensions/table/table/utilities/helpers";
export const deleteTableWhenAllCellsSelected: KeyboardShortcutCommand = ({ editor }) => {
const { selection } = editor.state;

View file

@ -0,0 +1,9 @@
import type { Selection } from "@tiptap/pm/state";
import { CellSelection } from "@tiptap/pm/tables";
/**
* @description Check if the selection is a cell selection
* @param {Selection} selection - The selection to check
* @returns {boolean} True if the selection is a cell selection, false otherwise
*/
export const isCellSelection = (selection: Selection): selection is CellSelection => selection instanceof CellSelection;

View file

@ -1,5 +0,0 @@
import { CellSelection } from "@tiptap/pm/tables";
export function isCellSelection(value: unknown): value is CellSelection {
return value instanceof CellSelection;
}

View file

@ -15,7 +15,7 @@ export {
RichTextEditorWithRef,
} from "@/components/editors";
export { isCellSelection } from "@/extensions/table/table/utilities/is-cell-selection";
export { isCellSelection } from "@/extensions/table/table/utilities/helpers";
// constants
export * from "@/constants/common";