@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
119 lines (117 loc) • 4.2 kB
JavaScript
import { Selection } from '@atlaskit/editor-prosemirror/state';
import { TableMap } from '@atlaskit/editor-tables/table-map';
import { findTable } from '@atlaskit/editor-tables/utils';
import { pluginKey } from '../plugin-key';
import { mergeEmptyColumns } from './merge';
export const deleteRows = (rect, isHeaderRowRequired = false) => tr => {
const table = findTable(tr.selection);
if (!table) {
return tr;
}
const rowsToDelete = [];
const map = TableMap.get(table.node);
for (let i = rect.top; i < rect.bottom; i++) {
// skip header row if its required
if (isHeaderRowRequired) {
const cell = table.node.nodeAt(map.map[i * map.width]);
if (cell && cell.type !== cell.type.schema.nodes.tableHeader) {
rowsToDelete.push(i);
}
} else {
rowsToDelete.push(i);
}
}
if (!rowsToDelete.length) {
return tr;
}
const rows = [];
const seen = {};
const deletedCells = {};
for (let rowIndex = 0; rowIndex < map.height; rowIndex++) {
const rowCells = [];
const row = table.node.child(rowIndex);
for (let colIndex = 0; colIndex < map.width; colIndex++) {
const cellPos = map.map[rowIndex * map.width + colIndex];
const cell = table.node.nodeAt(cellPos);
if (!cell) {
continue;
}
const cellsInRow = map.cellsInRect({
left: 0,
top: rowIndex,
right: map.width,
bottom: rowIndex + 1
});
if (rowsToDelete.indexOf(rowIndex) === -1 && !seen[cellPos]) {
// decrement rowspans for row-spanning cells that overlap deleted rows
if (cellsInRow.indexOf(cellPos) > -1) {
let overlappingRows = 0;
rowsToDelete.forEach(rowIndexToDelete => {
if (rowIndex < rowIndexToDelete && cell.attrs.rowspan + rowIndex - 1 >= rowIndexToDelete) {
overlappingRows += 1;
}
});
if (overlappingRows > 0) {
const newCell = cell.type.createChecked({
...cell.attrs,
rowspan: cell.attrs.rowspan - overlappingRows
}, cell.content, cell.marks);
rowCells.push(newCell);
seen[cellPos] = true;
continue;
}
} else if (deletedCells[cellPos]) {
// if we're removing a row-spanning cell, we need to add missing cells to rows below
const attrs = {
...cell.attrs,
colspan: 1,
rowspan: 1
};
if (cell.attrs.colwidth) {
const pos = colIndex > 0 ? colIndex - map.colCount(cellPos) : 0;
attrs.colwidth = cell.attrs.colwidth.slice().splice(pos, 1);
}
const newCell = cell.type.createChecked(attrs, cell.type.schema.nodes.paragraph.createChecked(), cell.marks);
rowCells.push(newCell);
continue;
}
// normal cells that we want to keep
if (!seen[cellPos]) {
seen[cellPos] = true;
rowCells.push(cell);
}
} else if (cellsInRow.indexOf(cellPos) > -1) {
deletedCells[cellPos] = true;
}
}
if (rowCells.length) {
rows.push(row.type.createChecked(row.attrs, rowCells, row.marks));
}
}
if (!rows.length) {
return tr;
}
const newTable = table.node.type.createChecked(table.node.attrs, rows, table.node.marks);
const fixedTable = mergeEmptyColumns(newTable);
if (fixedTable === null) {
return tr;
}
const cursorPos = getNextCursorPos(newTable, rowsToDelete);
// If we delete a row we should also clean up the hover selection
tr.setMeta(pluginKey, {
type: 'CLEAR_HOVER_SELECTION',
data: {
isInDanger: false,
isWholeTableInDanger: false
}
});
return tr.replaceWith(table.pos, table.pos + table.node.nodeSize, fixedTable)
// move cursor before the deleted rows if possible, otherwise - to the first row
.setSelection(Selection.near(tr.doc.resolve(table.pos + cursorPos)));
};
function getNextCursorPos(table, deletedRows) {
const minRow = Math.min(...deletedRows);
const nextRowWithCursor = minRow > 0 ? minRow - 1 : 0;
const map = TableMap.get(table);
return map.map[nextRowWithCursor * map.width];
}