Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 83 additions & 1 deletion packages/core/src/blocks/Table/block.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Node, mergeAttributes } from "@tiptap/core";
import { DOMParser, Fragment, Node as PMNode, Schema } from "prosemirror-model";
import { TableView } from "prosemirror-tables";
import { CellSelection, TableMap, TableView } from "prosemirror-tables";
import { NodeView } from "prosemirror-view";
import { createBlockNoteExtension } from "../../editor/BlockNoteExtension.js";
import {
BlockConfig,
createBlockSpecFromTiptapNode,
TableContent,
} from "../../schema/index.js";
import { mergeCSSClasses } from "../../util/browser.js";
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
Expand Down Expand Up @@ -394,6 +395,87 @@ export const createTableBlockSpec = () =>
TiptapTableRow,
],
}),
// Extension for keyboard shortcut which deletes the table if it's empty
// and all cells are selected. Uses a separate extension as it needs
// priority over keyboard handlers in the `TableExtension`'s
// `tableEditing` plugin.
createBlockNoteExtension({
key: "table-keyboard-delete",
keyboardShortcuts: {
Backspace: ({ editor }) => {
if (!(editor.prosemirrorState.selection instanceof CellSelection)) {
return false;
}

const block = editor.getTextCursorPosition().block;
const content = block.content as TableContent<any, any>;

const rows = content.rows.length;
let cols = 0;

for (let rowIndex = 0; rowIndex < content.rows.length; rowIndex++) {
for (
let cellIndex = 0;
cellIndex < content.rows[rowIndex].cells.length;
cellIndex++
) {
const cell = content.rows[rowIndex].cells[cellIndex];

// Counts number of columns in table from first row.
if (rowIndex === 0) {
const colSpan =
"type" in cell && cell.props.colspan
? cell.props.colspan
: 1;
cols += colSpan;
}

// Returns `false` if any cell isn't empty.
if (
("type" in cell && cell.content.length > 0) ||
(!("type" in cell) && cell.length > 0)
) {
return false;
}
}
}

// Need to use ProseMirror API to check if selection spans table.
const anchorCellColIndex =
editor.prosemirrorState.selection.$anchorCell.index();
const anchorCellRowIndex =
editor.prosemirrorState.selection.$anchorCell.index(-1);
const headCellColIndex =
editor.prosemirrorState.selection.$headCell.index();
const headCellRowIndex =
editor.prosemirrorState.selection.$headCell.index(-1);

const minColIndex = Math.min(anchorCellColIndex, headCellColIndex);
const maxColIndex = Math.max(anchorCellColIndex, headCellColIndex);
const minRowIndex = Math.min(anchorCellRowIndex, headCellRowIndex);
const maxRowIndex = Math.max(anchorCellRowIndex, headCellRowIndex);

if (
minColIndex !== 0 ||
maxColIndex !== cols - 1 ||
minRowIndex !== 0 ||
maxRowIndex !== rows - 1
) {
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you test this with merged/split cells? I think that it will actually be a bit off since the number of table cells will not be the same as the number of td nodes. I suggest looking at what: getCellSelection does in tableHandlesPlugin, and either use or come up with something similar to that

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized this can be done much simpler - the whole table is selected when each cell is selected, meaning we can just check if the number of selected cells is equal to the number of total cells or if it's less.


const selectionBlock =
editor.getPrevBlock(block) || editor.getNextBlock(block);
if (selectionBlock) {
editor.setTextCursorPosition(block);
}

editor.removeBlocks([block]);

return true;
},
},
}),
],
);

Expand Down
Loading